summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/fusefrontend/fs_dir.go79
-rw-r--r--internal/syscallcompat/sys_darwin.go5
-rw-r--r--internal/syscallcompat/sys_linux.go22
3 files changed, 60 insertions, 46 deletions
diff --git a/internal/fusefrontend/fs_dir.go b/internal/fusefrontend/fs_dir.go
index afcc5e5..880f271 100644
--- a/internal/fusefrontend/fs_dir.go
+++ b/internal/fusefrontend/fs_dir.go
@@ -24,13 +24,13 @@ const dsStoreName = ".DS_Store"
// mkdirWithIv - create a new directory and corresponding diriv file. dirfd
// should be a handle to the parent directory, cName is the name of the new
// directory and mode specifies the access permissions to use.
-func (fs *FS) mkdirWithIv(dirfd int, cName string, mode uint32) error {
+func (fs *FS) mkdirWithIv(dirfd int, cName string, mode uint32, context *fuse.Context) error {
// Between the creation of the directory and the creation of gocryptfs.diriv
// the directory is inconsistent. Take the lock to prevent other readers
// from seeing it.
fs.dirIVLock.Lock()
defer fs.dirIVLock.Unlock()
- err := syscallcompat.Mkdirat(dirfd, cName, mode)
+ err := syscallcompat.MkdiratUser(dirfd, cName, mode, context)
if err != nil {
return err
}
@@ -63,40 +63,20 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu
return fuse.ToStatus(err)
}
defer syscall.Close(dirfd)
- // Don't set the full mode before we have set the correct owner.
- // See Create FUSE call for more details.
- origMode := mode
- if fs.args.PreserveOwner {
- mode = 0000
+ // Make sure context is nil if we don't want to preserve the owner
+ if !fs.args.PreserveOwner {
+ context = nil
}
if fs.args.PlaintextNames {
- err = syscallcompat.Mkdirat(dirfd, cName, mode)
- if err != nil {
- return fuse.ToStatus(err)
- }
- // Set owner
- if fs.args.PreserveOwner {
- err = syscallcompat.Fchownat(dirfd, cName, int(context.Owner.Uid),
- int(context.Owner.Gid), unix.AT_SYMLINK_NOFOLLOW)
- if err != nil {
- tlog.Warn.Printf("Mkdir %q: Fchownat %d:%d failed: %v", cName, context.Owner.Uid, context.Owner.Gid, err)
- // In case of a failure, we don't want to proceed setting more
- // permissive modes.
- return fuse.ToStatus(err)
- }
- }
- // Set mode
- if origMode != mode {
- err = syscallcompat.Fchmodat(dirfd, cName, origMode, unix.AT_SYMLINK_NOFOLLOW)
- if err != nil {
- tlog.Warn.Printf("Mkdir %q: Fchmodat %#o -> %#o failed: %v", cName, mode, origMode, err)
- }
- }
- return fuse.OK
+ err = syscallcompat.MkdiratUser(dirfd, cName, mode, context)
+ return fuse.ToStatus(err)
}
- // We need write and execute permissions to create gocryptfs.diriv
- mode = mode | 0300
+ // We need write and execute permissions to create gocryptfs.diriv.
+ // Also, we need read permissions to open the directory (to avoid
+ // race-conditions between getting and setting the mode).
+ origMode := mode
+ mode = mode | 0700
// Handle long file name
if nametransform.IsLongContent(cName) {
@@ -107,33 +87,40 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu
}
// Create directory
- err = fs.mkdirWithIv(dirfd, cName, mode)
+ err = fs.mkdirWithIv(dirfd, cName, mode, context)
if err != nil {
nametransform.DeleteLongNameAt(dirfd, cName)
return fuse.ToStatus(err)
}
} else {
- err = fs.mkdirWithIv(dirfd, cName, mode)
+ err = fs.mkdirWithIv(dirfd, cName, mode, context)
if err != nil {
return fuse.ToStatus(err)
}
}
- // Set owner
- if fs.args.PreserveOwner {
- err = syscallcompat.Fchownat(dirfd, cName, int(context.Owner.Uid),
- int(context.Owner.Gid), unix.AT_SYMLINK_NOFOLLOW)
+ // Set mode
+ if origMode != mode {
+ dirfd2, err := syscallcompat.Openat(dirfd, cName,
+ syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
if err != nil {
- tlog.Warn.Printf("Mkdir %q: Fchownat %d:%d failed: %v", cName, context.Owner.Uid, context.Owner.Gid, err)
- // In case of a failure, we don't want to proceed setting more
- // permissive modes.
+ tlog.Warn.Printf("Mkdir %q: Openat failed: %v", cName, err)
return fuse.ToStatus(err)
}
- }
- // Set mode
- if origMode != mode {
- err = syscallcompat.Fchmodat(dirfd, cName, origMode, unix.AT_SYMLINK_NOFOLLOW)
+ defer syscall.Close(dirfd2)
+
+ var st syscall.Stat_t
+ err = syscall.Fstat(dirfd2, &st)
+ if err != nil {
+ tlog.Warn.Printf("Mkdir %q: Fstat failed: %v", cName, err)
+ return fuse.ToStatus(err)
+ }
+
+ // Preserve SGID bit if it was set due to inheritance.
+ origMode = uint32(st.Mode&^0777) | origMode
+ err = syscall.Fchmod(dirfd2, origMode)
if err != nil {
- tlog.Warn.Printf("Mkdir %q: Fchmodat %#o -> %#o failed: %v", cName, mode, origMode, err)
+ tlog.Warn.Printf("Mkdir %q: Fchmod %#o -> %#o failed: %v", cName, mode, origMode, err)
+ return fuse.ToStatus(err)
}
}
return fuse.OK
diff --git a/internal/syscallcompat/sys_darwin.go b/internal/syscallcompat/sys_darwin.go
index 7defc5f..3c431b9 100644
--- a/internal/syscallcompat/sys_darwin.go
+++ b/internal/syscallcompat/sys_darwin.go
@@ -79,6 +79,11 @@ func Mkdirat(dirfd int, path string, mode uint32) (err error) {
return emulateMkdirat(dirfd, path, mode)
}
+func MkdiratUser(dirfd int, path string, mode uint32, context *fuse.Context) (err error) {
+ // FIXME: take into account context.Owner
+ return Mkdirat(dirfd, path, mode)
+}
+
func Fstatat(dirfd int, path string, stat *unix.Stat_t, flags int) (err error) {
return emulateFstatat(dirfd, path, stat, flags)
}
diff --git a/internal/syscallcompat/sys_linux.go b/internal/syscallcompat/sys_linux.go
index 595aa1d..a431195 100644
--- a/internal/syscallcompat/sys_linux.go
+++ b/internal/syscallcompat/sys_linux.go
@@ -180,6 +180,28 @@ func Mkdirat(dirfd int, path string, mode uint32) (err error) {
return syscall.Mkdirat(dirfd, path, mode)
}
+// MkdiratUser runs the Mkdirat syscall in the context of a different user.
+func MkdiratUser(dirfd int, path string, mode uint32, context *fuse.Context) (err error) {
+ if context != nil {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ err = syscall.Setregid(-1, int(context.Owner.Gid))
+ if err != nil {
+ return err
+ }
+ defer syscall.Setregid(-1, 0)
+
+ err = syscall.Setreuid(-1, int(context.Owner.Uid))
+ if err != nil {
+ return err
+ }
+ defer syscall.Setreuid(-1, 0)
+ }
+
+ return Mkdirat(dirfd, path, mode)
+}
+
// Fstatat syscall.
func Fstatat(dirfd int, path string, stat *unix.Stat_t, flags int) (err error) {
// Why would we ever want to call this without AT_SYMLINK_NOFOLLOW?