v2api: -sharestorage: disable hard link tracking & add tests
Hard link tracking was not correctly disabled since the migration to the go-fuse v2 api. Add a test to ensure it stays off. Fixes https://github.com/rfjakob/gocryptfs/issues/525
This commit is contained in:
parent
eaca820e87
commit
e2dc52a965
@ -46,4 +46,7 @@ type Args struct {
|
||||
Suid bool
|
||||
// Enable the FUSE kernel_cache option
|
||||
KernelCache bool
|
||||
// SharedStorage disables caching & hard link tracking,
|
||||
// enabled via cli flag "-sharedstorage"
|
||||
SharedStorage bool
|
||||
}
|
||||
|
@ -102,8 +102,9 @@ func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno sy
|
||||
// newChild attaches a new child inode to n.
|
||||
// The passed-in `st` will be modified to get a unique inode number.
|
||||
func (n *Node) newChild(ctx context.Context, st *syscall.Stat_t, out *fuse.EntryOut) *fs.Inode {
|
||||
// Get unique inode number
|
||||
rn := n.rootNode()
|
||||
// Get stable inode number based on underlying (device,ino) pair
|
||||
// (or set to zero in case of `-sharestorage`)
|
||||
rn.inoMap.TranslateStat(st)
|
||||
out.Attr.FromStat(st)
|
||||
// Create child node
|
||||
|
@ -50,7 +50,7 @@ type RootNode struct {
|
||||
IsIdle uint32
|
||||
// inoMap translates inode numbers from different devices to unique inode
|
||||
// numbers.
|
||||
inoMap *inomap.InoMap
|
||||
inoMap inomap.TranslateStater
|
||||
}
|
||||
|
||||
func NewRootNode(args Args, c *contentenc.ContentEnc, n nametransform.NameTransformer) *RootNode {
|
||||
@ -60,12 +60,18 @@ func NewRootNode(args Args, c *contentenc.ContentEnc, n nametransform.NameTransf
|
||||
if len(args.Exclude) > 0 {
|
||||
tlog.Warn.Printf("Forward mode does not support -exclude")
|
||||
}
|
||||
return &RootNode{
|
||||
rn := &RootNode{
|
||||
args: args,
|
||||
nameTransform: n,
|
||||
contentEnc: c,
|
||||
inoMap: inomap.New(),
|
||||
}
|
||||
// In `-sharedstorage` mode we always set the inode number to zero.
|
||||
// This makes go-fuse generate a new inode number for each lookup.
|
||||
if args.SharedStorage {
|
||||
rn.inoMap = &inomap.TranslateStatZero{}
|
||||
}
|
||||
return rn
|
||||
}
|
||||
|
||||
// mangleOpenFlags is used by Create() and Open() to convert the open flags the user
|
||||
|
@ -104,3 +104,14 @@ func (m *InoMap) TranslateStat(st *syscall.Stat_t) {
|
||||
in := QInoFromStat(st)
|
||||
st.Ino = m.Translate(in)
|
||||
}
|
||||
|
||||
type TranslateStater interface {
|
||||
TranslateStat(st *syscall.Stat_t)
|
||||
}
|
||||
|
||||
// TranslateStatZero always sets st.Ino to zero. Used for `-sharedstorage`.
|
||||
type TranslateStatZero struct{}
|
||||
|
||||
func (z TranslateStatZero) TranslateStat(st *syscall.Stat_t) {
|
||||
st.Ino = 0
|
||||
}
|
||||
|
10
mount.go
10
mount.go
@ -280,6 +280,7 @@ func initFuseFrontend(args *argContainer) (rootNode fs.InodeEmbedder, wipeKeys f
|
||||
ExcludeFrom: args.excludeFrom,
|
||||
Suid: args.suid,
|
||||
KernelCache: args.kernel_cache,
|
||||
SharedStorage: args.sharedstorage,
|
||||
}
|
||||
// confFile is nil when "-zerokey" or "-masterkey" was used
|
||||
if confFile != nil {
|
||||
@ -349,7 +350,11 @@ func initGoFuse(rootNode fs.InodeEmbedder, args *argContainer) *fuse.Server {
|
||||
if args.sharedstorage {
|
||||
// sharedstorage mode sets all cache timeouts to zero so changes to the
|
||||
// backing shared storage show up immediately.
|
||||
fuseOpts = &fs.Options{}
|
||||
// Hard links are disabled by using automatically incrementing
|
||||
// inode numbers provided by go-fuse.
|
||||
fuseOpts = &fs.Options{
|
||||
FirstAutomaticIno: 1000,
|
||||
}
|
||||
} else {
|
||||
fuseOpts = &fs.Options{
|
||||
// These options are to be compatible with libfuse defaults,
|
||||
@ -421,7 +426,8 @@ func initGoFuse(rootNode fs.InodeEmbedder, args *argContainer) *fuse.Server {
|
||||
} else if args.rw {
|
||||
mOpts.Options = append(mOpts.Options, "rw")
|
||||
}
|
||||
// If both "nosuid" and "suid" were passed, the safer option wins.
|
||||
// If both "nosuid" & "suid", "nodev" & "dev", etc were passed, the safer
|
||||
// option wins.
|
||||
if args.nosuid {
|
||||
mOpts.Options = append(mOpts.Options, "nosuid")
|
||||
} else if args.suid {
|
||||
|
@ -822,3 +822,50 @@ func TestInitNotEmpty(t *testing.T) {
|
||||
t.Fatalf("wrong exit code: have=%d, want=%d", exitCode, exitcodes.CipherDir)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSharedstorage checks that `-sharedstorage` hands out arbitrary inode
|
||||
// numbers (no hard link tracking)
|
||||
func TestSharedstorage(t *testing.T) {
|
||||
dir := test_helpers.InitFS(t)
|
||||
mnt := dir + ".mnt"
|
||||
err := os.Mkdir(mnt, 0700)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
test_helpers.MountOrFatal(t, dir, mnt, "-extpass=echo test", "-sharedstorage")
|
||||
defer test_helpers.UnmountPanic(mnt)
|
||||
foo1 := mnt + "/foo1"
|
||||
foo2 := mnt + "/foo2"
|
||||
if err := ioutil.WriteFile(foo1, nil, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Link(foo1, foo2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var st1, st2, st3 syscall.Stat_t
|
||||
if err := syscall.Stat(foo1, &st1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// The link show show a new inode number
|
||||
if err := syscall.Stat(foo2, &st2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Stat()'ing again should give us again a new inode number
|
||||
if err := syscall.Stat(foo2, &st3); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if st1.Ino == st2.Ino || st2.Ino == st3.Ino || st1.Ino == st3.Ino {
|
||||
t.Error(st1.Ino, st2.Ino, st3.Ino)
|
||||
}
|
||||
// Check that we we don't have stat caching. New length should show up
|
||||
// on the hard link immediately.
|
||||
if err := ioutil.WriteFile(foo1, []byte("xxxxxx"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := syscall.Stat(foo2, &st2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if st2.Size != 6 {
|
||||
t.Fatal(st2.Size)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user