From 0c1ceed1fa55e2a9174050c324f679821a5fca8d Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sun, 14 Oct 2018 21:55:37 +0200 Subject: [PATCH] fusefrontend: make GetAttr() symlink-safe Use openBackingDir() and Fstatat(). High performance impact, though part of it should be mitigated by adding DirIV caching to the new code paths. $ ./benchmark.bash Testing gocryptfs at /tmp/benchmark.bash.Eou: gocryptfs v1.6-37-ge3914b3-dirty; go-fuse v20170619-66-g6df8ddc; 2018-10-14 go1.11 WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.2289 s, 213 MB/s READ: 262144000 bytes (262 MB, 250 MiB) copied, 1.02616 s, 255 MB/s UNTAR: 24.490 MD5: 13.120 LS: 3.368 RM: 9.232 --- internal/fusefrontend/fs.go | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go index 8e443b7..a301231 100644 --- a/internal/fusefrontend/fs.go +++ b/internal/fusefrontend/fs.go @@ -73,30 +73,36 @@ func NewFS(args Args, c *contentenc.ContentEnc, n *nametransform.NameTransform) } // GetAttr implements pathfs.Filesystem. -func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) { - tlog.Debug.Printf("FS.GetAttr('%s')", name) - if fs.isFiltered(name) { +// +// GetAttr is symlink-safe through use of openBackingDir() and Fstatat(). +func (fs *FS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr, fuse.Status) { + tlog.Debug.Printf("FS.GetAttr(%q)", relPath) + if fs.isFiltered(relPath) { return nil, fuse.EPERM } - cName, err := fs.encryptPath(name) + dirfd, cName, err := fs.openBackingDir(relPath) if err != nil { return nil, fuse.ToStatus(err) } - a, status := fs.FileSystem.GetAttr(cName, context) - if a == nil { - tlog.Debug.Printf("FS.GetAttr failed: %s", status.String()) - return a, status + var st unix.Stat_t + err = syscallcompat.Fstatat(dirfd, cName, &st, unix.AT_SYMLINK_NOFOLLOW) + syscall.Close(dirfd) + if err != nil { + return nil, fuse.ToStatus(err) } + a := &fuse.Attr{} + st2 := syscallcompat.Unix2syscall(st) + a.FromStat(&st2) if a.IsRegular() { a.Size = fs.contentEnc.CipherSizeToPlainSize(a.Size) } else if a.IsSymlink() { - target, _ := fs.Readlink(name, context) + target, _ := fs.Readlink(relPath, context) a.Size = uint64(len(target)) } if fs.args.ForceOwner != nil { a.Owner = *fs.args.ForceOwner } - return a, status + return a, fuse.OK } // mangleOpenFlags is used by Create() and Open() to convert the open flags the user