Skip to content

Commit

Permalink
Treat symlinks to directories like directories
Browse files Browse the repository at this point in the history
This allows symlinking to create additional subalbums.

Closes: #431
  • Loading branch information
hupfdule committed Jul 15, 2021
1 parent 2e4f83a commit 0d6a881
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 2 deletions.
8 changes: 7 additions & 1 deletion api/scanner/scanner_album.go
Expand Up @@ -181,7 +181,13 @@ func findMediaForAlbum(album *models.Album, cache *scanner_cache.AlbumScannerCac
for _, item := range dirContent {
photoPath := path.Join(album.Path, item.Name())

if !item.IsDir() && cache.IsPathMedia(photoPath) {
isDirSymlink, err := utils.IsDirSymlink(photoPath)
if err != nil {
log.Printf("Cannot detect whether %s is symlink to a directory. Pretending it is not", photoPath)
isDirSymlink = false
}

if !item.IsDir() && !isDirSymlink && cache.IsPathMedia(photoPath) {
// Match file against ignore data
if albumIgnore.MatchesPath(item.Name()) {
log.Printf("File %s ignored\n", item.Name())
Expand Down
9 changes: 8 additions & 1 deletion api/scanner/scanner_user.go
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/photoview/photoview/api/graphql/models"
"github.com/photoview/photoview/api/scanner/scanner_cache"
"github.com/photoview/photoview/api/scanner/scanner_utils"
"github.com/photoview/photoview/api/utils"
"github.com/pkg/errors"
ignore "github.com/sabhiram/go-gitignore"
"gorm.io/gorm"
Expand Down Expand Up @@ -198,7 +199,13 @@ func findAlbumsForUser(db *gorm.DB, user *models.User, album_cache *scanner_cach
continue
}

if item.IsDir() && directoryContainsPhotos(subalbumPath, album_cache, albumIgnore) {
isDirSymlink, err := utils.IsDirSymlink(subalbumPath)
if err != nil {
scanErrors = append(scanErrors, errors.Wrapf(err, "could not check for symlink target of %s", subalbumPath))
continue
}

if (item.IsDir() || isDirSymlink) && directoryContainsPhotos(subalbumPath, album_cache, albumIgnore) {
scanQueue.PushBack(scanInfo{
path: subalbumPath,
parent: album,
Expand Down
33 changes: 33 additions & 0 deletions api/utils/utils.go
Expand Up @@ -5,7 +5,11 @@ import (
"fmt"
"log"
"math/big"
"os"
"path"
"path/filepath"

"github.com/pkg/errors"
)

func GenerateToken() string {
Expand Down Expand Up @@ -80,3 +84,32 @@ func FaceRecognitionModelsPath() string {

return EnvFaceRecognitionModelsPath.GetValue()
}

// IsDirSymlink checks that the given path is a symlink and resolves to a
// directory.
func IsDirSymlink(path string) (bool, error) {
isDirSymlink := false

fileInfo, err := os.Lstat(path)
if err != nil {
return false, errors.Wrapf(err, "could not stat %s", path)
}

//Resolve symlinks
if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink {
resolvedPath, err := filepath.EvalSymlinks(path)
if err != nil {
return false, errors.Wrapf(err, "Cannot resolve linktarget of %s, ignoring it", path)
}

resolvedFile, err := os.Stat(resolvedPath)
if err != nil {
return false, errors.Wrapf(err, "Cannot get fileinfo of linktarget %s of symlink %s, ignoring it", resolvedPath, path)
}
isDirSymlink = resolvedFile.IsDir()

return isDirSymlink, nil
}

return false, nil
}
82 changes: 82 additions & 0 deletions api/utils/utils_test.go
@@ -0,0 +1,82 @@
package utils_test

import (
"io/ioutil"
"os"
"path"
"testing"

"github.com/photoview/photoview/api/test_utils"
"github.com/photoview/photoview/api/utils"
)

func TestMain(m *testing.M) {
os.Exit(test_utils.IntegrationTestRun(m))
}

func TestIsDirSymlink(t *testing.T) {
test_utils.FilesystemTest(t)

// TODO: regular file
// file symlink
// dir symlink
// non-existant file

// Prepare a temporary directory for testing purposes
dir, err := ioutil.TempDir("", "testing")
if err != nil {
t.Fatalf("unable to create temp directory for testing")
}
defer os.RemoveAll(dir)

// Create regular file
_, err = os.Create(path.Join(dir, "regular_file"))
if err != nil {
t.Fatalf("unable to create regular file for testing")
}

// Create directory
err = os.Mkdir(path.Join(dir, "directory"), 0755)
if err != nil {
t.Fatalf("unable to create directory for testing")
}

// Create symlink to regular file
err = os.Symlink(path.Join(dir, "regular_file"), path.Join(dir, "file_link"))
if err != nil {
t.Fatalf("unable to create file link for testing")
}

// Create symlink to directory
err = os.Symlink(path.Join(dir, "directory"), path.Join(dir, "dir_link"))
if err != nil {
t.Fatalf("unable to create dir link for testing")
}

// Execute the actual tests

isDirLink, _ := utils.IsDirSymlink(path.Join(dir, "regular_file"))
if isDirLink {
t.Error("Failed detection of regular file")
}

isDirLink, _ = utils.IsDirSymlink(path.Join(dir, "directory"))
if isDirLink {
t.Error("Failed detection of directory")
}

isDirLink, _ = utils.IsDirSymlink(path.Join(dir, "file_link"))
if isDirLink {
t.Error("Failed detection of link to regular file")
}

isDirLink, _ = utils.IsDirSymlink(path.Join(dir, "dir_link"))
if isDirLink {
t.Error("Failed detection of link to directory")
}

isDirLink, err = utils.IsDirSymlink(path.Join(dir, "non_existant"))
if err == nil {
t.Error("Missing error for non-existant file")
}
}

0 comments on commit 0d6a881

Please sign in to comment.