v2api: implement GetAttr and Readdir
This commit is contained in:
parent
77632b7554
commit
ebdf58b9eb
@ -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
|
||||
}
|
||||
|
97
internal/fusefrontend/node_dir_ops.go
Normal file
97
internal/fusefrontend/node_dir_ops.go
Normal file
@ -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
|
||||
}
|
24
internal/fusefrontend/root_node.go
Normal file
24
internal/fusefrontend/root_node.go
Normal file
@ -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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user