fusefrontend: allow_other: close race between mkdir and chown
Fixes the same problem as described in 72b975867a3b9bdf53fc2da62e2ba4a328d7e4ab, except for directories instead of device nodes.
This commit is contained in:
parent
67bcbe81e8
commit
614745ee57
@ -47,7 +47,7 @@ func initDir(args *argContainer) {
|
||||
// Forward mode with filename encryption enabled needs a gocryptfs.diriv
|
||||
// in the root dir
|
||||
if !args.plaintextnames && !args.reverse {
|
||||
err = nametransform.WriteDirIV(args.cipherdir)
|
||||
err = nametransform.WriteDirIV(nil, args.cipherdir)
|
||||
if err != nil {
|
||||
tlog.Fatal.Println(err)
|
||||
os.Exit(exitcodes.Init)
|
||||
|
@ -24,7 +24,7 @@ import (
|
||||
|
||||
const dsStoreName = ".DS_Store"
|
||||
|
||||
func (fs *FS) mkdirWithIv(cPath string, mode uint32) error {
|
||||
func (fs *FS) mkdirWithIv(dirfd *os.File, cName string, mode uint32) 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.
|
||||
@ -32,14 +32,14 @@ func (fs *FS) mkdirWithIv(cPath string, mode uint32) error {
|
||||
// The new directory may take the place of an older one that is still in the cache
|
||||
fs.nameTransform.DirIVCache.Clear()
|
||||
defer fs.dirIVLock.Unlock()
|
||||
err := os.Mkdir(cPath, os.FileMode(mode))
|
||||
err := syscallcompat.Mkdirat(int(dirfd.Fd()), cName, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Create gocryptfs.diriv
|
||||
err = nametransform.WriteDirIV(cPath)
|
||||
err = nametransform.WriteDirIV(dirfd, cName)
|
||||
if err != nil {
|
||||
err2 := syscall.Rmdir(cPath)
|
||||
err2 := syscallcompat.Unlinkat(int(dirfd.Fd()), cName, unix.AT_REMOVEDIR)
|
||||
if err2 != nil {
|
||||
tlog.Warn.Printf("mkdirWithIv: rollback failed: %v", err2)
|
||||
}
|
||||
@ -52,17 +52,19 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu
|
||||
if fs.isFiltered(newPath) {
|
||||
return fuse.EPERM
|
||||
}
|
||||
cPath, err := fs.getBackingPath(newPath)
|
||||
dirfd, cName, err := fs.openBackingPath(newPath)
|
||||
if err != nil {
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
defer dirfd.Close()
|
||||
if fs.args.PlaintextNames {
|
||||
err = os.Mkdir(cPath, os.FileMode(mode))
|
||||
err = syscallcompat.Mkdirat(int(dirfd.Fd()), cName, mode)
|
||||
// Set owner
|
||||
if fs.args.PreserveOwner {
|
||||
err = os.Lchown(cPath, int(context.Owner.Uid), int(context.Owner.Gid))
|
||||
err = syscallcompat.Fchownat(int(dirfd.Fd()), cName, int(context.Owner.Uid),
|
||||
int(context.Owner.Gid), unix.AT_SYMLINK_NOFOLLOW)
|
||||
if err != nil {
|
||||
tlog.Warn.Printf("Mkdir: Lchown failed: %v", err)
|
||||
tlog.Warn.Printf("Mkdir: Fchownat failed: %v", err)
|
||||
}
|
||||
}
|
||||
return fuse.ToStatus(err)
|
||||
@ -73,15 +75,7 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu
|
||||
mode = mode | 0300
|
||||
|
||||
// Handle long file name
|
||||
cName := filepath.Base(cPath)
|
||||
if nametransform.IsLongContent(cName) {
|
||||
var dirfd *os.File
|
||||
dirfd, err = os.Open(filepath.Dir(cPath))
|
||||
if err != nil {
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
defer dirfd.Close()
|
||||
|
||||
// Create ".name"
|
||||
err = fs.nameTransform.WriteLongName(dirfd, cName, newPath)
|
||||
if err != nil {
|
||||
@ -89,33 +83,35 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu
|
||||
}
|
||||
|
||||
// Create directory
|
||||
err = fs.mkdirWithIv(cPath, mode)
|
||||
err = fs.mkdirWithIv(dirfd, cName, mode)
|
||||
if err != nil {
|
||||
nametransform.DeleteLongName(dirfd, cName)
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
} else {
|
||||
err = fs.mkdirWithIv(cPath, mode)
|
||||
err = fs.mkdirWithIv(dirfd, cName, mode)
|
||||
if err != nil {
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
}
|
||||
// Set permissions back to what the user wanted
|
||||
if origMode != mode {
|
||||
err = os.Chmod(cPath, os.FileMode(origMode))
|
||||
err = syscallcompat.Fchmodat(int(dirfd.Fd()), cName, origMode, unix.AT_SYMLINK_NOFOLLOW)
|
||||
if err != nil {
|
||||
tlog.Warn.Printf("Mkdir: Chmod failed: %v", err)
|
||||
tlog.Warn.Printf("Mkdir: Fchmodat failed: %v", err)
|
||||
}
|
||||
}
|
||||
// Set owner
|
||||
if fs.args.PreserveOwner {
|
||||
err = os.Lchown(cPath, int(context.Owner.Uid), int(context.Owner.Gid))
|
||||
err = syscallcompat.Fchownat(int(dirfd.Fd()), cName, int(context.Owner.Uid),
|
||||
int(context.Owner.Gid), unix.AT_SYMLINK_NOFOLLOW)
|
||||
if err != nil {
|
||||
tlog.Warn.Printf("Mkdir: Lchown 1 failed: %v", err)
|
||||
tlog.Warn.Printf("Mkdir: Fchownat 1 failed: %v", err)
|
||||
}
|
||||
err = os.Lchown(filepath.Join(cPath, nametransform.DirIVFilename), int(context.Owner.Uid), int(context.Owner.Gid))
|
||||
err = syscallcompat.Fchownat(int(dirfd.Fd()), filepath.Join(cName, nametransform.DirIVFilename),
|
||||
int(context.Owner.Uid), int(context.Owner.Gid), unix.AT_SYMLINK_NOFOLLOW)
|
||||
if err != nil {
|
||||
tlog.Warn.Printf("Mkdir: Lchown 2 failed: %v", err)
|
||||
tlog.Warn.Printf("Mkdir: Fchownat 2 failed: %v", err)
|
||||
}
|
||||
}
|
||||
return fuse.OK
|
||||
|
@ -75,16 +75,17 @@ func fdReadDirIV(fd *os.File) (iv []byte, err error) {
|
||||
// WriteDirIV - create diriv file inside "dir" (absolute ciphertext path)
|
||||
// This function is exported because it is used from pathfs_frontend, main,
|
||||
// and also the automated tests.
|
||||
func WriteDirIV(dir string) error {
|
||||
func WriteDirIV(dirfd *os.File, dir string) error {
|
||||
iv := cryptocore.RandBytes(DirIVLen)
|
||||
file := filepath.Join(dir, DirIVFilename)
|
||||
// 0400 permissions: gocryptfs.diriv should never be modified after creation.
|
||||
// Don't use "ioutil.WriteFile", it causes trouble on NFS: https://github.com/rfjakob/gocryptfs/issues/105
|
||||
fd, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0400)
|
||||
fdRaw, err := syscallcompat.Openat(int(dirfd.Fd()), file, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0400)
|
||||
if err != nil {
|
||||
tlog.Warn.Printf("WriteDirIV: OpenFile: %v", err)
|
||||
tlog.Warn.Printf("WriteDirIV: Openat: %v", err)
|
||||
return err
|
||||
}
|
||||
fd := os.NewFile(uintptr(fdRaw), file)
|
||||
_, err = fd.Write(iv)
|
||||
if err != nil {
|
||||
tlog.Warn.Printf("WriteDirIV: Write: %v", err)
|
||||
|
@ -187,3 +187,20 @@ func Symlinkat(oldpath string, newdirfd int, newpath string) (err error) {
|
||||
defer syscall.Fchdir(cwd)
|
||||
return syscall.Symlink(oldpath, newpath)
|
||||
}
|
||||
|
||||
// Poor man's Mkdirat.
|
||||
func Mkdirat(dirfd int, path string, mode uint32) (err error) {
|
||||
chdirMutex.Lock()
|
||||
defer chdirMutex.Unlock()
|
||||
cwd, err := syscall.Open(".", syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.Close(cwd)
|
||||
err = syscall.Fchdir(dirfd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.Fchdir(cwd)
|
||||
return syscall.Mkdir(path, mode)
|
||||
}
|
||||
|
@ -109,3 +109,8 @@ func Symlinkat(oldpath string, newdirfd int, newpath string) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Mkdirat syscall.
|
||||
func Mkdirat(dirfd int, path string, mode uint32) (err error) {
|
||||
return syscall.Mkdirat(dirfd, path, mode)
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ func ResetTmpDir(createDirIV bool) {
|
||||
panic(err)
|
||||
}
|
||||
if createDirIV {
|
||||
err = nametransform.WriteDirIV(DefaultCipherDir)
|
||||
err = nametransform.WriteDirIV(nil, DefaultCipherDir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user