diff --git a/CHANGES.md b/CHANGES.md index 2c3329f0..f8107a5b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,7 @@ * `hasher` will no longer create a directory if a non-existing one is passed as an argument. * trivrost will no longer attempt to repeat range requests to a host after it has failed to conformly respond while displaying the confusing message `Taking longer than usual: HTTP Status 200` and will now fail immediately in such cases instead. * trivrost will no longer fail to comply with HTTP 2 strictly using lower-case HTTP Header names. This had been caused by methods of `http.Header` still being oriented around HTTP 1 canonical header names due to Go's backwards compatibility promise. +* hasher now supports symlinks within hashed directories. ## 1.4.6 (2021-01-25) ### Fixes diff --git a/pkg/launcher/hashing/scan.go b/pkg/launcher/hashing/scan.go index 4604f075..64092d91 100644 --- a/pkg/launcher/hashing/scan.go +++ b/pkg/launcher/hashing/scan.go @@ -10,6 +10,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "github.com/setlog/trivrost/pkg/launcher/config" log "github.com/sirupsen/logrus" @@ -61,24 +62,41 @@ func mustHashRelatively(ctx context.Context, readDir readDirFunc, readFile readF } func mustHashDir(ctx context.Context, readDir readDirFunc, readFile readFileFunc, stat statFunc, hashFilePath string) config.FileInfoMap { - fm := make(config.FileInfoMap) - for _, info := range mustReadDir(readDir, hashFilePath) { - if info.IsDir() { - fm.Join(mustHashDir(ctx, readDir, readFile, stat, filepath.Join(hashFilePath, info.Name()))) + fileMap := make(config.FileInfoMap) + for _, curPathInfo := range mustReadDir(readDir, hashFilePath) { + curPath := filepath.Join(hashFilePath, curPathInfo.Name()) + resolvedPath := evaluateSoftLink(curPath) + if curPath != resolvedPath { + log.Warnf("File \"%s\"->\"%s\" is a symlink, will be treated as a regular file/dir.", curPath, resolvedPath) + curPath = resolvedPath + } + if !strings.HasPrefix(curPath, hashFilePath) { + panic(fmt.Errorf("hashing '%s' outside hash directory is not allowed", curPath)) + } + curPathInfo, _ = stat(curPath) + if curPathInfo.IsDir() { + fileMap.Join(mustHashDir(ctx, readDir, readFile, stat, curPath)) } else { - filePath := filepath.Join(hashFilePath, info.Name()) - sha, size, err := calculateSha256(ctx, filePath, readFile) + sha, size, err := calculateSha256(ctx, curPath, readFile) if err != nil { panic(fmt.Errorf("failed hashing file \"%s\": %w", hashFilePath, err)) } - fm[filePath] = &config.FileInfo{SHA256: sha, Size: size} + fileMap[curPath] = &config.FileInfo{SHA256: sha, Size: size} } } - return fm + return fileMap +} + +func evaluateSoftLink(filePath string) string { + evaluatedName, err := filepath.EvalSymlinks(filePath) + if err != nil { + panic(err) + } + return evaluatedName } func mustReadDir(readDir readDirFunc, directoryPath string) []os.FileInfo { - infos, err := readDir(directoryPath) + infos, err := readDir(evaluateSoftLink(directoryPath)) if err != nil { if os.IsNotExist(err) { return nil