fusefrontent: make Open() symlink-safe
This commit is contained in:
parent
05c8d4a1c4
commit
22fba4ac3e
@ -96,6 +96,7 @@ func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Stat
|
|||||||
|
|
||||||
// mangleOpenFlags is used by Create() and Open() to convert the open flags the user
|
// 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.
|
// 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) {
|
func (fs *FS) mangleOpenFlags(flags uint32) (newFlags int) {
|
||||||
newFlags = int(flags)
|
newFlags = int(flags)
|
||||||
// Convert WRONLY to RDWR. We always need read access to do read-modify-write cycles.
|
// Convert WRONLY to RDWR. We always need read access to do read-modify-write cycles.
|
||||||
@ -109,7 +110,8 @@ func (fs *FS) mangleOpenFlags(flags uint32) (newFlags int) {
|
|||||||
// accesses. Running xfstests generic/013 on ext4 used to trigger lots of
|
// accesses. Running xfstests generic/013 on ext4 used to trigger lots of
|
||||||
// EINVAL errors due to missing alignment. Just fall back to buffered IO.
|
// EINVAL errors due to missing alignment. Just fall back to buffered IO.
|
||||||
newFlags = newFlags &^ syscallcompat.O_DIRECT
|
newFlags = newFlags &^ syscallcompat.O_DIRECT
|
||||||
|
// We always want O_NOFOLLOW to be safe against symlink races
|
||||||
|
newFlags |= syscall.O_NOFOLLOW
|
||||||
return newFlags
|
return newFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,30 +120,29 @@ func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile n
|
|||||||
if fs.isFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return nil, fuse.EPERM
|
return nil, fuse.EPERM
|
||||||
}
|
}
|
||||||
|
newFlags := fs.mangleOpenFlags(flags)
|
||||||
// Taking this lock makes sure we don't race openWriteOnlyFile()
|
// Taking this lock makes sure we don't race openWriteOnlyFile()
|
||||||
fs.openWriteOnlyLock.RLock()
|
fs.openWriteOnlyLock.RLock()
|
||||||
defer fs.openWriteOnlyLock.RUnlock()
|
defer fs.openWriteOnlyLock.RUnlock()
|
||||||
|
// Symlink-safe open
|
||||||
newFlags := fs.mangleOpenFlags(flags)
|
dirfd, cName, err := fs.openBackingDir(path)
|
||||||
cPath, err := fs.getBackingPath(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Debug.Printf("Open: getBackingPath: %v", err)
|
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
tlog.Debug.Printf("Open: %s", cPath)
|
fd, err := syscallcompat.Openat(int(dirfd.Fd()), cName, newFlags, 0)
|
||||||
f, err := os.OpenFile(cPath, newFlags, 0)
|
// Handle a few specific errors
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sysErr := err.(*os.PathError).Err
|
if err == syscall.EMFILE {
|
||||||
if sysErr == syscall.EMFILE {
|
|
||||||
var lim syscall.Rlimit
|
var lim syscall.Rlimit
|
||||||
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
|
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
|
||||||
tlog.Warn.Printf("Open %q: too many open files. Current \"ulimit -n\": %d", cPath, lim.Cur)
|
tlog.Warn.Printf("Open %q: too many open files. Current \"ulimit -n\": %d", cName, lim.Cur)
|
||||||
}
|
}
|
||||||
if sysErr == syscall.EACCES && (int(flags)&os.O_WRONLY > 0) {
|
if err == syscall.EACCES && (int(flags)&os.O_WRONLY > 0) {
|
||||||
return fs.openWriteOnlyFile(cPath, newFlags)
|
return fs.openWriteOnlyFile(dirfd, cName, newFlags)
|
||||||
}
|
}
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
|
f := os.NewFile(uintptr(fd), cName)
|
||||||
return NewFile(f, fs)
|
return NewFile(f, fs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,17 +150,18 @@ func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile n
|
|||||||
// problem if the file permissions do not allow reading (i.e. 0200 permissions).
|
// 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,
|
// This function works around that problem by chmod'ing the file, obtaining a fd,
|
||||||
// and chmod'ing it back.
|
// and chmod'ing it back.
|
||||||
func (fs *FS) openWriteOnlyFile(cPath string, newFlags int) (fuseFile nodefs.File, status fuse.Status) {
|
func (fs *FS) openWriteOnlyFile(dirfd *os.File, cName string, newFlags int) (fuseFile nodefs.File, status fuse.Status) {
|
||||||
woFd, err := os.OpenFile(cPath, os.O_WRONLY, 0)
|
woFd, err := syscallcompat.Openat(int(dirfd.Fd()), cName, syscall.O_WRONLY|syscall.O_NOFOLLOW, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
defer woFd.Close()
|
defer syscall.Close(woFd)
|
||||||
fi, err := woFd.Stat()
|
var st syscall.Stat_t
|
||||||
|
err = syscall.Fstat(woFd, &st)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
perms := fi.Mode().Perm()
|
perms := st.Mode & 0777
|
||||||
// Verify that we don't have read permissions
|
// Verify that we don't have read permissions
|
||||||
if perms&0400 != 0 {
|
if perms&0400 != 0 {
|
||||||
tlog.Warn.Printf("openWriteOnlyFile: unexpected permissions %#o, returning EPERM", perms)
|
tlog.Warn.Printf("openWriteOnlyFile: unexpected permissions %#o, returning EPERM", perms)
|
||||||
@ -173,22 +175,23 @@ func (fs *FS) openWriteOnlyFile(cPath string, newFlags int) (fuseFile nodefs.Fil
|
|||||||
fs.openWriteOnlyLock.RLock()
|
fs.openWriteOnlyLock.RLock()
|
||||||
}()
|
}()
|
||||||
// Relax permissions and revert on return
|
// Relax permissions and revert on return
|
||||||
err = woFd.Chmod(perms | 0400)
|
syscall.Fchmod(woFd, perms|0400)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Warn.Printf("openWriteOnlyFile: changing permissions failed: %v", err)
|
tlog.Warn.Printf("openWriteOnlyFile: changing permissions failed: %v", err)
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
err2 := woFd.Chmod(perms)
|
err2 := syscall.Fchmod(woFd, perms)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
tlog.Warn.Printf("openWriteOnlyFile: reverting permissions failed: %v", err2)
|
tlog.Warn.Printf("openWriteOnlyFile: reverting permissions failed: %v", err2)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
rwFd, err := os.OpenFile(cPath, newFlags, 0)
|
rwFd, err := syscallcompat.Openat(int(dirfd.Fd()), cName, newFlags, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
return NewFile(rwFd, fs)
|
f := os.NewFile(uintptr(rwFd), cName)
|
||||||
|
return NewFile(f, fs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create implements pathfs.Filesystem.
|
// Create implements pathfs.Filesystem.
|
||||||
|
Loading…
Reference in New Issue
Block a user