fusefrontend: implement recursive diriv caching
The new contrib/maxlen.bash showed that we have exponential
runtime with respect to directory depth.
The new recursive diriv caching is a lot smarter as it caches
intermediate lookups. maxlen.bash now completes in a few seconds.
xfstests results same as
2d158e4c82/screenlog.0
:
Failures: generic/035 generic/062 generic/080 generic/093 generic/099 generic/215 generic/285 generic/319 generic/426 generic/444 generic/467 generic/477 generic/523
Failed 13 of 580 tests
benchmark.bash results are identical:
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.BdQ: gocryptfs v2.0.1-17-g6b09bc0; go-fuse v2.1.1-0.20210611132105-24a1dfe6b4f8; 2021-06-25 go1.16.5 linux/amd64
/tmp/benchmark.bash.BdQ.mnt is a mountpoint
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 0,4821 s, 544 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0,266061 s, 985 MB/s
UNTAR: 8,280
MD5: 4,564
LS: 1,745
RM: 2,244
This commit is contained in:
parent
05b813f202
commit
84e702126a
@ -7,7 +7,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
|
||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -50,6 +49,9 @@ func (e *dirCacheEntry) Clear() {
|
|||||||
|
|
||||||
type dirCache struct {
|
type dirCache struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
|
// Expected length of the stored IVs. Only used for sanity checks.
|
||||||
|
// Usually set to 16, but 0 in plaintextnames mode.
|
||||||
|
ivLen int
|
||||||
// Cache entries
|
// Cache entries
|
||||||
entries [dirCacheSize]dirCacheEntry
|
entries [dirCacheSize]dirCacheEntry
|
||||||
// Where to store the next entry (index into entries)
|
// Where to store the next entry (index into entries)
|
||||||
@ -77,7 +79,7 @@ func (d *dirCache) Clear() {
|
|||||||
func (d *dirCache) Store(node *Node, fd int, iv []byte) {
|
func (d *dirCache) Store(node *Node, fd int, iv []byte) {
|
||||||
// Note: package ensurefds012, imported from main, guarantees that dirCache
|
// Note: package ensurefds012, imported from main, guarantees that dirCache
|
||||||
// can never get fds 0,1,2.
|
// can never get fds 0,1,2.
|
||||||
if fd <= 0 || len(iv) != nametransform.DirIVLen {
|
if fd <= 0 || len(iv) != d.ivLen {
|
||||||
log.Panicf("Store sanity check failed: fd=%d len=%d", fd, len(iv))
|
log.Panicf("Store sanity check failed: fd=%d len=%d", fd, len(iv))
|
||||||
}
|
}
|
||||||
d.Lock()
|
d.Lock()
|
||||||
@ -139,7 +141,7 @@ func (d *dirCache) Lookup(node *Node) (fd int, iv []byte) {
|
|||||||
if enableStats {
|
if enableStats {
|
||||||
d.hits++
|
d.hits++
|
||||||
}
|
}
|
||||||
if fd <= 0 || len(iv) != nametransform.DirIVLen {
|
if fd <= 0 || len(iv) != d.ivLen {
|
||||||
log.Panicf("Lookup sanity check failed: fd=%d len=%d", fd, len(iv))
|
log.Panicf("Lookup sanity check failed: fd=%d len=%d", fd, len(iv))
|
||||||
}
|
}
|
||||||
d.dbg("dirCache.Lookup %p hit fd=%d dup=%d iv=%x\n", node, e.fd, fd, iv)
|
d.dbg("dirCache.Lookup %p hit fd=%d dup=%d iv=%x\n", node, e.fd, fd, iv)
|
||||||
|
@ -52,7 +52,7 @@ func (n *Node) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut)
|
|||||||
return f.(fs.FileGetattrer).Getattr(ctx, out)
|
return f.(fs.FileGetattrer).Getattr(ctx, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
dirfd, cName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -106,7 +106,7 @@ func (n *Node) Unlink(ctx context.Context, name string) (errno syscall.Errno) {
|
|||||||
//
|
//
|
||||||
// Symlink-safe through openBackingDir() + Readlinkat().
|
// Symlink-safe through openBackingDir() + Readlinkat().
|
||||||
func (n *Node) Readlink(ctx context.Context) (out []byte, errno syscall.Errno) {
|
func (n *Node) Readlink(ctx context.Context) (out []byte, errno syscall.Errno) {
|
||||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
dirfd, cName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ func (n *Node) Setattr(ctx context.Context, f fs.FileHandle, in *fuse.SetAttrIn,
|
|||||||
return f2.Setattr(ctx, in, out)
|
return f2.Setattr(ctx, in, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
dirfd, cName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -271,7 +271,7 @@ func (n *Node) Link(ctx context.Context, target fs.InodeEmbedder, name string, o
|
|||||||
defer syscall.Close(dirfd)
|
defer syscall.Close(dirfd)
|
||||||
|
|
||||||
n2 := toNode(target)
|
n2 := toNode(target)
|
||||||
dirfd2, cName2, errno := n2.prepareAtSyscall("")
|
dirfd2, cName2, errno := n2.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ func (n *Node) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.En
|
|||||||
// This function is symlink-safe through use of openBackingDir() and
|
// This function is symlink-safe through use of openBackingDir() and
|
||||||
// ReadDirIVAt().
|
// ReadDirIVAt().
|
||||||
func (n *Node) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
|
func (n *Node) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
|
||||||
parentDirFd, cDirName, errno := n.prepareAtSyscall("")
|
parentDirFd, cDirName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return nil, errno
|
return nil, errno
|
||||||
}
|
}
|
||||||
@ -360,7 +360,7 @@ retry:
|
|||||||
|
|
||||||
// Opendir is a FUSE call to check if the directory can be opened.
|
// Opendir is a FUSE call to check if the directory can be opened.
|
||||||
func (n *Node) Opendir(ctx context.Context) (errno syscall.Errno) {
|
func (n *Node) Opendir(ctx context.Context) (errno syscall.Errno) {
|
||||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
dirfd, cName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,12 @@ package fusefrontend
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
|
||||||
"path/filepath"
|
|
||||||
"sync/atomic"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/hanwen/go-fuse/v2/fs"
|
"github.com/hanwen/go-fuse/v2/fs"
|
||||||
|
|
||||||
"github.com/hanwen/go-fuse/v2/fuse"
|
"github.com/hanwen/go-fuse/v2/fuse"
|
||||||
|
|
||||||
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
|
||||||
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
@ -80,91 +76,6 @@ func (n *Node) rootNode() *RootNode {
|
|||||||
return n.Root().Operations().(*RootNode)
|
return n.Root().Operations().(*RootNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareAtSyscall returns a (dirfd, cName) pair that can be used
|
|
||||||
// with the "___at" family of system calls (openat, fstatat, unlinkat...) to
|
|
||||||
// access the backing encrypted directory.
|
|
||||||
//
|
|
||||||
// If you pass a `child` file name, the (dirfd, cName) pair will refer to
|
|
||||||
// a child of this node.
|
|
||||||
// If `child` is empty, the (dirfd, cName) pair refers to this node itself. For
|
|
||||||
// the root node, that means (dirfd, ".").
|
|
||||||
func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno syscall.Errno) {
|
|
||||||
rn := n.rootNode()
|
|
||||||
// all filesystem operations go through prepareAtSyscall(), so this is a
|
|
||||||
// good place to reset the idle marker.
|
|
||||||
atomic.StoreUint32(&rn.IsIdle, 0)
|
|
||||||
|
|
||||||
// root node itself is special
|
|
||||||
if child == "" && n.IsRoot() {
|
|
||||||
var err error
|
|
||||||
dirfd, cName, err = rn.openBackingDir("")
|
|
||||||
if err != nil {
|
|
||||||
errno = fs.ToErrno(err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// normal node itself can be converted to child of parent node
|
|
||||||
if child == "" {
|
|
||||||
name, p1 := n.Parent()
|
|
||||||
if p1 == nil || name == "" {
|
|
||||||
return -1, "", syscall.ENOENT
|
|
||||||
}
|
|
||||||
p2 := toNode(p1.Operations())
|
|
||||||
return p2.prepareAtSyscall(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache lookup
|
|
||||||
// TODO make it work for plaintextnames as well?
|
|
||||||
cacheable := (!rn.args.PlaintextNames)
|
|
||||||
if cacheable {
|
|
||||||
var iv []byte
|
|
||||||
dirfd, iv = rn.dirCache.Lookup(n)
|
|
||||||
if dirfd > 0 {
|
|
||||||
var cName string
|
|
||||||
var err error
|
|
||||||
if rn.nameTransform.HaveBadnamePatterns() {
|
|
||||||
//BadName allowed, try to determine filenames
|
|
||||||
cName, err = rn.nameTransform.EncryptAndHashBadName(child, iv, dirfd)
|
|
||||||
} else {
|
|
||||||
cName, err = rn.nameTransform.EncryptAndHashName(child, iv)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return -1, "", fs.ToErrno(err)
|
|
||||||
}
|
|
||||||
return dirfd, cName, 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slowpath
|
|
||||||
if child == "" {
|
|
||||||
log.Panicf("BUG: child name is empty - this cannot happen")
|
|
||||||
}
|
|
||||||
p := filepath.Join(n.Path(), child)
|
|
||||||
if rn.isFiltered(p) {
|
|
||||||
errno = syscall.EPERM
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dirfd, cName, err := rn.openBackingDir(p)
|
|
||||||
if err != nil {
|
|
||||||
errno = fs.ToErrno(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache store
|
|
||||||
if cacheable {
|
|
||||||
// TODO: openBackingDir already calls ReadDirIVAt(). Avoid duplicate work?
|
|
||||||
iv, err := nametransform.ReadDirIVAt(dirfd)
|
|
||||||
if err != nil {
|
|
||||||
syscall.Close(dirfd)
|
|
||||||
return -1, "", fs.ToErrno(err)
|
|
||||||
}
|
|
||||||
rn.dirCache.Store(n, dirfd, iv)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// newChild attaches a new child inode to n.
|
// newChild attaches a new child inode to n.
|
||||||
// The passed-in `st` will be modified to get a unique inode number
|
// The passed-in `st` will be modified to get a unique inode number
|
||||||
// (or, in `-sharedstorage` mode, the inode number will be set to zero).
|
// (or, in `-sharedstorage` mode, the inode number will be set to zero).
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
//
|
//
|
||||||
// Symlink-safe through Openat().
|
// Symlink-safe through Openat().
|
||||||
func (n *Node) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
|
func (n *Node) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
|
||||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
dirfd, cName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
116
internal/fusefrontend/node_prepare_syscall.go
Normal file
116
internal/fusefrontend/node_prepare_syscall.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package fusefrontend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
|
|
||||||
|
"github.com/hanwen/go-fuse/v2/fs"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
||||||
|
)
|
||||||
|
|
||||||
|
// prepareAtSyscall returns a (dirfd, cName) pair that can be used
|
||||||
|
// with the "___at" family of system calls (openat, fstatat, unlinkat...) to
|
||||||
|
// access the backing encrypted child file.
|
||||||
|
func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno syscall.Errno) {
|
||||||
|
if child == "" {
|
||||||
|
tlog.Warn.Printf("BUG: prepareAtSyscall: child=%q, should have called prepareAtSyscallMyself", child)
|
||||||
|
return n.prepareAtSyscallMyself()
|
||||||
|
}
|
||||||
|
|
||||||
|
rn := n.rootNode()
|
||||||
|
|
||||||
|
// All filesystem operations go through here, so this is a good place
|
||||||
|
// to reset the idle marker.
|
||||||
|
atomic.StoreUint32(&rn.IsIdle, 0)
|
||||||
|
|
||||||
|
if n.IsRoot() && rn.isFiltered(child) {
|
||||||
|
return -1, "", syscall.EPERM
|
||||||
|
}
|
||||||
|
|
||||||
|
var encryptName func(int, string, []byte) (string, error)
|
||||||
|
if !rn.args.PlaintextNames {
|
||||||
|
encryptName = func(dirfd int, child string, iv []byte) (cName string, err error) {
|
||||||
|
// Badname allowed, try to determine filenames
|
||||||
|
if rn.nameTransform.HaveBadnamePatterns() {
|
||||||
|
return rn.nameTransform.EncryptAndHashBadName(child, iv, dirfd)
|
||||||
|
}
|
||||||
|
return rn.nameTransform.EncryptAndHashName(child, iv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache lookup
|
||||||
|
var iv []byte
|
||||||
|
dirfd, iv = rn.dirCache.Lookup(n)
|
||||||
|
if dirfd > 0 {
|
||||||
|
if rn.args.PlaintextNames {
|
||||||
|
return dirfd, child, 0
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
cName, err = encryptName(dirfd, child, iv)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(dirfd)
|
||||||
|
return -1, "", fs.ToErrno(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slowpath: Open ourselves & read diriv
|
||||||
|
parentDirfd, myCName, errno := n.prepareAtSyscallMyself()
|
||||||
|
if errno != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer syscall.Close(parentDirfd)
|
||||||
|
|
||||||
|
dirfd, err := syscallcompat.Openat(parentDirfd, myCName, syscall.O_NOFOLLOW|syscall.O_DIRECTORY|syscallcompat.O_PATH, 0)
|
||||||
|
|
||||||
|
// Cache store
|
||||||
|
if !rn.args.PlaintextNames {
|
||||||
|
var err error
|
||||||
|
iv, err = nametransform.ReadDirIVAt(dirfd)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(dirfd)
|
||||||
|
return -1, "", fs.ToErrno(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rn.dirCache.Store(n, dirfd, iv)
|
||||||
|
|
||||||
|
if rn.args.PlaintextNames {
|
||||||
|
return dirfd, child, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
cName, err = encryptName(dirfd, child, iv)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(dirfd)
|
||||||
|
return -1, "", fs.ToErrno(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) prepareAtSyscallMyself() (dirfd int, cName string, errno syscall.Errno) {
|
||||||
|
dirfd = -1
|
||||||
|
|
||||||
|
// Handle root node
|
||||||
|
if n.IsRoot() {
|
||||||
|
var err error
|
||||||
|
rn := n.rootNode()
|
||||||
|
dirfd, cName, err = rn.openBackingDir("")
|
||||||
|
if err != nil {
|
||||||
|
errno = fs.ToErrno(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise convert to prepareAtSyscall of parent node
|
||||||
|
myName, p1 := n.Parent()
|
||||||
|
if p1 == nil || myName == "" {
|
||||||
|
errno = syscall.ENOENT
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parent := toNode(p1.Operations())
|
||||||
|
return parent.prepareAtSyscall(myName)
|
||||||
|
}
|
@ -20,7 +20,7 @@ func filterXattrSetFlags(flags int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) getXAttr(cAttr string) (out []byte, errno syscall.Errno) {
|
func (n *Node) getXAttr(cAttr string) (out []byte, errno syscall.Errno) {
|
||||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
dirfd, cName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ func (n *Node) getXAttr(cAttr string) (out []byte, errno syscall.Errno) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) setXAttr(context *fuse.Context, cAttr string, cData []byte, flags uint32) (errno syscall.Errno) {
|
func (n *Node) setXAttr(context *fuse.Context, cAttr string, cData []byte, flags uint32) (errno syscall.Errno) {
|
||||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
dirfd, cName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -64,7 +64,7 @@ func (n *Node) setXAttr(context *fuse.Context, cAttr string, cData []byte, flags
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) removeXAttr(cAttr string) (errno syscall.Errno) {
|
func (n *Node) removeXAttr(cAttr string) (errno syscall.Errno) {
|
||||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
dirfd, cName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ func (n *Node) removeXAttr(cAttr string) (errno syscall.Errno) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) listXAttr() (out []string, errno syscall.Errno) {
|
func (n *Node) listXAttr() (out []string, errno syscall.Errno) {
|
||||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
dirfd, cName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ func filterXattrSetFlags(flags int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) getXAttr(cAttr string) (out []byte, errno syscall.Errno) {
|
func (n *Node) getXAttr(cAttr string) (out []byte, errno syscall.Errno) {
|
||||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
dirfd, cName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ func (n *Node) getXAttr(cAttr string) (out []byte, errno syscall.Errno) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) setXAttr(context *fuse.Context, cAttr string, cData []byte, flags uint32) (errno syscall.Errno) {
|
func (n *Node) setXAttr(context *fuse.Context, cAttr string, cData []byte, flags uint32) (errno syscall.Errno) {
|
||||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
dirfd, cName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ func (n *Node) setXAttr(context *fuse.Context, cAttr string, cData []byte, flags
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) removeXAttr(cAttr string) (errno syscall.Errno) {
|
func (n *Node) removeXAttr(cAttr string) (errno syscall.Errno) {
|
||||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
dirfd, cName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ func (n *Node) removeXAttr(cAttr string) (errno syscall.Errno) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) listXAttr() (out []string, errno syscall.Errno) {
|
func (n *Node) listXAttr() (out []string, errno syscall.Errno) {
|
||||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
dirfd, cName, errno := n.prepareAtSyscallMyself()
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -61,11 +61,16 @@ func NewRootNode(args Args, c *contentenc.ContentEnc, n *nametransform.NameTrans
|
|||||||
if len(args.Exclude) > 0 {
|
if len(args.Exclude) > 0 {
|
||||||
tlog.Warn.Printf("Forward mode does not support -exclude")
|
tlog.Warn.Printf("Forward mode does not support -exclude")
|
||||||
}
|
}
|
||||||
|
ivLen := nametransform.DirIVLen
|
||||||
|
if args.PlaintextNames {
|
||||||
|
ivLen = 0
|
||||||
|
}
|
||||||
rn := &RootNode{
|
rn := &RootNode{
|
||||||
args: args,
|
args: args,
|
||||||
nameTransform: n,
|
nameTransform: n,
|
||||||
contentEnc: c,
|
contentEnc: c,
|
||||||
inoMap: inomap.New(),
|
inoMap: inomap.New(),
|
||||||
|
dirCache: dirCache{ivLen: ivLen},
|
||||||
}
|
}
|
||||||
// In `-sharedstorage` mode we always set the inode number to zero.
|
// In `-sharedstorage` mode we always set the inode number to zero.
|
||||||
// This makes go-fuse generate a new inode number for each lookup.
|
// This makes go-fuse generate a new inode number for each lookup.
|
||||||
@ -122,15 +127,15 @@ func (rn *RootNode) reportMitigatedCorruption(item string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// isFiltered - check if plaintext "path" should be forbidden
|
// isFiltered - check if plaintext file "child" should be forbidden
|
||||||
//
|
//
|
||||||
// Prevents name clashes with internal files when file names are not encrypted
|
// Prevents name clashes with internal files when file names are not encrypted
|
||||||
func (rn *RootNode) isFiltered(path string) bool {
|
func (rn *RootNode) isFiltered(child string) bool {
|
||||||
if !rn.args.PlaintextNames {
|
if !rn.args.PlaintextNames {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// gocryptfs.conf in the root directory is forbidden
|
// gocryptfs.conf in the root directory is forbidden
|
||||||
if path == configfile.ConfDefaultName {
|
if child == configfile.ConfDefaultName {
|
||||||
tlog.Info.Printf("The name /%s is reserved when -plaintextnames is used\n",
|
tlog.Info.Printf("The name /%s is reserved when -plaintextnames is used\n",
|
||||||
configfile.ConfDefaultName)
|
configfile.ConfDefaultName)
|
||||||
return true
|
return true
|
||||||
|
Loading…
Reference in New Issue
Block a user