diff options
| author | Jakob Unterwurzacher | 2020-06-21 12:42:18 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2020-06-21 12:42:18 +0200 | 
| commit | ebdf58b9ebf366ac6dbf500c0f4f4d211451bb44 (patch) | |
| tree | d30c80ae667d844ce0081820b9b07e48940d4cf0 /internal | |
| parent | 77632b7554000f2e832a0e9fded1a3894b2502c3 (diff) | |
v2api: implement GetAttr and Readdir
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/fusefrontend/node.go | 27 | ||||
| -rw-r--r-- | internal/fusefrontend/node_dir_ops.go | 97 | ||||
| -rw-r--r-- | internal/fusefrontend/root_node.go | 24 | 
3 files changed, 144 insertions, 4 deletions
| diff --git a/internal/fusefrontend/node.go b/internal/fusefrontend/node.go index f246408..f4359a2 100644 --- a/internal/fusefrontend/node.go +++ b/internal/fusefrontend/node.go @@ -31,6 +31,15 @@ type RootNode struct {  	nameTransform nametransform.NameTransformer  	// Content encryption helper  	contentEnc *contentenc.ContentEnc +	// 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  	// IsIdle 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 @@ -68,6 +77,7 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs  	if err != nil {  		return nil, fs.ToErrno(err)  	} +	defer syscall.Close(dirfd)  	// Get device number and inode number into `st`  	st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW)  	if err != nil { @@ -88,9 +98,18 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs  }  func (n *Node) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) syscall.Errno { -	return 1 -} +	rn := n.rootNode() +	dirfd, cName, err := rn.openBackingDir(n.path()) +	if err != nil { +		return fs.ToErrno(err) +	} +	defer syscall.Close(dirfd) -func (n *Node) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) { -	return nil, 1 +	st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW) +	if err != nil { +		return fs.ToErrno(err) +	} +	rn.inoMap.TranslateStat(st) +	out.Attr.FromStat(st) +	return 0  } diff --git a/internal/fusefrontend/node_dir_ops.go b/internal/fusefrontend/node_dir_ops.go new file mode 100644 index 0000000..38d0940 --- /dev/null +++ b/internal/fusefrontend/node_dir_ops.go @@ -0,0 +1,97 @@ +package fusefrontend + +import ( +	"context" +	"path/filepath" +	"syscall" + +	"github.com/hanwen/go-fuse/v2/fs" +	"github.com/hanwen/go-fuse/v2/fuse" + +	"github.com/rfjakob/gocryptfs/internal/configfile" +	"github.com/rfjakob/gocryptfs/internal/nametransform" +	"github.com/rfjakob/gocryptfs/internal/syscallcompat" +	"github.com/rfjakob/gocryptfs/internal/tlog" +) + +func (n *Node) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) { +	rn := n.rootNode() +	p := n.path() +	dirName := filepath.Base(p) +	parentDirFd, cDirName, err := rn.openBackingDir(p) +	if err != nil { +		return nil, fs.ToErrno(err) +	} +	defer syscall.Close(parentDirFd) + +	// Read ciphertext directory +	var cipherEntries []fuse.DirEntry +	fd, err := syscallcompat.Openat(parentDirFd, cDirName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) +	if err != nil { +		return nil, fs.ToErrno(err) +	} +	defer syscall.Close(fd) +	cipherEntries, err = syscallcompat.Getdents(fd) +	if err != nil { +		return nil, fs.ToErrno(err) +	} +	// Get DirIV (stays nil if PlaintextNames is used) +	var cachedIV []byte +	if !rn.args.PlaintextNames { +		// Read the DirIV from disk +		cachedIV, err = nametransform.ReadDirIVAt(fd) +		if err != nil { +			tlog.Warn.Printf("OpenDir %q: could not read %s: %v", cDirName, nametransform.DirIVFilename, err) +			return nil, syscall.EIO +		} +	} +	// Decrypted directory entries +	var plain []fuse.DirEntry +	// Filter and decrypt filenames +	for i := range cipherEntries { +		cName := cipherEntries[i].Name +		if dirName == "." && cName == configfile.ConfDefaultName { +			// silently ignore "gocryptfs.conf" in the top level dir +			continue +		} +		if rn.args.PlaintextNames { +			plain = append(plain, cipherEntries[i]) +			continue +		} +		if cName == nametransform.DirIVFilename { +			// silently ignore "gocryptfs.diriv" everywhere if dirIV is enabled +			continue +		} +		// Handle long file name +		isLong := nametransform.LongNameNone +		if rn.args.LongNames { +			isLong = nametransform.NameType(cName) +		} +		if isLong == nametransform.LongNameContent { +			cNameLong, err := nametransform.ReadLongNameAt(fd, cName) +			if err != nil { +				tlog.Warn.Printf("OpenDir %q: invalid entry %q: Could not read .name: %v", +					cDirName, cName, err) +				rn.reportMitigatedCorruption(cName) +				continue +			} +			cName = cNameLong +		} else if isLong == nametransform.LongNameFilename { +			// ignore "gocryptfs.longname.*.name" +			continue +		} +		name, err := rn.nameTransform.DecryptName(cName, cachedIV) +		if err != nil { +			tlog.Warn.Printf("OpenDir %q: invalid entry %q: %v", +				cDirName, cName, err) +			rn.reportMitigatedCorruption(cName) +			continue +		} +		// Override the ciphertext name with the plaintext name but reuse the rest +		// of the structure +		cipherEntries[i].Name = name +		plain = append(plain, cipherEntries[i]) +	} + +	return fs.NewListDirStream(plain), 0 +} diff --git a/internal/fusefrontend/root_node.go b/internal/fusefrontend/root_node.go new file mode 100644 index 0000000..94fd021 --- /dev/null +++ b/internal/fusefrontend/root_node.go @@ -0,0 +1,24 @@ +package fusefrontend + +import ( +	"time" + +	"github.com/rfjakob/gocryptfs/internal/tlog" +) + +// 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 (rn *RootNode) reportMitigatedCorruption(item string) { +	if rn.MitigatedCorruptions == nil { +		return +	} +	select { +	case rn.MitigatedCorruptions <- item: +	case <-time.After(1 * time.Second): +		tlog.Warn.Printf("BUG: reportCorruptItem: timeout") +		//debug.PrintStack() +		return +	} +} | 
