2016-07-06 21:51:25 +02:00
|
|
|
// Package fusefrontend interfaces directly with the go-fuse library.
|
2016-02-06 19:27:59 +01:00
|
|
|
package fusefrontend
|
2015-09-08 00:54:24 +02:00
|
|
|
|
2015-12-19 13:21:15 +01:00
|
|
|
// FUSE operations on paths
|
|
|
|
|
2015-09-08 00:54:24 +02:00
|
|
|
import (
|
|
|
|
"os"
|
2015-11-27 22:20:01 +01:00
|
|
|
"sync"
|
2019-09-08 16:29:20 +02:00
|
|
|
"sync/atomic"
|
2015-11-27 22:20:01 +01:00
|
|
|
"syscall"
|
2015-09-08 00:54:24 +02:00
|
|
|
"time"
|
|
|
|
|
2017-11-26 21:59:24 +01:00
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
|
2015-09-08 00:54:24 +02:00
|
|
|
"github.com/hanwen/go-fuse/fuse"
|
|
|
|
"github.com/hanwen/go-fuse/fuse/nodefs"
|
|
|
|
"github.com/hanwen/go-fuse/fuse/pathfs"
|
2016-02-06 19:20:54 +01:00
|
|
|
|
2019-01-02 21:52:52 +01:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/configfile"
|
2016-02-06 20:23:36 +01:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/contentenc"
|
2016-02-06 19:20:54 +01:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
2019-11-16 21:36:27 +01:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/openfiletable"
|
2017-03-18 16:01:50 +01:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/serialize_reads"
|
2016-07-03 20:05:32 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
2016-06-15 23:30:44 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
2015-09-08 00:54:24 +02:00
|
|
|
)
|
|
|
|
|
2016-10-02 06:14:18 +02:00
|
|
|
// FS implements the go-fuse virtual filesystem interface.
|
2015-09-08 00:54:24 +02:00
|
|
|
type FS struct {
|
2019-01-13 20:27:35 +01:00
|
|
|
// Embed pathfs.defaultFileSystem to avoid compile failure when the
|
|
|
|
// pathfs.FileSystem interface gets new functions. defaultFileSystem
|
|
|
|
// provides a no-op implementation for all functions.
|
|
|
|
pathfs.FileSystem
|
|
|
|
args Args // Stores configuration arguments
|
2015-11-27 21:48:58 +01:00
|
|
|
// dirIVLock: Lock()ed if any "gocryptfs.diriv" file is modified
|
|
|
|
// Readers must RLock() it to prevent them from seeing intermediate
|
|
|
|
// states
|
|
|
|
dirIVLock sync.RWMutex
|
2016-02-06 19:20:54 +01:00
|
|
|
// Filename encryption helper
|
2019-02-16 21:55:54 +01:00
|
|
|
nameTransform nametransform.NameTransformer
|
2016-02-06 19:20:54 +01:00
|
|
|
// Content encryption helper
|
|
|
|
contentEnc *contentenc.ContentEnc
|
2017-07-11 23:19:58 +02:00
|
|
|
// This lock is used by openWriteOnlyFile() to block concurrent opens while
|
|
|
|
// it relaxes the permissions on a file.
|
|
|
|
openWriteOnlyLock sync.RWMutex
|
2018-07-01 15:48:53 +02:00
|
|
|
// MitigatedCorruptions is used to report data corruption that is internally
|
|
|
|
// mitigated by ignoring the corrupt item. For example, when OpenDir() finds
|
|
|
|
// a corrupt filename, we still return the other valid filenames.
|
|
|
|
// The corruption is logged to syslog to inform the user, and in addition,
|
|
|
|
// the corrupt filename is logged to this channel via
|
|
|
|
// reportMitigatedCorruption().
|
|
|
|
// "gocryptfs -fsck" reads from the channel to also catch these transparently-
|
|
|
|
// mitigated corruptions.
|
|
|
|
MitigatedCorruptions chan string
|
2019-09-08 16:29:20 +02:00
|
|
|
// This flag is set to zero each time fs.isFiltered() is called
|
|
|
|
// (uint32 so that it can be reset with CompareAndSwapUint32).
|
|
|
|
// When -idle was used when mounting, idleMonitor() sets it to 1
|
|
|
|
// periodically.
|
|
|
|
IsIdle uint32
|
2019-11-16 21:36:27 +01:00
|
|
|
// dirCache caches directory fds
|
2019-01-02 22:32:21 +01:00
|
|
|
dirCache dirCacheStruct
|
2019-11-16 21:36:27 +01:00
|
|
|
// inumMap translates inode numbers from different devices to unique inode
|
|
|
|
// numbers.
|
|
|
|
inumMap *openfiletable.InumMap
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2019-01-13 20:27:35 +01:00
|
|
|
//var _ pathfs.FileSystem = &FS{} // Verify that interface is implemented.
|
2016-10-02 06:14:18 +02:00
|
|
|
|
|
|
|
// NewFS returns a new encrypted FUSE overlay filesystem.
|
2019-02-16 21:55:54 +01:00
|
|
|
func NewFS(args Args, c *contentenc.ContentEnc, n nametransform.NameTransformer) *FS {
|
2017-03-18 16:01:50 +01:00
|
|
|
if args.SerializeReads {
|
2017-04-29 14:50:58 +02:00
|
|
|
serialize_reads.InitSerializer()
|
2017-03-18 16:01:50 +01:00
|
|
|
}
|
2018-08-11 23:26:49 +02:00
|
|
|
if len(args.Exclude) > 0 {
|
|
|
|
tlog.Warn.Printf("Forward mode does not support -exclude")
|
|
|
|
}
|
2019-11-16 21:36:27 +01:00
|
|
|
var st syscall.Stat_t
|
|
|
|
err := syscall.Stat(args.Cipherdir, &st)
|
|
|
|
if err != nil {
|
|
|
|
tlog.Warn.Printf("NewFS: could not stat cipherdir: %v", err)
|
|
|
|
st.Dev = 0
|
|
|
|
}
|
2015-09-08 00:54:24 +02:00
|
|
|
return &FS{
|
2019-01-13 20:27:35 +01:00
|
|
|
FileSystem: pathfs.NewDefaultFileSystem(),
|
2016-02-06 20:23:36 +01:00
|
|
|
args: args,
|
2018-02-17 16:26:35 +01:00
|
|
|
nameTransform: n,
|
|
|
|
contentEnc: c,
|
2019-11-16 23:21:37 +01:00
|
|
|
inumMap: openfiletable.NewInumMap(uint64(st.Dev)), // cast is needed for Darwin
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-02 06:14:18 +02:00
|
|
|
// GetAttr implements pathfs.Filesystem.
|
2018-10-14 21:55:37 +02:00
|
|
|
//
|
|
|
|
// GetAttr is symlink-safe through use of openBackingDir() and Fstatat().
|
|
|
|
func (fs *FS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
|
|
|
tlog.Debug.Printf("FS.GetAttr(%q)", relPath)
|
|
|
|
if fs.isFiltered(relPath) {
|
2015-11-03 22:25:29 +01:00
|
|
|
return nil, fuse.EPERM
|
2015-11-03 00:00:13 +01:00
|
|
|
}
|
2018-10-14 21:55:37 +02:00
|
|
|
dirfd, cName, err := fs.openBackingDir(relPath)
|
2015-11-27 00:03:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, fuse.ToStatus(err)
|
|
|
|
}
|
2018-10-14 21:55:37 +02:00
|
|
|
var st unix.Stat_t
|
|
|
|
err = syscallcompat.Fstatat(dirfd, cName, &st, unix.AT_SYMLINK_NOFOLLOW)
|
|
|
|
syscall.Close(dirfd)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fuse.ToStatus(err)
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
2018-10-14 21:55:37 +02:00
|
|
|
a := &fuse.Attr{}
|
|
|
|
st2 := syscallcompat.Unix2syscall(st)
|
2019-11-16 21:36:27 +01:00
|
|
|
fs.inumMap.TranslateStat(&st2)
|
2018-10-14 21:55:37 +02:00
|
|
|
a.FromStat(&st2)
|
2015-09-16 19:32:37 +02:00
|
|
|
if a.IsRegular() {
|
2016-02-06 19:20:54 +01:00
|
|
|
a.Size = fs.contentEnc.CipherSizeToPlainSize(a.Size)
|
2015-09-16 19:32:37 +02:00
|
|
|
} else if a.IsSymlink() {
|
2018-10-14 21:55:37 +02:00
|
|
|
target, _ := fs.Readlink(relPath, context)
|
2015-09-16 19:32:37 +02:00
|
|
|
a.Size = uint64(len(target))
|
|
|
|
}
|
2017-05-30 23:01:06 +02:00
|
|
|
if fs.args.ForceOwner != nil {
|
|
|
|
a.Owner = *fs.args.ForceOwner
|
|
|
|
}
|
2018-10-14 21:55:37 +02:00
|
|
|
return a, fuse.OK
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2017-05-01 17:49:37 +02:00
|
|
|
// 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.
|
2018-09-22 19:40:48 +02:00
|
|
|
// The returned flags always contain O_NOFOLLOW.
|
2017-05-01 17:49:37 +02:00
|
|
|
func (fs *FS) mangleOpenFlags(flags uint32) (newFlags int) {
|
2015-09-08 00:54:24 +02:00
|
|
|
newFlags = int(flags)
|
2017-05-01 17:49:37 +02:00
|
|
|
// Convert WRONLY to RDWR. We always need read access to do read-modify-write cycles.
|
2019-01-07 18:59:22 +01:00
|
|
|
if (newFlags & syscall.O_ACCMODE) == syscall.O_WRONLY {
|
2015-09-08 00:54:24 +02:00
|
|
|
newFlags = newFlags ^ os.O_WRONLY | os.O_RDWR
|
|
|
|
}
|
|
|
|
// We also cannot open the file in append mode, we need to seek back for RMW
|
|
|
|
newFlags = newFlags &^ os.O_APPEND
|
2018-07-02 23:54:37 +02:00
|
|
|
// O_DIRECT accesses must be aligned in both offset and length. Due to our
|
|
|
|
// crypto header, alignment will be off, even if userspace makes aligned
|
|
|
|
// accesses. Running xfstests generic/013 on ext4 used to trigger lots of
|
|
|
|
// EINVAL errors due to missing alignment. Just fall back to buffered IO.
|
2018-07-04 09:04:00 +02:00
|
|
|
newFlags = newFlags &^ syscallcompat.O_DIRECT
|
2019-01-07 00:21:17 +01:00
|
|
|
// Create and Open are two separate FUSE operations, so O_CREAT should not
|
|
|
|
// be part of the open flags.
|
|
|
|
newFlags = newFlags &^ syscall.O_CREAT
|
2018-09-22 19:40:48 +02:00
|
|
|
// We always want O_NOFOLLOW to be safe against symlink races
|
|
|
|
newFlags |= syscall.O_NOFOLLOW
|
2017-05-01 17:49:37 +02:00
|
|
|
return newFlags
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2018-10-01 21:39:19 +02:00
|
|
|
// Open - FUSE call. Open already-existing file.
|
|
|
|
//
|
|
|
|
// Symlink-safe through Openat().
|
2015-11-03 00:00:13 +01:00
|
|
|
func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) {
|
2015-12-08 16:13:29 +01:00
|
|
|
if fs.isFiltered(path) {
|
2015-11-03 00:00:13 +01:00
|
|
|
return nil, fuse.EPERM
|
|
|
|
}
|
2018-09-22 19:40:48 +02:00
|
|
|
newFlags := fs.mangleOpenFlags(flags)
|
2017-07-11 23:19:58 +02:00
|
|
|
// Taking this lock makes sure we don't race openWriteOnlyFile()
|
|
|
|
fs.openWriteOnlyLock.RLock()
|
|
|
|
defer fs.openWriteOnlyLock.RUnlock()
|
2018-09-22 19:40:48 +02:00
|
|
|
// Symlink-safe open
|
|
|
|
dirfd, cName, err := fs.openBackingDir(path)
|
2015-11-27 00:03:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 20:30:33 +02:00
|
|
|
defer syscall.Close(dirfd)
|
2018-09-22 20:10:34 +02:00
|
|
|
fd, err := syscallcompat.Openat(dirfd, cName, newFlags, 0)
|
2018-09-22 19:40:48 +02:00
|
|
|
// Handle a few specific errors
|
2015-09-08 00:54:24 +02:00
|
|
|
if err != nil {
|
2018-09-22 19:40:48 +02:00
|
|
|
if err == syscall.EMFILE {
|
2017-05-03 23:46:52 +02:00
|
|
|
var lim syscall.Rlimit
|
|
|
|
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
|
2018-09-22 19:40:48 +02:00
|
|
|
tlog.Warn.Printf("Open %q: too many open files. Current \"ulimit -n\": %d", cName, lim.Cur)
|
2017-05-03 23:46:52 +02:00
|
|
|
}
|
2019-01-07 18:59:22 +01:00
|
|
|
if err == syscall.EACCES && (int(flags)&syscall.O_ACCMODE) == syscall.O_WRONLY {
|
2018-09-22 19:40:48 +02:00
|
|
|
return fs.openWriteOnlyFile(dirfd, cName, newFlags)
|
2017-07-11 23:19:58 +02:00
|
|
|
}
|
2015-09-08 00:54:24 +02:00
|
|
|
return nil, fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 19:40:48 +02:00
|
|
|
f := os.NewFile(uintptr(fd), cName)
|
2017-05-01 17:49:37 +02:00
|
|
|
return NewFile(f, fs)
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2019-01-02 20:48:46 +01:00
|
|
|
// openBackingFile opens the ciphertext file that backs relative plaintext
|
|
|
|
// path "relPath". Always adds O_NOFOLLOW to the flags.
|
|
|
|
func (fs *FS) openBackingFile(relPath string, flags int) (fd int, err error) {
|
|
|
|
dirfd, cName, err := fs.openBackingDir(relPath)
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
defer syscall.Close(dirfd)
|
|
|
|
return syscallcompat.Openat(dirfd, cName, flags|syscall.O_NOFOLLOW, 0)
|
|
|
|
}
|
|
|
|
|
2017-07-11 23:19:58 +02:00
|
|
|
// Due to RMW, we always need read permissions on the backing file. This is a
|
|
|
|
// 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.
|
2019-01-02 20:45:08 +01:00
|
|
|
func (fs *FS) openWriteOnlyFile(dirfd int, cName string, newFlags int) (*File, fuse.Status) {
|
2018-09-22 20:10:34 +02:00
|
|
|
woFd, err := syscallcompat.Openat(dirfd, cName, syscall.O_WRONLY|syscall.O_NOFOLLOW, 0)
|
2017-07-11 23:19:58 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 19:40:48 +02:00
|
|
|
defer syscall.Close(woFd)
|
|
|
|
var st syscall.Stat_t
|
|
|
|
err = syscall.Fstat(woFd, &st)
|
2017-07-11 23:19:58 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 21:10:54 +02:00
|
|
|
// The cast to uint32 fixes a build failure on Darwin, where st.Mode is uint16.
|
2019-01-12 21:24:48 +01:00
|
|
|
perms := uint32(st.Mode)
|
2017-07-11 23:19:58 +02:00
|
|
|
// Verify that we don't have read permissions
|
|
|
|
if perms&0400 != 0 {
|
|
|
|
tlog.Warn.Printf("openWriteOnlyFile: unexpected permissions %#o, returning EPERM", perms)
|
|
|
|
return nil, fuse.ToStatus(syscall.EPERM)
|
|
|
|
}
|
|
|
|
// Upgrade the lock to block other Open()s and downgrade again on return
|
|
|
|
fs.openWriteOnlyLock.RUnlock()
|
|
|
|
fs.openWriteOnlyLock.Lock()
|
|
|
|
defer func() {
|
|
|
|
fs.openWriteOnlyLock.Unlock()
|
|
|
|
fs.openWriteOnlyLock.RLock()
|
|
|
|
}()
|
|
|
|
// Relax permissions and revert on return
|
2019-01-06 19:53:26 +01:00
|
|
|
err = syscall.Fchmod(woFd, perms|0400)
|
2017-07-11 23:19:58 +02:00
|
|
|
if err != nil {
|
|
|
|
tlog.Warn.Printf("openWriteOnlyFile: changing permissions failed: %v", err)
|
|
|
|
return nil, fuse.ToStatus(err)
|
|
|
|
}
|
|
|
|
defer func() {
|
2018-09-22 19:40:48 +02:00
|
|
|
err2 := syscall.Fchmod(woFd, perms)
|
2017-07-11 23:19:58 +02:00
|
|
|
if err2 != nil {
|
|
|
|
tlog.Warn.Printf("openWriteOnlyFile: reverting permissions failed: %v", err2)
|
|
|
|
}
|
|
|
|
}()
|
2018-09-22 20:10:34 +02:00
|
|
|
rwFd, err := syscallcompat.Openat(dirfd, cName, newFlags, 0)
|
2017-07-11 23:19:58 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 19:40:48 +02:00
|
|
|
f := os.NewFile(uintptr(rwFd), cName)
|
|
|
|
return NewFile(f, fs)
|
2017-07-11 23:19:58 +02:00
|
|
|
}
|
|
|
|
|
2018-10-01 21:39:19 +02:00
|
|
|
// Create - FUSE call. Creates a new file.
|
|
|
|
//
|
|
|
|
// Symlink-safe through the use of Openat().
|
2018-09-22 20:31:49 +02:00
|
|
|
func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
|
2015-12-08 16:13:29 +01:00
|
|
|
if fs.isFiltered(path) {
|
2015-11-03 22:25:29 +01:00
|
|
|
return nil, fuse.EPERM
|
2015-11-03 00:00:13 +01:00
|
|
|
}
|
2017-05-01 17:49:37 +02:00
|
|
|
newFlags := fs.mangleOpenFlags(flags)
|
2018-09-22 20:31:49 +02:00
|
|
|
dirfd, cName, err := fs.openBackingDir(path)
|
2015-11-27 00:03:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 20:31:49 +02:00
|
|
|
defer syscall.Close(dirfd)
|
|
|
|
fd := -1
|
2019-01-12 20:42:05 +01:00
|
|
|
// Make sure context is nil if we don't want to preserve the owner
|
|
|
|
if !fs.args.PreserveOwner {
|
|
|
|
context = nil
|
|
|
|
}
|
2016-05-29 22:41:46 +02:00
|
|
|
// Handle long file name
|
2018-01-17 00:23:09 +01:00
|
|
|
if !fs.args.PlaintextNames && nametransform.IsLongContent(cName) {
|
2016-04-10 19:32:10 +02:00
|
|
|
// Create ".name"
|
2018-11-04 22:05:38 +01:00
|
|
|
err = fs.nameTransform.WriteLongNameAt(dirfd, cName, path)
|
2016-04-10 19:32:10 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, fuse.ToStatus(err)
|
|
|
|
}
|
|
|
|
// Create content
|
2019-01-15 11:35:20 +01:00
|
|
|
fd, err = syscallcompat.OpenatUser(dirfd, cName, newFlags|syscall.O_CREAT|syscall.O_EXCL, mode, context)
|
2016-02-06 22:54:14 +01:00
|
|
|
if err != nil {
|
2018-11-04 22:05:38 +01:00
|
|
|
nametransform.DeleteLongNameAt(dirfd, cName)
|
2016-02-06 22:54:14 +01:00
|
|
|
}
|
2016-05-29 22:41:46 +02:00
|
|
|
} else {
|
2018-09-22 20:31:49 +02:00
|
|
|
// Create content, normal (short) file name
|
2019-01-12 20:42:05 +01:00
|
|
|
fd, err = syscallcompat.OpenatUser(dirfd, cName, newFlags|syscall.O_CREAT|syscall.O_EXCL, mode, context)
|
2019-01-15 11:35:20 +01:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
// xfstests generic/488 triggers this
|
|
|
|
if err == syscall.EMFILE {
|
|
|
|
var lim syscall.Rlimit
|
|
|
|
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
|
|
|
|
tlog.Warn.Printf("Create %q: too many open files. Current \"ulimit -n\": %d", cName, lim.Cur)
|
2016-05-29 22:41:46 +02:00
|
|
|
}
|
2019-01-15 11:35:20 +01:00
|
|
|
return nil, fuse.ToStatus(err)
|
2016-02-06 22:54:14 +01:00
|
|
|
}
|
2018-09-22 20:31:49 +02:00
|
|
|
f := os.NewFile(uintptr(fd), cName)
|
|
|
|
return NewFile(f, fs)
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:08:49 +01:00
|
|
|
// Chmod - FUSE call. Change permissions on "path".
|
2018-10-01 21:39:19 +02:00
|
|
|
//
|
|
|
|
// Symlink-safe through use of Fchmodat().
|
2015-09-08 00:54:24 +02:00
|
|
|
func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
2015-12-08 16:13:29 +01:00
|
|
|
if fs.isFiltered(path) {
|
2015-11-03 22:25:29 +01:00
|
|
|
return fuse.EPERM
|
2015-11-03 00:00:13 +01:00
|
|
|
}
|
fusefrontend: use OpenDirNofollow in openBackingDir
Rename openBackingPath to openBackingDir and use OpenDirNofollow
to be safe against symlink races. Note that openBackingDir is
not used in several important code paths like Create().
But it is used in Unlink, and the performance impact in the RM benchmark
to be acceptable:
Before
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.bYO: gocryptfs v1.6-12-g930c37e-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.07979 s, 243 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.882413 s, 297 MB/s
UNTAR: 16.703
MD5: 7.606
LS: 1.349
RM: 3.237
After
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.jK3: gocryptfs v1.6-13-g84d6faf-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.06261 s, 247 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.947228 s, 277 MB/s
UNTAR: 17.197
MD5: 7.540
LS: 1.364
RM: 3.410
2018-09-08 19:27:33 +02:00
|
|
|
dirfd, cName, err := fs.openBackingDir(path)
|
2015-11-27 00:03:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 20:10:34 +02:00
|
|
|
defer syscall.Close(dirfd)
|
2016-06-26 20:13:21 +02:00
|
|
|
// os.Chmod goes through the "syscallMode" translation function that messes
|
2017-11-29 12:54:34 +01:00
|
|
|
// up the suid and sgid bits. So use a syscall directly.
|
2019-01-14 21:54:16 +01:00
|
|
|
err = syscallcompat.FchmodatNofollow(dirfd, cName, mode)
|
2016-06-26 20:13:21 +02:00
|
|
|
return fuse.ToStatus(err)
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2018-10-01 21:39:19 +02:00
|
|
|
// Chown - FUSE call. Change the owner of "path".
|
|
|
|
//
|
|
|
|
// Symlink-safe through use of Fchownat().
|
2015-09-08 00:54:24 +02:00
|
|
|
func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
2015-12-08 16:13:29 +01:00
|
|
|
if fs.isFiltered(path) {
|
2015-11-03 22:25:29 +01:00
|
|
|
return fuse.EPERM
|
2015-11-03 00:00:13 +01:00
|
|
|
}
|
fusefrontend: use OpenDirNofollow in openBackingDir
Rename openBackingPath to openBackingDir and use OpenDirNofollow
to be safe against symlink races. Note that openBackingDir is
not used in several important code paths like Create().
But it is used in Unlink, and the performance impact in the RM benchmark
to be acceptable:
Before
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.bYO: gocryptfs v1.6-12-g930c37e-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.07979 s, 243 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.882413 s, 297 MB/s
UNTAR: 16.703
MD5: 7.606
LS: 1.349
RM: 3.237
After
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.jK3: gocryptfs v1.6-13-g84d6faf-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.06261 s, 247 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.947228 s, 277 MB/s
UNTAR: 17.197
MD5: 7.540
LS: 1.364
RM: 3.410
2018-09-08 19:27:33 +02:00
|
|
|
dirfd, cName, err := fs.openBackingDir(path)
|
2015-11-27 00:03:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 20:10:34 +02:00
|
|
|
defer syscall.Close(dirfd)
|
2019-01-09 02:40:10 +01:00
|
|
|
err = syscallcompat.Fchownat(dirfd, cName, int(uid), int(gid), unix.AT_SYMLINK_NOFOLLOW)
|
|
|
|
return fuse.ToStatus(err)
|
2015-09-18 22:22:11 +02:00
|
|
|
}
|
|
|
|
|
2018-10-01 21:39:19 +02:00
|
|
|
// Mknod - FUSE call. Create a device file.
|
|
|
|
//
|
|
|
|
// Symlink-safe through use of Mknodat().
|
2015-11-27 00:03:10 +01:00
|
|
|
func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) {
|
2015-12-08 16:13:29 +01:00
|
|
|
if fs.isFiltered(path) {
|
2015-11-03 22:25:29 +01:00
|
|
|
return fuse.EPERM
|
2015-11-03 00:00:13 +01:00
|
|
|
}
|
fusefrontend: use OpenDirNofollow in openBackingDir
Rename openBackingPath to openBackingDir and use OpenDirNofollow
to be safe against symlink races. Note that openBackingDir is
not used in several important code paths like Create().
But it is used in Unlink, and the performance impact in the RM benchmark
to be acceptable:
Before
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.bYO: gocryptfs v1.6-12-g930c37e-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.07979 s, 243 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.882413 s, 297 MB/s
UNTAR: 16.703
MD5: 7.606
LS: 1.349
RM: 3.237
After
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.jK3: gocryptfs v1.6-13-g84d6faf-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.06261 s, 247 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.947228 s, 277 MB/s
UNTAR: 17.197
MD5: 7.540
LS: 1.364
RM: 3.410
2018-09-08 19:27:33 +02:00
|
|
|
dirfd, cName, err := fs.openBackingDir(path)
|
2017-11-26 21:59:24 +01:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 20:10:34 +02:00
|
|
|
defer syscall.Close(dirfd)
|
2019-01-12 21:19:23 +01:00
|
|
|
// Make sure context is nil if we don't want to preserve the owner
|
|
|
|
if !fs.args.PreserveOwner {
|
|
|
|
context = nil
|
|
|
|
}
|
2017-11-28 00:20:42 +01:00
|
|
|
// Create ".name" file to store long file name (except in PlaintextNames mode)
|
|
|
|
if !fs.args.PlaintextNames && nametransform.IsLongContent(cName) {
|
2018-11-04 22:05:38 +01:00
|
|
|
err = fs.nameTransform.WriteLongNameAt(dirfd, cName, path)
|
2016-04-10 19:32:10 +02:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
2016-11-28 23:09:47 +01:00
|
|
|
// Create "gocryptfs.longfile." device node
|
2019-01-12 21:19:23 +01:00
|
|
|
err = syscallcompat.MknodatUser(dirfd, cName, mode, int(dev), context)
|
2016-04-10 19:32:10 +02:00
|
|
|
if err != nil {
|
2018-11-04 22:05:38 +01:00
|
|
|
nametransform.DeleteLongNameAt(dirfd, cName)
|
2016-04-10 19:32:10 +02:00
|
|
|
}
|
2016-11-28 23:09:47 +01:00
|
|
|
} else {
|
|
|
|
// Create regular device node
|
2019-01-12 21:19:23 +01:00
|
|
|
err = syscallcompat.MknodatUser(dirfd, cName, mode, int(dev), context)
|
2016-11-28 23:09:47 +01:00
|
|
|
}
|
2019-01-12 21:19:23 +01:00
|
|
|
return fuse.ToStatus(err)
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2018-11-04 22:31:55 +01:00
|
|
|
// Truncate - FUSE call. Truncates a file.
|
|
|
|
//
|
2016-06-26 18:35:19 +02:00
|
|
|
// Support truncate(2) by opening the file and calling ftruncate(2)
|
|
|
|
// While the glibc "truncate" wrapper seems to always use ftruncate, fsstress from
|
|
|
|
// xfstests uses this a lot by calling "truncate64" directly.
|
2018-11-04 22:31:55 +01:00
|
|
|
//
|
|
|
|
// Symlink-safe by letting file.Truncate() do all the work.
|
2015-09-08 00:54:24 +02:00
|
|
|
func (fs *FS) Truncate(path string, offset uint64, context *fuse.Context) (code fuse.Status) {
|
2016-06-26 18:35:19 +02:00
|
|
|
file, code := fs.Open(path, uint32(os.O_RDWR), context)
|
|
|
|
if code != fuse.OK {
|
|
|
|
return code
|
|
|
|
}
|
|
|
|
code = file.Truncate(offset)
|
|
|
|
file.Release()
|
|
|
|
return code
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2018-10-01 21:39:19 +02:00
|
|
|
// Utimens - FUSE call. Set the timestamps on file "path".
|
2018-11-04 22:53:41 +01:00
|
|
|
//
|
|
|
|
// Symlink-safe through UtimesNanoAt.
|
2016-08-09 22:18:46 +02:00
|
|
|
func (fs *FS) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Context) (code fuse.Status) {
|
2015-12-08 16:13:29 +01:00
|
|
|
if fs.isFiltered(path) {
|
2015-11-03 22:25:29 +01:00
|
|
|
return fuse.EPERM
|
2015-11-03 00:00:13 +01:00
|
|
|
}
|
2018-11-04 22:53:41 +01:00
|
|
|
dirfd, cName, err := fs.openBackingDir(path)
|
2015-11-27 00:03:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
2018-11-04 22:53:41 +01:00
|
|
|
defer syscall.Close(dirfd)
|
2019-01-16 15:38:32 +01:00
|
|
|
err = syscallcompat.UtimesNanoAtNofollow(dirfd, cName, a, m)
|
2018-11-04 22:53:41 +01:00
|
|
|
return fuse.ToStatus(err)
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2018-10-01 21:39:19 +02:00
|
|
|
// StatFs - FUSE call. Returns information about the filesystem.
|
|
|
|
//
|
|
|
|
// Symlink-safe because the passed path is ignored.
|
2016-05-12 19:51:13 +02:00
|
|
|
func (fs *FS) StatFs(path string) *fuse.StatfsOut {
|
2019-01-13 20:27:35 +01:00
|
|
|
var st syscall.Statfs_t
|
|
|
|
err := syscall.Statfs(fs.args.Cipherdir, &st)
|
|
|
|
if err == nil {
|
|
|
|
var out fuse.StatfsOut
|
|
|
|
out.FromStatfsT(&st)
|
|
|
|
return &out
|
|
|
|
}
|
|
|
|
return nil
|
2016-05-12 19:51:13 +02:00
|
|
|
}
|
|
|
|
|
2018-03-24 21:36:45 +01:00
|
|
|
// decryptSymlinkTarget: "cData64" is base64-decoded and decrypted
|
|
|
|
// like file contents (GCM).
|
2018-03-25 21:02:33 +02:00
|
|
|
// The empty string decrypts to the empty string.
|
2018-11-04 22:01:18 +01:00
|
|
|
//
|
|
|
|
// This function does not do any I/O and is hence symlink-safe.
|
2018-03-24 21:36:45 +01:00
|
|
|
func (fs *FS) decryptSymlinkTarget(cData64 string) (string, error) {
|
2018-03-25 21:02:33 +02:00
|
|
|
if cData64 == "" {
|
|
|
|
return "", nil
|
|
|
|
}
|
2019-02-16 21:55:54 +01:00
|
|
|
cData, err := fs.nameTransform.B64DecodeString(cData64)
|
2018-03-24 21:36:45 +01:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
data, err := fs.contentEnc.DecryptBlock([]byte(cData), 0, nil)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(data), nil
|
|
|
|
}
|
|
|
|
|
2018-11-04 22:01:18 +01:00
|
|
|
// Readlink - FUSE call.
|
|
|
|
//
|
|
|
|
// Symlink-safe through openBackingDir() + Readlinkat().
|
2018-04-02 18:32:30 +02:00
|
|
|
func (fs *FS) Readlink(relPath string, context *fuse.Context) (out string, status fuse.Status) {
|
2018-11-04 22:01:18 +01:00
|
|
|
dirfd, cName, err := fs.openBackingDir(relPath)
|
2015-11-27 00:03:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return "", fuse.ToStatus(err)
|
|
|
|
}
|
2018-11-04 22:01:18 +01:00
|
|
|
defer syscall.Close(dirfd)
|
|
|
|
cTarget, err := syscallcompat.Readlinkat(dirfd, cName)
|
2016-09-25 17:01:04 +02:00
|
|
|
if err != nil {
|
|
|
|
return "", fuse.ToStatus(err)
|
2015-09-09 19:32:59 +02:00
|
|
|
}
|
2016-06-23 21:29:00 +02:00
|
|
|
if fs.args.PlaintextNames {
|
|
|
|
return cTarget, fuse.OK
|
2015-11-28 16:52:57 +01:00
|
|
|
}
|
2016-06-23 21:29:00 +02:00
|
|
|
// Symlinks are encrypted like file contents (GCM) and base64-encoded
|
2018-03-24 21:36:45 +01:00
|
|
|
target, err := fs.decryptSymlinkTarget(cTarget)
|
2015-11-28 16:47:30 +01:00
|
|
|
if err != nil {
|
2018-11-04 22:01:18 +01:00
|
|
|
tlog.Warn.Printf("Readlink %q: decrypting target failed: %v", cName, err)
|
2015-11-28 16:47:30 +01:00
|
|
|
return "", fuse.EIO
|
|
|
|
}
|
|
|
|
return string(target), fuse.OK
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2018-11-04 22:31:55 +01:00
|
|
|
// Unlink - FUSE call. Delete a file.
|
|
|
|
//
|
|
|
|
// Symlink-safe through use of Unlinkat().
|
2015-11-27 00:03:10 +01:00
|
|
|
func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) {
|
2015-12-08 16:13:29 +01:00
|
|
|
if fs.isFiltered(path) {
|
2015-11-03 22:25:29 +01:00
|
|
|
return fuse.EPERM
|
2015-11-03 00:00:13 +01:00
|
|
|
}
|
fusefrontend: use OpenDirNofollow in openBackingDir
Rename openBackingPath to openBackingDir and use OpenDirNofollow
to be safe against symlink races. Note that openBackingDir is
not used in several important code paths like Create().
But it is used in Unlink, and the performance impact in the RM benchmark
to be acceptable:
Before
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.bYO: gocryptfs v1.6-12-g930c37e-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.07979 s, 243 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.882413 s, 297 MB/s
UNTAR: 16.703
MD5: 7.606
LS: 1.349
RM: 3.237
After
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.jK3: gocryptfs v1.6-13-g84d6faf-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.06261 s, 247 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.947228 s, 277 MB/s
UNTAR: 17.197
MD5: 7.540
LS: 1.364
RM: 3.410
2018-09-08 19:27:33 +02:00
|
|
|
dirfd, cName, err := fs.openBackingDir(path)
|
2015-11-27 00:03:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
2015-09-16 19:32:37 +02:00
|
|
|
}
|
2018-09-22 20:10:34 +02:00
|
|
|
defer syscall.Close(dirfd)
|
2017-11-28 01:28:29 +01:00
|
|
|
// Delete content
|
2018-09-22 20:10:34 +02:00
|
|
|
err = syscallcompat.Unlinkat(dirfd, cName, 0)
|
2017-11-28 01:28:29 +01:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
|
|
|
// Delete ".name" file
|
2017-11-28 01:22:55 +01:00
|
|
|
if !fs.args.PlaintextNames && nametransform.IsLongContent(cName) {
|
2018-11-04 22:05:38 +01:00
|
|
|
err = nametransform.DeleteLongNameAt(dirfd, cName)
|
2016-04-10 19:32:10 +02:00
|
|
|
if err != nil {
|
2016-06-15 23:30:44 +02:00
|
|
|
tlog.Warn.Printf("Unlink: could not delete .name file: %v", err)
|
2016-04-10 19:32:10 +02:00
|
|
|
}
|
2016-02-07 14:02:09 +01:00
|
|
|
}
|
|
|
|
return fuse.ToStatus(err)
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2018-03-24 21:36:45 +01:00
|
|
|
// encryptSymlinkTarget: "data" is encrypted like file contents (GCM)
|
|
|
|
// and base64-encoded.
|
2018-03-25 21:02:33 +02:00
|
|
|
// The empty string encrypts to the empty string.
|
2018-11-04 22:31:55 +01:00
|
|
|
//
|
|
|
|
// Symlink-safe because it does not do any I/O.
|
2018-03-24 21:36:45 +01:00
|
|
|
func (fs *FS) encryptSymlinkTarget(data string) (cData64 string) {
|
2018-03-25 21:02:33 +02:00
|
|
|
if data == "" {
|
|
|
|
return ""
|
|
|
|
}
|
2018-03-24 21:36:45 +01:00
|
|
|
cData := fs.contentEnc.EncryptBlock([]byte(data), 0, nil)
|
2019-02-16 21:55:54 +01:00
|
|
|
cData64 = fs.nameTransform.B64EncodeToString(cData)
|
2018-03-24 21:36:45 +01:00
|
|
|
return cData64
|
|
|
|
}
|
|
|
|
|
2018-11-04 22:31:55 +01:00
|
|
|
// Symlink - FUSE call. Create a symlink.
|
|
|
|
//
|
|
|
|
// Symlink-safe through use of Symlinkat.
|
2015-11-27 00:03:10 +01:00
|
|
|
func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) {
|
2016-06-15 23:30:44 +02:00
|
|
|
tlog.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName)
|
2015-12-08 16:13:29 +01:00
|
|
|
if fs.isFiltered(linkName) {
|
2015-11-03 22:25:29 +01:00
|
|
|
return fuse.EPERM
|
2015-11-03 00:00:13 +01:00
|
|
|
}
|
fusefrontend: use OpenDirNofollow in openBackingDir
Rename openBackingPath to openBackingDir and use OpenDirNofollow
to be safe against symlink races. Note that openBackingDir is
not used in several important code paths like Create().
But it is used in Unlink, and the performance impact in the RM benchmark
to be acceptable:
Before
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.bYO: gocryptfs v1.6-12-g930c37e-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.07979 s, 243 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.882413 s, 297 MB/s
UNTAR: 16.703
MD5: 7.606
LS: 1.349
RM: 3.237
After
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.jK3: gocryptfs v1.6-13-g84d6faf-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.06261 s, 247 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.947228 s, 277 MB/s
UNTAR: 17.197
MD5: 7.540
LS: 1.364
RM: 3.410
2018-09-08 19:27:33 +02:00
|
|
|
dirfd, cName, err := fs.openBackingDir(linkName)
|
2017-11-28 01:02:11 +01:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 20:10:34 +02:00
|
|
|
defer syscall.Close(dirfd)
|
2019-01-12 21:22:52 +01:00
|
|
|
// Make sure context is nil if we don't want to preserve the owner
|
|
|
|
if !fs.args.PreserveOwner {
|
|
|
|
context = nil
|
|
|
|
}
|
2018-06-19 20:13:56 +02:00
|
|
|
cTarget := target
|
2017-11-28 00:38:17 +01:00
|
|
|
if !fs.args.PlaintextNames {
|
|
|
|
// Symlinks are encrypted like file contents (GCM) and base64-encoded
|
2018-03-24 21:36:45 +01:00
|
|
|
cTarget = fs.encryptSymlinkTarget(target)
|
2015-11-28 16:52:57 +01:00
|
|
|
}
|
2017-11-28 01:02:11 +01:00
|
|
|
// Create ".name" file to store long file name (except in PlaintextNames mode)
|
2017-11-28 00:38:17 +01:00
|
|
|
if !fs.args.PlaintextNames && nametransform.IsLongContent(cName) {
|
2018-11-04 22:05:38 +01:00
|
|
|
err = fs.nameTransform.WriteLongNameAt(dirfd, cName, linkName)
|
2016-04-10 19:32:10 +02:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
2016-11-28 23:15:24 +01:00
|
|
|
// Create "gocryptfs.longfile." symlink
|
2019-01-12 21:22:52 +01:00
|
|
|
err = syscallcompat.SymlinkatUser(cTarget, dirfd, cName, context)
|
2016-04-10 19:32:10 +02:00
|
|
|
if err != nil {
|
2018-11-04 22:05:38 +01:00
|
|
|
nametransform.DeleteLongNameAt(dirfd, cName)
|
2016-04-10 19:32:10 +02:00
|
|
|
}
|
2016-11-28 23:15:24 +01:00
|
|
|
} else {
|
|
|
|
// Create symlink
|
2019-01-12 21:22:52 +01:00
|
|
|
err = syscallcompat.SymlinkatUser(cTarget, dirfd, cName, context)
|
2016-11-28 23:15:24 +01:00
|
|
|
}
|
2019-01-12 21:22:52 +01:00
|
|
|
return fuse.ToStatus(err)
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2018-11-04 22:05:38 +01:00
|
|
|
// Rename - FUSE call.
|
2018-11-04 22:25:32 +01:00
|
|
|
//
|
|
|
|
// Symlink-safe through Renameat().
|
2015-11-03 22:25:29 +01:00
|
|
|
func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
|
2019-01-02 22:32:21 +01:00
|
|
|
defer fs.dirCache.Clear()
|
2015-12-08 16:13:29 +01:00
|
|
|
if fs.isFiltered(newPath) {
|
2015-11-03 22:25:29 +01:00
|
|
|
return fuse.EPERM
|
2015-11-03 00:00:13 +01:00
|
|
|
}
|
2018-09-22 20:56:01 +02:00
|
|
|
oldDirfd, oldCName, err := fs.openBackingDir(oldPath)
|
2015-11-27 00:03:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 20:56:01 +02:00
|
|
|
defer syscall.Close(oldDirfd)
|
|
|
|
newDirfd, newCName, err := fs.openBackingDir(newPath)
|
2015-11-27 00:03:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 20:56:01 +02:00
|
|
|
defer syscall.Close(newDirfd)
|
2018-01-17 00:23:09 +01:00
|
|
|
// Easy case.
|
|
|
|
if fs.args.PlaintextNames {
|
2018-09-22 20:56:01 +02:00
|
|
|
return fuse.ToStatus(syscallcompat.Renameat(oldDirfd, oldCName, newDirfd, newCName))
|
|
|
|
}
|
|
|
|
// Long destination file name: create .name file
|
|
|
|
nameFileAlreadyThere := false
|
|
|
|
if nametransform.IsLongContent(newCName) {
|
2018-11-04 22:05:38 +01:00
|
|
|
err = fs.nameTransform.WriteLongNameAt(newDirfd, newCName, newPath)
|
2017-11-25 01:56:56 +01:00
|
|
|
// Failure to write the .name file is expected when the target path already
|
|
|
|
// exists. Since hashes are pretty unique, there is no need to modify the
|
2018-09-22 20:56:01 +02:00
|
|
|
// .name file in this case, and we ignore the error.
|
2017-11-25 01:56:56 +01:00
|
|
|
if err == syscall.EEXIST {
|
2018-09-22 20:56:01 +02:00
|
|
|
nameFileAlreadyThere = true
|
2017-11-25 01:56:56 +01:00
|
|
|
} else if err != nil {
|
2016-02-07 14:02:09 +01:00
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Actual rename
|
2018-09-22 20:56:01 +02:00
|
|
|
tlog.Debug.Printf("Renameat %d/%s -> %d/%s\n", oldDirfd, oldCName, newDirfd, newCName)
|
|
|
|
err = syscallcompat.Renameat(oldDirfd, oldCName, newDirfd, newCName)
|
2016-06-30 23:12:22 +02:00
|
|
|
if err == syscall.ENOTEMPTY || err == syscall.EEXIST {
|
|
|
|
// If an empty directory is overwritten we will always get an error as
|
2016-04-10 19:32:10 +02:00
|
|
|
// the "empty" directory will still contain gocryptfs.diriv.
|
2016-06-30 23:12:22 +02:00
|
|
|
// Interestingly, ext4 returns ENOTEMPTY while xfs returns EEXIST.
|
|
|
|
// We handle that by trying to fs.Rmdir() the target directory and trying
|
|
|
|
// again.
|
2016-06-15 23:30:44 +02:00
|
|
|
tlog.Debug.Printf("Rename: Handling ENOTEMPTY")
|
2015-12-19 13:21:15 +01:00
|
|
|
if fs.Rmdir(newPath, context) == fuse.OK {
|
2018-09-22 20:56:01 +02:00
|
|
|
err = syscallcompat.Renameat(oldDirfd, oldCName, newDirfd, newCName)
|
2015-12-19 13:21:15 +01:00
|
|
|
}
|
|
|
|
}
|
2016-04-10 19:32:10 +02:00
|
|
|
if err != nil {
|
2018-09-22 20:56:01 +02:00
|
|
|
if nametransform.IsLongContent(newCName) && nameFileAlreadyThere == false {
|
|
|
|
// Roll back .name creation unless the .name file was already there
|
2018-11-04 22:05:38 +01:00
|
|
|
nametransform.DeleteLongNameAt(newDirfd, newCName)
|
2016-04-10 19:32:10 +02:00
|
|
|
}
|
|
|
|
return fuse.ToStatus(err)
|
2016-02-07 14:02:09 +01:00
|
|
|
}
|
2018-09-22 20:56:01 +02:00
|
|
|
if nametransform.IsLongContent(oldCName) {
|
2018-11-04 22:05:38 +01:00
|
|
|
nametransform.DeleteLongNameAt(oldDirfd, oldCName)
|
2016-04-10 19:32:10 +02:00
|
|
|
}
|
|
|
|
return fuse.OK
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2018-10-01 21:39:19 +02:00
|
|
|
// Link - FUSE call. Creates a hard link at "newPath" pointing to file
|
|
|
|
// "oldPath".
|
|
|
|
//
|
|
|
|
// Symlink-safe through use of Linkat().
|
2015-11-27 00:03:10 +01:00
|
|
|
func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
|
2015-12-08 16:13:29 +01:00
|
|
|
if fs.isFiltered(newPath) {
|
2015-11-03 22:25:29 +01:00
|
|
|
return fuse.EPERM
|
2015-11-03 00:00:13 +01:00
|
|
|
}
|
fusefrontend: use OpenDirNofollow in openBackingDir
Rename openBackingPath to openBackingDir and use OpenDirNofollow
to be safe against symlink races. Note that openBackingDir is
not used in several important code paths like Create().
But it is used in Unlink, and the performance impact in the RM benchmark
to be acceptable:
Before
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.bYO: gocryptfs v1.6-12-g930c37e-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.07979 s, 243 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.882413 s, 297 MB/s
UNTAR: 16.703
MD5: 7.606
LS: 1.349
RM: 3.237
After
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.jK3: gocryptfs v1.6-13-g84d6faf-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.06261 s, 247 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.947228 s, 277 MB/s
UNTAR: 17.197
MD5: 7.540
LS: 1.364
RM: 3.410
2018-09-08 19:27:33 +02:00
|
|
|
oldDirFd, cOldName, err := fs.openBackingDir(oldPath)
|
2015-11-27 00:03:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 20:10:34 +02:00
|
|
|
defer syscall.Close(oldDirFd)
|
fusefrontend: use OpenDirNofollow in openBackingDir
Rename openBackingPath to openBackingDir and use OpenDirNofollow
to be safe against symlink races. Note that openBackingDir is
not used in several important code paths like Create().
But it is used in Unlink, and the performance impact in the RM benchmark
to be acceptable:
Before
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.bYO: gocryptfs v1.6-12-g930c37e-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.07979 s, 243 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.882413 s, 297 MB/s
UNTAR: 16.703
MD5: 7.606
LS: 1.349
RM: 3.237
After
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.jK3: gocryptfs v1.6-13-g84d6faf-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.06261 s, 247 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.947228 s, 277 MB/s
UNTAR: 17.197
MD5: 7.540
LS: 1.364
RM: 3.410
2018-09-08 19:27:33 +02:00
|
|
|
newDirFd, cNewName, err := fs.openBackingDir(newPath)
|
2015-11-27 00:03:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
2018-09-22 20:10:34 +02:00
|
|
|
defer syscall.Close(newDirFd)
|
2017-12-12 14:42:49 +01:00
|
|
|
// Handle long file name (except in PlaintextNames mode)
|
2017-12-12 14:38:00 +01:00
|
|
|
if !fs.args.PlaintextNames && nametransform.IsLongContent(cNewName) {
|
2018-11-04 22:05:38 +01:00
|
|
|
err = fs.nameTransform.WriteLongNameAt(newDirFd, cNewName, newPath)
|
2016-02-07 14:02:09 +01:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
2017-12-12 14:42:49 +01:00
|
|
|
// Create "gocryptfs.longfile." link
|
2018-09-22 20:10:34 +02:00
|
|
|
err = syscallcompat.Linkat(oldDirFd, cOldName, newDirFd, cNewName, 0)
|
2016-04-10 19:32:10 +02:00
|
|
|
if err != nil {
|
2018-11-04 22:05:38 +01:00
|
|
|
nametransform.DeleteLongNameAt(newDirFd, cNewName)
|
2016-04-10 19:32:10 +02:00
|
|
|
}
|
2017-12-12 14:42:49 +01:00
|
|
|
} else {
|
|
|
|
// Create regular link
|
2018-09-22 20:10:34 +02:00
|
|
|
err = syscallcompat.Linkat(oldDirFd, cOldName, newDirFd, cNewName, 0)
|
2016-02-07 14:02:09 +01:00
|
|
|
}
|
2017-12-12 14:42:49 +01:00
|
|
|
return fuse.ToStatus(err)
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
|
|
|
|
2018-09-30 19:33:52 +02:00
|
|
|
// Access - FUSE call. Check if a file can be accessed in the specified mode(s)
|
|
|
|
// (read, write, execute).
|
|
|
|
//
|
2019-01-20 13:34:46 +01:00
|
|
|
// From https://github.com/libfuse/libfuse/blob/master/include/fuse.h :
|
|
|
|
//
|
|
|
|
// > Check file access permissions
|
|
|
|
// >
|
|
|
|
// > If the 'default_permissions' mount option is given, this method is not
|
|
|
|
// > called.
|
|
|
|
//
|
|
|
|
// We always enable default_permissions when -allow_other is passed, so there
|
|
|
|
// is no need for this function to check the uid in fuse.Context.
|
|
|
|
//
|
2018-09-30 19:33:52 +02:00
|
|
|
// Symlink-safe through use of faccessat.
|
|
|
|
func (fs *FS) Access(relPath string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
|
|
|
if fs.isFiltered(relPath) {
|
2015-11-03 22:25:29 +01:00
|
|
|
return fuse.EPERM
|
|
|
|
}
|
2018-09-30 19:33:52 +02:00
|
|
|
dirfd, cName, err := fs.openBackingDir(relPath)
|
2015-11-27 00:03:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return fuse.ToStatus(err)
|
|
|
|
}
|
2019-01-20 13:10:59 +01:00
|
|
|
err = syscallcompat.Faccessat(dirfd, cName, mode)
|
2019-01-01 20:10:17 +01:00
|
|
|
syscall.Close(dirfd)
|
2018-09-30 19:33:52 +02:00
|
|
|
return fuse.ToStatus(err)
|
2015-09-08 00:54:24 +02:00
|
|
|
}
|
2018-04-03 21:19:44 +02:00
|
|
|
|
2018-07-01 15:48:53 +02:00
|
|
|
// reportMitigatedCorruption is used to report a corruption that was transparently
|
|
|
|
// mitigated and did not return an error to the user. Pass the name of the corrupt
|
|
|
|
// item (filename for OpenDir(), xattr name for ListXAttr() etc).
|
|
|
|
// See the MitigatedCorruptions channel for more info.
|
|
|
|
func (fs *FS) reportMitigatedCorruption(item string) {
|
|
|
|
if fs.MitigatedCorruptions == nil {
|
2018-04-03 21:19:44 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
select {
|
2018-07-01 15:48:53 +02:00
|
|
|
case fs.MitigatedCorruptions <- item:
|
2018-04-03 21:19:44 +02:00
|
|
|
case <-time.After(1 * time.Second):
|
|
|
|
tlog.Warn.Printf("BUG: reportCorruptItem: timeout")
|
|
|
|
//debug.PrintStack()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2019-01-02 21:52:52 +01:00
|
|
|
|
|
|
|
// isFiltered - check if plaintext "path" should be forbidden
|
|
|
|
//
|
|
|
|
// Prevents name clashes with internal files when file names are not encrypted
|
|
|
|
func (fs *FS) isFiltered(path string) bool {
|
2019-09-08 16:29:20 +02:00
|
|
|
atomic.StoreUint32(&fs.IsIdle, 0)
|
|
|
|
|
2019-01-02 21:52:52 +01:00
|
|
|
if !fs.args.PlaintextNames {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
// gocryptfs.conf in the root directory is forbidden
|
|
|
|
if path == configfile.ConfDefaultName {
|
|
|
|
tlog.Info.Printf("The name /%s is reserved when -plaintextnames is used\n",
|
|
|
|
configfile.ConfDefaultName)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
// Note: gocryptfs.diriv is NOT forbidden because diriv and plaintextnames
|
|
|
|
// are exclusive
|
|
|
|
return false
|
|
|
|
}
|