mediaapi/writers: Add validation and error handling to getPathFromMediaMetadata

This commit is contained in:
Robert Swain 2017-05-18 17:39:30 +02:00
parent 10a2b2f8e6
commit 00e8fed3a7
3 changed files with 68 additions and 10 deletions

View file

@ -187,7 +187,16 @@ func (r *downloadRequest) respondFromLocalFile(w http.ResponseWriter, absBasePat
"Content-Disposition": r.MediaMetadata.ContentDisposition,
}).Infof("Downloading file")
filePath := getPathFromMediaMetadata(r.MediaMetadata, absBasePath)
filePath, err := getPathFromMediaMetadata(r.MediaMetadata, absBasePath)
if err != nil {
// FIXME: Remove erroneous file from database?
r.Logger.Warnln("Failed to get file path from metadata:", err)
r.jsonErrorResponse(w, util.JSONResponse{
Code: 404,
JSON: jsonerror.NotFound(fmt.Sprintf("File with media ID %q does not exist", r.MediaMetadata.MediaID)),
})
return
}
file, err := os.Open(filePath)
if err != nil {
// FIXME: Remove erroneous file from database?
@ -364,9 +373,19 @@ func (r *downloadRequest) commitFileAndMetadata(tmpDir types.Path, absBasePath t
}).Infof("Storing file metadata to media repository database")
// The database is the source of truth so we need to have moved the file first
err := moveFile(
finalPath, err := getPathFromMediaMetadata(r.MediaMetadata, absBasePath)
if err != nil {
r.Logger.Warnf("Failed to get file path from metadata: %q\n", err)
tmpDirErr := os.RemoveAll(string(tmpDir))
if tmpDirErr != nil {
r.Logger.Warnf("Failed to remove tmpDir (%v): %q\n", tmpDir, tmpDirErr)
}
return updateActiveRemoteRequests
}
err = moveFile(
types.Path(path.Join(string(tmpDir), "content")),
types.Path(getPathFromMediaMetadata(r.MediaMetadata, absBasePath)),
types.Path(finalPath),
)
if err != nil {
tmpDirErr := os.RemoveAll(string(tmpDir))
@ -387,7 +406,7 @@ func (r *downloadRequest) commitFileAndMetadata(tmpDir types.Path, absBasePath t
// if written to disk, add to db
err = db.StoreMediaMetadata(r.MediaMetadata)
if err != nil {
finalDir := path.Dir(getPathFromMediaMetadata(r.MediaMetadata, absBasePath))
finalDir := path.Dir(finalPath)
finalDirErr := os.RemoveAll(finalDir)
if finalDirErr != nil {
r.Logger.Warnf("Failed to remove finalDir (%v): %q\n", finalDir, finalDirErr)

View file

@ -20,6 +20,8 @@ import (
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
log "github.com/Sirupsen/logrus"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
@ -75,13 +77,42 @@ func createTempFileWriter(absBasePath types.Path, logger *log.Entry) (*bufio.Wri
return writer, tmpFile, tmpDir, nil
}
func getPathFromMediaMetadata(m *types.MediaMetadata, absBasePath types.Path) string {
return path.Join(
// getPathFromMediaMetadata validates and constructs the on-disk path to the media
// based on its origin and mediaID
// If a mediaID is too short, which could happen for other homeserver implementations,
// place it into a short-id subdirectory of the origin directory
// If the mediaID is long enough, we split it in two using one part as a subdirectory
// name and the other part as the file name. This is to allow storage of more files due
// to filesystem limitations on the number of files in a directory. For example, if
// mediaID is 'qwerty', we create subdirectory called 'qwe' and place the file in 'qwe'
// and call it 'rty'.
func getPathFromMediaMetadata(m *types.MediaMetadata, absBasePath types.Path) (string, error) {
var subDir string
var fileName string
if len(m.MediaID) > 3 {
subDir = string(m.MediaID[:3])
fileName = string(m.MediaID[3:])
} else {
subDir = "short-id"
fileName = string(m.MediaID)
}
filePath, err := filepath.Abs(path.Join(
string(absBasePath),
string(m.Origin),
string(m.MediaID[:3]),
string(m.MediaID[3:]),
)
subDir,
fileName,
))
// check if the absolute absBasePath is a prefix of the absolute filePath
// if so, no directory escape has occurred and the filePath is valid
// Note: absBasePath is already absolute
if err != nil || strings.HasPrefix(filePath, string(absBasePath)) == false {
return "", fmt.Errorf("Invalid filePath (not within absBasePath %v): %v", absBasePath, filePath)
}
return filePath, nil
}
// moveFile attempts to move the file src to dst

View file

@ -210,7 +210,15 @@ func Upload(req *http.Request, cfg *config.MediaAPI, db *storage.Database) util.
// TODO: generate thumbnails
finalPath := getPathFromMediaMetadata(r.MediaMetadata, cfg.AbsBasePath)
finalPath, err := getPathFromMediaMetadata(r.MediaMetadata, cfg.AbsBasePath)
if err != nil {
logger.Warnf("Failed to get file path from metadata: %q\n", err)
removeDir(tmpDir, logger)
return util.JSONResponse{
Code: 400,
JSON: jsonerror.Unknown(fmt.Sprintf("Failed to upload")),
}
}
err = moveFile(
types.Path(path.Join(string(tmpDir), "content")),