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
|
// Forward mode with filename encryption enabled needs a gocryptfs.diriv
|
||||||
// in the root dir
|
// in the root dir
|
||||||
if !args.plaintextnames && !args.reverse {
|
if !args.plaintextnames && !args.reverse {
|
||||||
err = nametransform.WriteDirIV(args.cipherdir)
|
err = nametransform.WriteDirIV(nil, args.cipherdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Println(err)
|
tlog.Fatal.Println(err)
|
||||||
os.Exit(exitcodes.Init)
|
os.Exit(exitcodes.Init)
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
|
|
||||||
const dsStoreName = ".DS_Store"
|
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
|
// Between the creation of the directory and the creation of gocryptfs.diriv
|
||||||
// the directory is inconsistent. Take the lock to prevent other readers
|
// the directory is inconsistent. Take the lock to prevent other readers
|
||||||
// from seeing it.
|
// 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
|
// The new directory may take the place of an older one that is still in the cache
|
||||||
fs.nameTransform.DirIVCache.Clear()
|
fs.nameTransform.DirIVCache.Clear()
|
||||||
defer fs.dirIVLock.Unlock()
|
defer fs.dirIVLock.Unlock()
|
||||||
err := os.Mkdir(cPath, os.FileMode(mode))
|
err := syscallcompat.Mkdirat(int(dirfd.Fd()), cName, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Create gocryptfs.diriv
|
// Create gocryptfs.diriv
|
||||||
err = nametransform.WriteDirIV(cPath)
|
err = nametransform.WriteDirIV(dirfd, cName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err2 := syscall.Rmdir(cPath)
|
err2 := syscallcompat.Unlinkat(int(dirfd.Fd()), cName, unix.AT_REMOVEDIR)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
tlog.Warn.Printf("mkdirWithIv: rollback failed: %v", err2)
|
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) {
|
if fs.isFiltered(newPath) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
}
|
}
|
||||||
cPath, err := fs.getBackingPath(newPath)
|
dirfd, cName, err := fs.openBackingPath(newPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
|
defer dirfd.Close()
|
||||||
if fs.args.PlaintextNames {
|
if fs.args.PlaintextNames {
|
||||||
err = os.Mkdir(cPath, os.FileMode(mode))
|
err = syscallcompat.Mkdirat(int(dirfd.Fd()), cName, mode)
|
||||||
// Set owner
|
// Set owner
|
||||||
if fs.args.PreserveOwner {
|
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 {
|
if err != nil {
|
||||||
tlog.Warn.Printf("Mkdir: Lchown failed: %v", err)
|
tlog.Warn.Printf("Mkdir: Fchownat failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fuse.ToStatus(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
|
mode = mode | 0300
|
||||||
|
|
||||||
// Handle long file name
|
// Handle long file name
|
||||||
cName := filepath.Base(cPath)
|
|
||||||
if nametransform.IsLongContent(cName) {
|
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"
|
// Create ".name"
|
||||||
err = fs.nameTransform.WriteLongName(dirfd, cName, newPath)
|
err = fs.nameTransform.WriteLongName(dirfd, cName, newPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -89,33 +83,35 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create directory
|
// Create directory
|
||||||
err = fs.mkdirWithIv(cPath, mode)
|
err = fs.mkdirWithIv(dirfd, cName, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
nametransform.DeleteLongName(dirfd, cName)
|
nametransform.DeleteLongName(dirfd, cName)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = fs.mkdirWithIv(cPath, mode)
|
err = fs.mkdirWithIv(dirfd, cName, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set permissions back to what the user wanted
|
// Set permissions back to what the user wanted
|
||||||
if origMode != mode {
|
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 {
|
if err != nil {
|
||||||
tlog.Warn.Printf("Mkdir: Chmod failed: %v", err)
|
tlog.Warn.Printf("Mkdir: Fchmodat failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set owner
|
// Set owner
|
||||||
if fs.args.PreserveOwner {
|
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 {
|
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 {
|
if err != nil {
|
||||||
tlog.Warn.Printf("Mkdir: Lchown 2 failed: %v", err)
|
tlog.Warn.Printf("Mkdir: Fchownat 2 failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fuse.OK
|
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)
|
// WriteDirIV - create diriv file inside "dir" (absolute ciphertext path)
|
||||||
// This function is exported because it is used from pathfs_frontend, main,
|
// This function is exported because it is used from pathfs_frontend, main,
|
||||||
// and also the automated tests.
|
// and also the automated tests.
|
||||||
func WriteDirIV(dir string) error {
|
func WriteDirIV(dirfd *os.File, dir string) error {
|
||||||
iv := cryptocore.RandBytes(DirIVLen)
|
iv := cryptocore.RandBytes(DirIVLen)
|
||||||
file := filepath.Join(dir, DirIVFilename)
|
file := filepath.Join(dir, DirIVFilename)
|
||||||
// 0400 permissions: gocryptfs.diriv should never be modified after creation.
|
// 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
|
// 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 {
|
if err != nil {
|
||||||
tlog.Warn.Printf("WriteDirIV: OpenFile: %v", err)
|
tlog.Warn.Printf("WriteDirIV: Openat: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
fd := os.NewFile(uintptr(fdRaw), file)
|
||||||
_, err = fd.Write(iv)
|
_, err = fd.Write(iv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Warn.Printf("WriteDirIV: Write: %v", err)
|
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)
|
defer syscall.Fchdir(cwd)
|
||||||
return syscall.Symlink(oldpath, newpath)
|
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
|
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)
|
panic(err)
|
||||||
}
|
}
|
||||||
if createDirIV {
|
if createDirIV {
|
||||||
err = nametransform.WriteDirIV(DefaultCipherDir)
|
err = nametransform.WriteDirIV(nil, DefaultCipherDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user