diff options
author | Jakob Unterwurzacher | 2020-07-26 18:35:12 +0200 |
---|---|---|
committer | Jakob Unterwurzacher | 2020-07-26 18:35:12 +0200 |
commit | 777b95f82ffea8a25b95089343b07b29378110da (patch) | |
tree | 641adf67d3969a2b87c52ef62b384f022b7362d7 /internal/fusefrontend/fs.go | |
parent | 81fb42b9124e0d8e59d67ff2072d4388ce42ff77 (diff) |
v2api: delete (most) fusefrontend v1 files
All the functionality in these files has been reimplemented
for the v2 api. Drop the old files.
Diffstat (limited to 'internal/fusefrontend/fs.go')
-rw-r--r-- | internal/fusefrontend/fs.go | 692 |
1 files changed, 0 insertions, 692 deletions
diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go deleted file mode 100644 index e8dae9f..0000000 --- a/internal/fusefrontend/fs.go +++ /dev/null @@ -1,692 +0,0 @@ -// Package fusefrontend interfaces directly with the go-fuse library. -package fusefrontend - -// FUSE operations on paths - -import ( - "os" - "sync" - "sync/atomic" - "syscall" - "time" - - "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fuse" - "github.com/hanwen/go-fuse/v2/fuse/nodefs" - "github.com/hanwen/go-fuse/v2/fuse/pathfs" - - "github.com/rfjakob/gocryptfs/internal/configfile" - "github.com/rfjakob/gocryptfs/internal/contentenc" - "github.com/rfjakob/gocryptfs/internal/inomap" - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/serialize_reads" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// FS implements the go-fuse virtual filesystem interface. -type FS struct { - // Embed pathfs.defaultFileSystem to avoid compile failure when the - // pathfs.FileSystem interface gets new functions. defaultFileSystem - // provides a no-op implementation for all functions. - pathfs.FileSystem - args Args // Stores configuration arguments - // dirIVLock: Lock()ed if any "gocryptfs.diriv" file is modified - // Readers must RLock() it to prevent them from seeing intermediate - // states - dirIVLock sync.RWMutex - // Filename encryption helper - nameTransform nametransform.NameTransformer - // Content encryption helper - contentEnc *contentenc.ContentEnc - // This lock is used by openWriteOnlyFile() to block concurrent opens while - // it relaxes the permissions on a file. - openWriteOnlyLock sync.RWMutex - // MitigatedCorruptions is used to report data corruption that is internally - // mitigated by ignoring the corrupt item. For example, when OpenDir() finds - // a corrupt filename, we still return the other valid filenames. - // The corruption is logged to syslog to inform the user, and in addition, - // the corrupt filename is logged to this channel via - // reportMitigatedCorruption(). - // "gocryptfs -fsck" reads from the channel to also catch these transparently- - // mitigated corruptions. - MitigatedCorruptions chan string - // This flag is set to zero each time fs.isFiltered() is called - // (uint32 so that it can be reset with CompareAndSwapUint32). - // When -idle was used when mounting, idleMonitor() sets it to 1 - // periodically. - IsIdle uint32 - // dirCache caches directory fds - dirCache dirCacheStruct - // inoMap translates inode numbers from different devices to unique inode - // numbers. - inoMap *inomap.InoMap -} - -//var _ pathfs.FileSystem = &FS{} // Verify that interface is implemented. - -// NewFS returns a new encrypted FUSE overlay filesystem. -func NewFS(args Args, c *contentenc.ContentEnc, n nametransform.NameTransformer) *FS { - if args.SerializeReads { - serialize_reads.InitSerializer() - } - if len(args.Exclude) > 0 { - tlog.Warn.Printf("Forward mode does not support -exclude") - } - var st syscall.Stat_t - err := syscall.Stat(args.Cipherdir, &st) - if err != nil { - tlog.Warn.Printf("NewFS: could not stat cipherdir: %v", err) - st.Dev = 0 - } - return &FS{ - FileSystem: pathfs.NewDefaultFileSystem(), - args: args, - nameTransform: n, - contentEnc: c, - inoMap: inomap.New(), - } -} - -// GetAttr implements pathfs.Filesystem. -// -// 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 - } - dirfd, cName, err := fs.openBackingDir(relPath) - if err != nil { - return nil, fuse.ToStatus(err) - } - 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) - fs.inoMap.TranslateStat(&st2) - a.FromStat(&st2) - if a.IsRegular() { - a.Size = fs.contentEnc.CipherSizeToPlainSize(a.Size) - } else if a.IsSymlink() { - target, _ := fs.Readlink(relPath, context) - a.Size = uint64(len(target)) - } - if fs.args.ForceOwner != nil { - a.Owner = *fs.args.ForceOwner - } - return a, fuse.OK -} - -// mangleOpenFlags is used by Create() and Open() to convert the open flags the user -// wants to the flags we internally use to open the backing file. -// The returned flags always contain O_NOFOLLOW. -func (fs *FS) mangleOpenFlags(flags uint32) (newFlags int) { - newFlags = int(flags) - // Convert WRONLY to RDWR. We always need read access to do read-modify-write cycles. - if (newFlags & syscall.O_ACCMODE) == syscall.O_WRONLY { - newFlags = newFlags ^ os.O_WRONLY | os.O_RDWR - } - // We also cannot open the file in append mode, we need to seek back for RMW - newFlags = newFlags &^ os.O_APPEND - // O_DIRECT accesses must be aligned in both offset and length. Due to our - // crypto header, alignment will be off, even if userspace makes aligned - // accesses. Running xfstests generic/013 on ext4 used to trigger lots of - // EINVAL errors due to missing alignment. Just fall back to buffered IO. - newFlags = newFlags &^ syscallcompat.O_DIRECT - // Create and Open are two separate FUSE operations, so O_CREAT should not - // be part of the open flags. - newFlags = newFlags &^ syscall.O_CREAT - // We always want O_NOFOLLOW to be safe against symlink races - newFlags |= syscall.O_NOFOLLOW - return newFlags -} - -// Open - FUSE call. Open already-existing file. -// -// Symlink-safe through Openat(). -func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) { - if fs.isFiltered(path) { - return nil, fuse.EPERM - } - newFlags := fs.mangleOpenFlags(flags) - // Taking this lock makes sure we don't race openWriteOnlyFile() - fs.openWriteOnlyLock.RLock() - defer fs.openWriteOnlyLock.RUnlock() - // Symlink-safe open - dirfd, cName, err := fs.openBackingDir(path) - if err != nil { - return nil, fuse.ToStatus(err) - } - defer syscall.Close(dirfd) - fd, err := syscallcompat.Openat(dirfd, cName, newFlags, 0) - // Handle a few specific errors - if err != nil { - if err == syscall.EMFILE { - var lim syscall.Rlimit - syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim) - tlog.Warn.Printf("Open %q: too many open files. Current \"ulimit -n\": %d", cName, lim.Cur) - } - if err == syscall.EACCES && (int(flags)&syscall.O_ACCMODE) == syscall.O_WRONLY { - return fs.openWriteOnlyFile(dirfd, cName, newFlags) - } - return nil, fuse.ToStatus(err) - } - f := os.NewFile(uintptr(fd), cName) - return NewFile(f, fs) -} - -// openBackingFile opens the ciphertext file that backs relative plaintext -// path "relPath". Always adds O_NOFOLLOW to the flags. -func (fs *FS) openBackingFile(relPath string, flags int) (fd int, err error) { - dirfd, cName, err := fs.openBackingDir(relPath) - if err != nil { - return -1, err - } - defer syscall.Close(dirfd) - return syscallcompat.Openat(dirfd, cName, flags|syscall.O_NOFOLLOW, 0) -} - -// Due to RMW, we always need read permissions on the backing file. This is a -// problem if the file permissions do not allow reading (i.e. 0200 permissions). -// This function works around that problem by chmod'ing the file, obtaining a fd, -// and chmod'ing it back. -func (fs *FS) openWriteOnlyFile(dirfd int, cName string, newFlags int) (*File, fuse.Status) { - woFd, err := syscallcompat.Openat(dirfd, cName, syscall.O_WRONLY|syscall.O_NOFOLLOW, 0) - if err != nil { - return nil, fuse.ToStatus(err) - } - defer syscall.Close(woFd) - var st syscall.Stat_t - err = syscall.Fstat(woFd, &st) - if err != nil { - return nil, fuse.ToStatus(err) - } - // The cast to uint32 fixes a build failure on Darwin, where st.Mode is uint16. - perms := uint32(st.Mode) - // Verify that we don't have read permissions - if perms&0400 != 0 { - tlog.Warn.Printf("openWriteOnlyFile: unexpected permissions %#o, returning EPERM", perms) - return nil, fuse.ToStatus(syscall.EPERM) - } - // Upgrade the lock to block other Open()s and downgrade again on return - fs.openWriteOnlyLock.RUnlock() - fs.openWriteOnlyLock.Lock() - defer func() { - fs.openWriteOnlyLock.Unlock() - fs.openWriteOnlyLock.RLock() - }() - // Relax permissions and revert on return - err = syscall.Fchmod(woFd, perms|0400) - if err != nil { - tlog.Warn.Printf("openWriteOnlyFile: changing permissions failed: %v", err) - return nil, fuse.ToStatus(err) - } - defer func() { - err2 := syscall.Fchmod(woFd, perms) - if err2 != nil { - tlog.Warn.Printf("openWriteOnlyFile: reverting permissions failed: %v", err2) - } - }() - rwFd, err := syscallcompat.Openat(dirfd, cName, newFlags, 0) - if err != nil { - return nil, fuse.ToStatus(err) - } - f := os.NewFile(uintptr(rwFd), cName) - return NewFile(f, fs) -} - -// Create - FUSE call. Creates a new file. -// -// Symlink-safe through the use of Openat(). -func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Context) (nodefs.File, fuse.Status) { - if fs.isFiltered(path) { - return nil, fuse.EPERM - } - newFlags := fs.mangleOpenFlags(flags) - dirfd, cName, err := fs.openBackingDir(path) - if err != nil { - return nil, fuse.ToStatus(err) - } - defer syscall.Close(dirfd) - fd := -1 - // Make sure context is nil if we don't want to preserve the owner - if !fs.args.PreserveOwner { - context = nil - } - // Handle long file name - if !fs.args.PlaintextNames && nametransform.IsLongContent(cName) { - // Create ".name" - err = fs.nameTransform.WriteLongNameAt(dirfd, cName, path) - if err != nil { - return nil, fuse.ToStatus(err) - } - // Create content - fd, err = syscallcompat.OpenatUser(dirfd, cName, newFlags|syscall.O_CREAT|syscall.O_EXCL, mode, context) - if err != nil { - nametransform.DeleteLongNameAt(dirfd, cName) - } - } else { - // Create content, normal (short) file name - fd, err = syscallcompat.OpenatUser(dirfd, cName, newFlags|syscall.O_CREAT|syscall.O_EXCL, mode, context) - } - if err != nil { - // xfstests generic/488 triggers this - if err == syscall.EMFILE { - var lim syscall.Rlimit - syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim) - tlog.Warn.Printf("Create %q: too many open files. Current \"ulimit -n\": %d", cName, lim.Cur) - } - return nil, fuse.ToStatus(err) - } - f := os.NewFile(uintptr(fd), cName) - return NewFile(f, fs) -} - -// Chmod - FUSE call. Change permissions on "path". -// -// Symlink-safe through use of Fchmodat(). -func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) { - if fs.isFiltered(path) { - return fuse.EPERM - } - dirfd, cName, err := fs.openBackingDir(path) - if err != nil { - return fuse.ToStatus(err) - } - defer syscall.Close(dirfd) - // os.Chmod goes through the "syscallMode" translation function that messes - // up the suid and sgid bits. So use a syscall directly. - err = syscallcompat.FchmodatNofollow(dirfd, cName, mode) - return fuse.ToStatus(err) -} - -// Chown - FUSE call. Change the owner of "path". -// -// Symlink-safe through use of Fchownat(). -func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) { - if fs.isFiltered(path) { - return fuse.EPERM - } - dirfd, cName, err := fs.openBackingDir(path) - if err != nil { - return fuse.ToStatus(err) - } - defer syscall.Close(dirfd) - err = syscallcompat.Fchownat(dirfd, cName, int(uid), int(gid), unix.AT_SYMLINK_NOFOLLOW) - return fuse.ToStatus(err) -} - -// Mknod - FUSE call. Create a device file. -// -// Symlink-safe through use of Mknodat(). -func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) { - if fs.isFiltered(path) { - return fuse.EPERM - } - dirfd, cName, err := fs.openBackingDir(path) - if err != nil { - return fuse.ToStatus(err) - } - defer syscall.Close(dirfd) - // Make sure context is nil if we don't want to preserve the owner - if !fs.args.PreserveOwner { - context = nil - } - // Create ".name" file to store long file name (except in PlaintextNames mode) - if !fs.args.PlaintextNames && nametransform.IsLongContent(cName) { - err = fs.nameTransform.WriteLongNameAt(dirfd, cName, path) - if err != nil { - return fuse.ToStatus(err) - } - // Create "gocryptfs.longfile." device node - err = syscallcompat.MknodatUser(dirfd, cName, mode, int(dev), context) - if err != nil { - nametransform.DeleteLongNameAt(dirfd, cName) - } - } else { - // Create regular device node - err = syscallcompat.MknodatUser(dirfd, cName, mode, int(dev), context) - } - return fuse.ToStatus(err) -} - -// Truncate - FUSE call. Truncates a file. -// -// Support truncate(2) by opening the file and calling ftruncate(2) -// While the glibc "truncate" wrapper seems to always use ftruncate, fsstress from -// xfstests uses this a lot by calling "truncate64" directly. -// -// Symlink-safe by letting file.Truncate() do all the work. -func (fs *FS) Truncate(path string, offset uint64, context *fuse.Context) (code fuse.Status) { - file, code := fs.Open(path, uint32(os.O_RDWR), context) - if code != fuse.OK { - return code - } - code = file.Truncate(offset) - file.Release() - return code -} - -// Utimens - FUSE call. Set the timestamps on file "path". -// -// Symlink-safe through UtimesNanoAt. -func (fs *FS) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Context) (code fuse.Status) { - if fs.isFiltered(path) { - return fuse.EPERM - } - dirfd, cName, err := fs.openBackingDir(path) - if err != nil { - return fuse.ToStatus(err) - } - defer syscall.Close(dirfd) - err = syscallcompat.UtimesNanoAtNofollow(dirfd, cName, a, m) - return fuse.ToStatus(err) -} - -// StatFs - FUSE call. Returns information about the filesystem. -// -// Symlink-safe because the passed path is ignored. -func (fs *FS) StatFs(path string) *fuse.StatfsOut { - var st syscall.Statfs_t - err := syscall.Statfs(fs.args.Cipherdir, &st) - if err == nil { - var out fuse.StatfsOut - out.FromStatfsT(&st) - return &out - } - return nil -} - -// decryptSymlinkTarget: "cData64" is base64-decoded and decrypted -// like file contents (GCM). -// The empty string decrypts to the empty string. -// -// This function does not do any I/O and is hence symlink-safe. -func (fs *FS) decryptSymlinkTarget(cData64 string) (string, error) { - if cData64 == "" { - return "", nil - } - cData, err := fs.nameTransform.B64DecodeString(cData64) - if err != nil { - return "", err - } - data, err := fs.contentEnc.DecryptBlock([]byte(cData), 0, nil) - if err != nil { - return "", err - } - return string(data), nil -} - -// Readlink - FUSE call. -// -// Symlink-safe through openBackingDir() + Readlinkat(). -func (fs *FS) Readlink(relPath string, context *fuse.Context) (out string, status fuse.Status) { - dirfd, cName, err := fs.openBackingDir(relPath) - if err != nil { - return "", fuse.ToStatus(err) - } - defer syscall.Close(dirfd) - cTarget, err := syscallcompat.Readlinkat(dirfd, cName) - if err != nil { - return "", fuse.ToStatus(err) - } - if fs.args.PlaintextNames { - return cTarget, fuse.OK - } - // Symlinks are encrypted like file contents (GCM) and base64-encoded - target, err := fs.decryptSymlinkTarget(cTarget) - if err != nil { - tlog.Warn.Printf("Readlink %q: decrypting target failed: %v", cName, err) - return "", fuse.EIO - } - return string(target), fuse.OK -} - -// Unlink - FUSE call. Delete a file. -// -// Symlink-safe through use of Unlinkat(). -func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) { - if fs.isFiltered(path) { - return fuse.EPERM - } - dirfd, cName, err := fs.openBackingDir(path) - if err != nil { - return fuse.ToStatus(err) - } - defer syscall.Close(dirfd) - // Delete content - err = syscallcompat.Unlinkat(dirfd, cName, 0) - if err != nil { - return fuse.ToStatus(err) - } - // Delete ".name" file - if !fs.args.PlaintextNames && nametransform.IsLongContent(cName) { - err = nametransform.DeleteLongNameAt(dirfd, cName) - if err != nil { - tlog.Warn.Printf("Unlink: could not delete .name file: %v", err) - } - } - return fuse.ToStatus(err) -} - -// encryptSymlinkTarget: "data" is encrypted like file contents (GCM) -// and base64-encoded. -// The empty string encrypts to the empty string. -// -// Symlink-safe because it does not do any I/O. -func (fs *FS) encryptSymlinkTarget(data string) (cData64 string) { - if data == "" { - return "" - } - cData := fs.contentEnc.EncryptBlock([]byte(data), 0, nil) - cData64 = fs.nameTransform.B64EncodeToString(cData) - return cData64 -} - -// Symlink - FUSE call. Create a symlink. -// -// Symlink-safe through use of Symlinkat. -func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) { - tlog.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName) - if fs.isFiltered(linkName) { - return fuse.EPERM - } - dirfd, cName, err := fs.openBackingDir(linkName) - if err != nil { - return fuse.ToStatus(err) - } - defer syscall.Close(dirfd) - // Make sure context is nil if we don't want to preserve the owner - if !fs.args.PreserveOwner { - context = nil - } - cTarget := target - if !fs.args.PlaintextNames { - // Symlinks are encrypted like file contents (GCM) and base64-encoded - cTarget = fs.encryptSymlinkTarget(target) - } - // Create ".name" file to store long file name (except in PlaintextNames mode) - if !fs.args.PlaintextNames && nametransform.IsLongContent(cName) { - err = fs.nameTransform.WriteLongNameAt(dirfd, cName, linkName) - if err != nil { - return fuse.ToStatus(err) - } - // Create "gocryptfs.longfile." symlink - err = syscallcompat.SymlinkatUser(cTarget, dirfd, cName, context) - if err != nil { - nametransform.DeleteLongNameAt(dirfd, cName) - } - } else { - // Create symlink - err = syscallcompat.SymlinkatUser(cTarget, dirfd, cName, context) - } - return fuse.ToStatus(err) -} - -// Rename - FUSE call. -// -// Symlink-safe through Renameat(). -func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) { - defer fs.dirCache.Clear() - if fs.isFiltered(newPath) { - return fuse.EPERM - } - oldDirfd, oldCName, err := fs.openBackingDir(oldPath) - if err != nil { - return fuse.ToStatus(err) - } - defer syscall.Close(oldDirfd) - newDirfd, newCName, err := fs.openBackingDir(newPath) - if err != nil { - return fuse.ToStatus(err) - } - defer syscall.Close(newDirfd) - // Easy case. - if fs.args.PlaintextNames { - return fuse.ToStatus(syscallcompat.Renameat(oldDirfd, oldCName, newDirfd, newCName)) - } - // Long destination file name: create .name file - nameFileAlreadyThere := false - if nametransform.IsLongContent(newCName) { - err = fs.nameTransform.WriteLongNameAt(newDirfd, newCName, newPath) - // Failure to write the .name file is expected when the target path already - // exists. Since hashes are pretty unique, there is no need to modify the - // .name file in this case, and we ignore the error. - if err == syscall.EEXIST { - nameFileAlreadyThere = true - } else if err != nil { - return fuse.ToStatus(err) - } - } - // Actual rename - tlog.Debug.Printf("Renameat %d/%s -> %d/%s\n", oldDirfd, oldCName, newDirfd, newCName) - err = syscallcompat.Renameat(oldDirfd, oldCName, newDirfd, newCName) - if err == syscall.ENOTEMPTY || err == syscall.EEXIST { - // If an empty directory is overwritten we will always get an error as - // the "empty" directory will still contain gocryptfs.diriv. - // Interestingly, ext4 returns ENOTEMPTY while xfs returns EEXIST. - // We handle that by trying to fs.Rmdir() the target directory and trying - // again. - tlog.Debug.Printf("Rename: Handling ENOTEMPTY") - if fs.Rmdir(newPath, context) == fuse.OK { - err = syscallcompat.Renameat(oldDirfd, oldCName, newDirfd, newCName) - } - } - if err != nil { - if nametransform.IsLongContent(newCName) && nameFileAlreadyThere == false { - // Roll back .name creation unless the .name file was already there - nametransform.DeleteLongNameAt(newDirfd, newCName) - } - return fuse.ToStatus(err) - } - if nametransform.IsLongContent(oldCName) { - nametransform.DeleteLongNameAt(oldDirfd, oldCName) - } - return fuse.OK -} - -// Link - FUSE call. Creates a hard link at "newPath" pointing to file -// "oldPath". -// -// Symlink-safe through use of Linkat(). -func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) { - if fs.isFiltered(newPath) { - return fuse.EPERM - } - oldDirFd, cOldName, err := fs.openBackingDir(oldPath) - if err != nil { - return fuse.ToStatus(err) - } - defer syscall.Close(oldDirFd) - newDirFd, cNewName, err := fs.openBackingDir(newPath) - if err != nil { - return fuse.ToStatus(err) - } - defer syscall.Close(newDirFd) - // Handle long file name (except in PlaintextNames mode) - if !fs.args.PlaintextNames && nametransform.IsLongContent(cNewName) { - err = fs.nameTransform.WriteLongNameAt(newDirFd, cNewName, newPath) - if err != nil { - return fuse.ToStatus(err) - } - // Create "gocryptfs.longfile." link - err = syscallcompat.Linkat(oldDirFd, cOldName, newDirFd, cNewName, 0) - if err != nil { - nametransform.DeleteLongNameAt(newDirFd, cNewName) - } - } else { - // Create regular link - err = syscallcompat.Linkat(oldDirFd, cOldName, newDirFd, cNewName, 0) - } - return fuse.ToStatus(err) -} - -// Access - FUSE call. Check if a file can be accessed in the specified mode(s) -// (read, write, execute). -// -// From https://github.com/libfuse/libfuse/blob/master/include/fuse.h : -// -// > Check file access permissions -// > -// > If the 'default_permissions' mount option is given, this method is not -// > called. -// -// We always enable default_permissions when -allow_other is passed, so there -// is no need for this function to check the uid in fuse.Context. -// -// Symlink-safe through use of faccessat. -func (fs *FS) Access(relPath string, mode uint32, context *fuse.Context) (code fuse.Status) { - if fs.isFiltered(relPath) { - return fuse.EPERM - } - dirfd, cName, err := fs.openBackingDir(relPath) - if err != nil { - return fuse.ToStatus(err) - } - err = syscallcompat.Faccessat(dirfd, cName, mode) - syscall.Close(dirfd) - return fuse.ToStatus(err) -} - -// reportMitigatedCorruption is used to report a corruption that was transparently -// mitigated and did not return an error to the user. Pass the name of the corrupt -// item (filename for OpenDir(), xattr name for ListXAttr() etc). -// See the MitigatedCorruptions channel for more info. -func (fs *FS) reportMitigatedCorruption(item string) { - if fs.MitigatedCorruptions == nil { - return - } - select { - case fs.MitigatedCorruptions <- item: - case <-time.After(1 * time.Second): - tlog.Warn.Printf("BUG: reportCorruptItem: timeout") - //debug.PrintStack() - return - } -} - -// isFiltered - check if plaintext "path" should be forbidden -// -// Prevents name clashes with internal files when file names are not encrypted -func (fs *FS) isFiltered(path string) bool { - atomic.StoreUint32(&fs.IsIdle, 0) - - if !fs.args.PlaintextNames { - return false - } - // gocryptfs.conf in the root directory is forbidden - if path == configfile.ConfDefaultName { - tlog.Info.Printf("The name /%s is reserved when -plaintextnames is used\n", - configfile.ConfDefaultName) - return true - } - // Note: gocryptfs.diriv is NOT forbidden because diriv and plaintextnames - // are exclusive - return false -} |