diff options
| -rw-r--r-- | internal/fusefrontend/fs.go | 45 | 
1 files changed, 24 insertions, 21 deletions
| diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go index d68939b..0e198d6 100644 --- a/internal/fusefrontend/fs.go +++ b/internal/fusefrontend/fs.go @@ -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  // 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) {  	newFlags = int(flags)  	// 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  	// EINVAL errors due to missing alignment. Just fall back to buffered IO.  	newFlags = newFlags &^ syscallcompat.O_DIRECT - +	// We always want O_NOFOLLOW to be safe against symlink races +	newFlags |= syscall.O_NOFOLLOW  	return newFlags  } @@ -118,30 +120,29 @@ func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile n  	if fs.isFiltered(path) {  		return nil, fuse.EPERM  	} +	newFlags := fs.mangleOpenFlags(flags)  	// Taking this lock makes sure we don't race openWriteOnlyFile()  	fs.openWriteOnlyLock.RLock()  	defer fs.openWriteOnlyLock.RUnlock() - -	newFlags := fs.mangleOpenFlags(flags) -	cPath, err := fs.getBackingPath(path) +	// Symlink-safe open +	dirfd, cName, err := fs.openBackingDir(path)  	if err != nil { -		tlog.Debug.Printf("Open: getBackingPath: %v", err)  		return nil, fuse.ToStatus(err)  	} -	tlog.Debug.Printf("Open: %s", cPath) -	f, err := os.OpenFile(cPath, newFlags, 0) +	fd, err := syscallcompat.Openat(int(dirfd.Fd()), cName, newFlags, 0) +	// Handle a few specific errors  	if err != nil { -		sysErr := err.(*os.PathError).Err -		if sysErr == syscall.EMFILE { +		if err == syscall.EMFILE {  			var lim syscall.Rlimit  			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) { -			return fs.openWriteOnlyFile(cPath, newFlags) +		if err == syscall.EACCES && (int(flags)&os.O_WRONLY > 0) { +			return fs.openWriteOnlyFile(dirfd, cName, newFlags)  		}  		return nil, fuse.ToStatus(err)  	} +	f := os.NewFile(uintptr(fd), cName)  	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).  // This function works around that problem by chmod'ing the file, obtaining a fd,  // and chmod'ing it back. -func (fs *FS) openWriteOnlyFile(cPath string, newFlags int) (fuseFile nodefs.File, status fuse.Status) { -	woFd, err := os.OpenFile(cPath, os.O_WRONLY, 0) +func (fs *FS) openWriteOnlyFile(dirfd *os.File, cName string, newFlags int) (fuseFile nodefs.File, status fuse.Status) { +	woFd, err := syscallcompat.Openat(int(dirfd.Fd()), cName, syscall.O_WRONLY|syscall.O_NOFOLLOW, 0)  	if err != nil {  		return nil, fuse.ToStatus(err)  	} -	defer woFd.Close() -	fi, err := woFd.Stat() +	defer syscall.Close(woFd) +	var st syscall.Stat_t +	err = syscall.Fstat(woFd, &st)  	if err != nil {  		return nil, fuse.ToStatus(err)  	} -	perms := fi.Mode().Perm() +	perms := st.Mode & 0777  	// Verify that we don't have read permissions  	if perms&0400 != 0 {  		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()  	}()  	// Relax permissions and revert on return -	err = woFd.Chmod(perms | 0400) +	syscall.Fchmod(woFd, perms|0400)  	if err != nil {  		tlog.Warn.Printf("openWriteOnlyFile: changing permissions failed: %v", err)  		return nil, fuse.ToStatus(err)  	}  	defer func() { -		err2 := woFd.Chmod(perms) +		err2 := syscall.Fchmod(woFd, perms)  		if err2 != nil {  			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 {  		return nil, fuse.ToStatus(err)  	} -	return NewFile(rwFd, fs) +	f := os.NewFile(uintptr(rwFd), cName) +	return NewFile(f, fs)  }  // Create implements pathfs.Filesystem. | 
