Răsfoiți Sursa

Enable (*FileStorage).ListDirectories to list symlinks which target directories (#2408)

* Enable (*FileStorage).ListDirectories to list symlinks

Done by making DirFileToStargeInfo follow symlinks to determine if the
target is a directory, then returning the _unfollowed_ path as a valid
directory so the path will be correctly constructed relative to the
symlink, rather than relative to the target. The purpose is only to
ensure that the target is in fact a directory.

Signed-off-by: Michael Dresser <michaelmdresser@gmail.com>

* Add unit test for symlinked ListDirectories

Signed-off-by: Michael Dresser <michaelmdresser@gmail.com>

---------

Signed-off-by: Michael Dresser <michaelmdresser@gmail.com>
Michael Dresser 2 ani în urmă
părinte
comite
89ea1820c7
2 a modificat fișierele cu 62 adăugiri și 3 ștergeri
  1. 25 3
      pkg/storage/filestorage.go
  2. 37 0
      pkg/storage/filestorage_test.go

+ 25 - 3
pkg/storage/filestorage.go

@@ -6,6 +6,7 @@ import (
 	gopath "path"
 	"path/filepath"
 
+	"github.com/opencost/opencost/pkg/log"
 	"github.com/opencost/opencost/pkg/util/fileutil"
 	"github.com/pkg/errors"
 )
@@ -84,7 +85,7 @@ func (fs *FileStorage) ListDirectories(path string) ([]*StorageInfo, error) {
 		files = append(files, info)
 	}
 
-	return DirFilesToStorageInfo(files, path), nil
+	return DirFilesToStorageInfo(files, path, fs.baseDir), nil
 }
 
 // Read uses the relative path of the storage combined with the provided path to
@@ -177,15 +178,36 @@ func FileToStorageInfo(fileInfo gofs.FileInfo) *StorageInfo {
 
 // DirFilesToStorageInfo maps a []fs.FileInfo to []*storage.StorageInfo
 // but only returning StorageInfo for directories
-func DirFilesToStorageInfo(fileInfo []gofs.FileInfo, path string) []*StorageInfo {
+func DirFilesToStorageInfo(fileInfo []gofs.FileInfo, relativePath string, basePath string) []*StorageInfo {
 	var stats []*StorageInfo
 	for _, info := range fileInfo {
 		if info.IsDir() {
 			stats = append(stats, &StorageInfo{
-				Name:    filepath.Join(path, info.Name()),
+				Name:    filepath.Join(relativePath, info.Name()),
 				Size:    info.Size(),
 				ModTime: info.ModTime(),
 			})
+		} else if info.Mode()&os.ModeSymlink == os.ModeSymlink {
+			targetPath, err := os.Readlink(filepath.Join(basePath, relativePath, info.Name()))
+			if err != nil {
+				log.Warnf("Converting files to storage info failed on supposed symlink that failed to be Readlink-ed (%s): %s", filepath.Join(basePath, relativePath, info.Name()), err)
+				continue
+			}
+			if targetInfo, err := os.Stat(targetPath); err == nil {
+				if targetInfo.IsDir() {
+					stats = append(stats, &StorageInfo{
+						// The Name added here is the path not readlink-ed
+						Name:    filepath.Join(relativePath, info.Name()),
+						Size:    info.Size(),
+						ModTime: info.ModTime(),
+					})
+				}
+			} else if errors.Is(err, os.ErrNotExist) {
+				continue
+			} else {
+				log.Warnf("Converting files to storage info failed to stat target (%s) of symlink (%s): %s", targetPath, info.Name(), err)
+				continue
+			}
 		}
 	}
 	return stats

+ 37 - 0
pkg/storage/filestorage_test.go

@@ -0,0 +1,37 @@
+package storage
+
+import (
+	"os"
+	"path/filepath"
+	"testing"
+)
+
+func TestFileStorageListDirectoriesSymlink(t *testing.T) {
+	storageBaseDir := t.TempDir()
+	subdirName := "sub"
+	slFileName := "sl"
+	symlinkTargetDir := t.TempDir()
+
+	if err := os.MkdirAll(filepath.Join(storageBaseDir, subdirName), 0700); err != nil {
+		t.Fatalf("failed to construct subdir: %s", err)
+	}
+
+	if err := os.Symlink(symlinkTargetDir, filepath.Join(storageBaseDir, subdirName, slFileName)); err != nil {
+		t.Fatalf("failed to create symlink at '%s' which points to '%s': %s", filepath.Join(storageBaseDir, subdirName, slFileName), symlinkTargetDir, err)
+	}
+
+	store := FileStorage{baseDir: storageBaseDir}
+	dirs, err := store.ListDirectories(subdirName)
+	if err != nil {
+		t.Fatalf("failed to list directories: %s", err)
+	}
+	if len(dirs) != 1 {
+		t.Fatalf("dirs should have 1 entry")
+	}
+	dir := dirs[0]
+	// This condition is important -- the returned path should be relative to
+	// the storage base dir, not the symlink's target dir.
+	if dir.Name != filepath.Join(subdirName, slFileName) {
+		t.Errorf("Expected dir.Name to be '%s' but it was '%s'", filepath.Join(subdirName, slFileName), dir.Name)
+	}
+}