reverse: use dynamic inode numbers
...with stable mappings for hard-linked files.
This commit is contained in:
parent
5f726aaa9d
commit
d76e7aadb4
|
@ -0,0 +1,24 @@
|
||||||
|
package fusefrontend_reverse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewInoGen() *inoGenT {
|
||||||
|
var ino uint64 = 1
|
||||||
|
return &inoGenT{&ino}
|
||||||
|
}
|
||||||
|
|
||||||
|
type inoGenT struct {
|
||||||
|
ino *uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next inode counter value
|
||||||
|
func (i *inoGenT) next() uint64 {
|
||||||
|
return atomic.AddUint64(i.ino, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
type devIno struct {
|
||||||
|
dev uint64
|
||||||
|
ino uint64
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/hanwen/go-fuse/fuse"
|
"github.com/hanwen/go-fuse/fuse"
|
||||||
|
@ -32,6 +33,12 @@ type reverseFS struct {
|
||||||
nameTransform *nametransform.NameTransform
|
nameTransform *nametransform.NameTransform
|
||||||
// Content encryption helper
|
// Content encryption helper
|
||||||
contentEnc *contentenc.ContentEnc
|
contentEnc *contentenc.ContentEnc
|
||||||
|
// Inode number generator
|
||||||
|
inoGen *inoGenT
|
||||||
|
// Maps backing files device+inode pairs to user-facing unique inode numbers
|
||||||
|
inoMap map[devIno]uint64
|
||||||
|
// Protects map access
|
||||||
|
inoMapLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypted FUSE overlay filesystem
|
// Encrypted FUSE overlay filesystem
|
||||||
|
@ -47,6 +54,8 @@ func NewFS(args fusefrontend.Args) *reverseFS {
|
||||||
args: args,
|
args: args,
|
||||||
nameTransform: nameTransform,
|
nameTransform: nameTransform,
|
||||||
contentEnc: contentEnc,
|
contentEnc: contentEnc,
|
||||||
|
inoGen: NewInoGen(),
|
||||||
|
inoMap: map[devIno]uint64{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,11 +94,11 @@ func (rfs *reverseFS) dirIVAttr(relPath string, context *fuse.Context) (*fuse.At
|
||||||
fmt.Printf("not exec")
|
fmt.Printf("not exec")
|
||||||
return nil, fuse.EPERM
|
return nil, fuse.EPERM
|
||||||
}
|
}
|
||||||
// All good. Let's fake the file.
|
// All good. Let's fake the file. We use the timestamps from the parent dir.
|
||||||
// We use the inode number of the parent dir (can this cause problems?).
|
|
||||||
a.Mode = DirIVMode
|
a.Mode = DirIVMode
|
||||||
a.Size = nametransform.DirIVLen
|
a.Size = nametransform.DirIVLen
|
||||||
a.Nlink = 1
|
a.Nlink = 1
|
||||||
|
a.Ino = rfs.inoGen.next()
|
||||||
|
|
||||||
return a, fuse.OK
|
return a, fuse.OK
|
||||||
}
|
}
|
||||||
|
@ -99,10 +108,45 @@ func isDirIV(relPath string) bool {
|
||||||
return filepath.Base(relPath) == nametransform.DirIVFilename
|
return filepath.Base(relPath) == nametransform.DirIVFilename
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rfs *reverseFS) inoAwareStat(relPlainPath string) (*fuse.Attr, fuse.Status) {
|
||||||
|
absPath, err := rfs.abs(relPlainPath, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fuse.ToStatus(err)
|
||||||
|
}
|
||||||
|
var fi os.FileInfo
|
||||||
|
if relPlainPath == "" {
|
||||||
|
// Look through symlinks for the root dir
|
||||||
|
fi, err = os.Stat(absPath)
|
||||||
|
} else {
|
||||||
|
fi, err = os.Lstat(absPath)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fuse.ToStatus(err)
|
||||||
|
}
|
||||||
|
st := fi.Sys().(*syscall.Stat_t)
|
||||||
|
// The file has hard links. We have to give it a stable inode number so
|
||||||
|
// tar or rsync can find them.
|
||||||
|
if fi.Mode().IsRegular() && st.Nlink > 1 {
|
||||||
|
di := devIno{st.Dev, st.Ino}
|
||||||
|
rfs.inoMapLock.Lock()
|
||||||
|
stableIno := rfs.inoMap[di]
|
||||||
|
if stableIno == 0 {
|
||||||
|
rfs.inoMap[di] = rfs.inoGen.next()
|
||||||
|
}
|
||||||
|
rfs.inoMapLock.Unlock()
|
||||||
|
st.Ino = stableIno
|
||||||
|
} else {
|
||||||
|
st.Ino = rfs.inoGen.next()
|
||||||
|
}
|
||||||
|
a := &fuse.Attr{}
|
||||||
|
a.FromStat(st)
|
||||||
|
return a, fuse.OK
|
||||||
|
}
|
||||||
|
|
||||||
// GetAttr - FUSE call
|
// GetAttr - FUSE call
|
||||||
func (rfs *reverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
func (rfs *reverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
||||||
if relPath == configfile.ConfDefaultName {
|
if relPath == configfile.ConfDefaultName {
|
||||||
return rfs.loopbackfs.GetAttr(configfile.ConfReverseName, context)
|
return rfs.inoAwareStat(configfile.ConfReverseName)
|
||||||
}
|
}
|
||||||
if isDirIV(relPath) {
|
if isDirIV(relPath) {
|
||||||
return rfs.dirIVAttr(relPath, context)
|
return rfs.dirIVAttr(relPath, context)
|
||||||
|
@ -110,11 +154,11 @@ func (rfs *reverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr
|
||||||
if rfs.isFiltered(relPath) {
|
if rfs.isFiltered(relPath) {
|
||||||
return nil, fuse.EPERM
|
return nil, fuse.EPERM
|
||||||
}
|
}
|
||||||
relPath, err := rfs.decryptPath(relPath)
|
cPath, err := rfs.decryptPath(relPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
a, status := rfs.loopbackfs.GetAttr(relPath, context)
|
a, status := rfs.inoAwareStat(cPath)
|
||||||
if !status.Ok() {
|
if !status.Ok() {
|
||||||
return nil, status
|
return nil, status
|
||||||
}
|
}
|
||||||
|
@ -189,3 +233,8 @@ func (rfs *reverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.
|
||||||
|
|
||||||
return entries, fuse.OK
|
return entries, fuse.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatFs - FUSE call
|
||||||
|
func (rfs *reverseFS) StatFs(name string) *fuse.StatfsOut {
|
||||||
|
return rfs.loopbackfs.StatFs(name)
|
||||||
|
}
|
||||||
|
|
3
main.go
3
main.go
|
@ -349,6 +349,9 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi
|
||||||
mOpts.Options = append(mOpts.Options, "fsname="+args.cipherdir)
|
mOpts.Options = append(mOpts.Options, "fsname="+args.cipherdir)
|
||||||
// Second column, "Type", will be shown as "fuse." + Name
|
// Second column, "Type", will be shown as "fuse." + Name
|
||||||
mOpts.Name = "gocryptfs"
|
mOpts.Name = "gocryptfs"
|
||||||
|
if args.reverse {
|
||||||
|
mOpts.Name += "-reverse"
|
||||||
|
}
|
||||||
|
|
||||||
// The kernel enforces read-only operation, we just have to pass "ro".
|
// The kernel enforces read-only operation, we just have to pass "ro".
|
||||||
// Reverse mounts are always read-only
|
// Reverse mounts are always read-only
|
||||||
|
|
Loading…
Reference in New Issue