diff options
author | Jakob Unterwurzacher | 2019-01-20 14:29:28 +0100 |
---|---|---|
committer | Jakob Unterwurzacher | 2019-01-20 14:29:28 +0100 |
commit | 962c52364415496b64a42b49fe5f90d593dc09f7 (patch) | |
tree | 85677402023a9c19a3883999adb806e9c155f90c | |
parent | fab585ec0193ec20b46b4d095af5c7226162c89d (diff) |
fusefrontend: ensure directories without W or X perms can be deleted
This fixed the "Permission denied" bug, but still has the problem that
the directory may be replaced behind our back. Mitigated by the fact
that we skip the workaround when running as root with -allow_other.
https://github.com/rfjakob/gocryptfs/issues/354
-rw-r--r-- | internal/fusefrontend/fs_dir.go | 51 |
1 files changed, 28 insertions, 23 deletions
diff --git a/internal/fusefrontend/fs_dir.go b/internal/fusefrontend/fs_dir.go index d26fd79..35aae66 100644 --- a/internal/fusefrontend/fs_dir.go +++ b/internal/fusefrontend/fs_dir.go @@ -151,42 +151,47 @@ func (fs *FS) Rmdir(relPath string, context *fuse.Context) (code fuse.Status) { err = unix.Unlinkat(parentDirFd, cName, unix.AT_REMOVEDIR) return fuse.ToStatus(err) } - dirfd, err := syscallcompat.Openat(parentDirFd, cName, - syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) - if err == syscall.EACCES { - // We need permission to read and modify the directory - tlog.Debug.Printf("Rmdir: handling EACCESS") + // Unless we are running as root, we need read, write and execute permissions + // to handle gocryptfs.diriv. + permWorkaround := false + var origMode uint32 + if !fs.args.PreserveOwner { var st unix.Stat_t err = syscallcompat.Fstatat(parentDirFd, cName, &st, unix.AT_SYMLINK_NOFOLLOW) if err != nil { - tlog.Debug.Printf("Rmdir: Stat: %v", err) return fuse.ToStatus(err) } - // This cast is needed on Darwin, where st.Mode is uint16. - origMode := uint32(st.Mode) - err = syscallcompat.FchmodatNofollow(parentDirFd, cName, origMode|0700) - if err != nil { - tlog.Debug.Printf("Rmdir: Fchmodat failed: %v", err) - return fuse.ToStatus(err) + if st.Mode&0700 != 0700 { + tlog.Debug.Printf("Rmdir: permWorkaround") + permWorkaround = true + // This cast is needed on Darwin, where st.Mode is uint16. + origMode = uint32(st.Mode) + err = syscallcompat.FchmodatNofollow(parentDirFd, cName, origMode|0700) + if err != nil { + tlog.Debug.Printf("Rmdir: permWorkaround: chmod failed: %v", err) + return fuse.ToStatus(err) + } } - // Retry open - dirfd, err = syscallcompat.Openat(parentDirFd, cName, - syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) - // Undo the chmod if removing the directory failed + } + dirfd, err := syscallcompat.Openat(parentDirFd, cName, + syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) + if err != nil { + tlog.Debug.Printf("Rmdir: Open: %v", err) + return fuse.ToStatus(err) + } + defer syscall.Close(dirfd) + // Undo the chmod if removing the directory failed. This must run before + // closing dirfd, so defer it after (defer is LIFO). + if permWorkaround { defer func() { if code != fuse.OK { - err = syscallcompat.FchmodatNofollow(parentDirFd, cName, origMode) + err = unix.Fchmod(dirfd, origMode) if err != nil { - tlog.Warn.Printf("Rmdir: Chmod rollback failed: %v", err) + tlog.Warn.Printf("Rmdir: permWorkaround: rollback failed: %v", err) } } }() } - if err != nil { - tlog.Debug.Printf("Rmdir: Open: %v", err) - return fuse.ToStatus(err) - } - defer syscall.Close(dirfd) retry: // Check directory contents children, err := syscallcompat.Getdents(dirfd) |