diff options
| -rw-r--r-- | internal/fusefrontend_reverse/ino_map.go | 24 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/rfs.go | 59 | ||||
| -rw-r--r-- | main.go | 3 | 
3 files changed, 81 insertions, 5 deletions
| diff --git a/internal/fusefrontend_reverse/ino_map.go b/internal/fusefrontend_reverse/ino_map.go new file mode 100644 index 0000000..5217732 --- /dev/null +++ b/internal/fusefrontend_reverse/ino_map.go @@ -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 +} diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go index 4b04b86..4f516fd 100644 --- a/internal/fusefrontend_reverse/rfs.go +++ b/internal/fusefrontend_reverse/rfs.go @@ -4,6 +4,7 @@ import (  	"fmt"  	"os"  	"path/filepath" +	"sync"  	"syscall"  	"github.com/hanwen/go-fuse/fuse" @@ -32,6 +33,12 @@ type reverseFS struct {  	nameTransform *nametransform.NameTransform  	// Content encryption helper  	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 @@ -47,6 +54,8 @@ func NewFS(args fusefrontend.Args) *reverseFS {  		args:          args,  		nameTransform: nameTransform,  		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")  		return nil, fuse.EPERM  	} -	// All good. Let's fake the file. -	// We use the inode number of the parent dir (can this cause problems?). +	// All good. Let's fake the file. We use the timestamps from the parent dir.  	a.Mode = DirIVMode  	a.Size = nametransform.DirIVLen  	a.Nlink = 1 +	a.Ino = rfs.inoGen.next()  	return a, fuse.OK  } @@ -99,10 +108,45 @@ func isDirIV(relPath string) bool {  	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  func (rfs *reverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr, fuse.Status) {  	if relPath == configfile.ConfDefaultName { -		return rfs.loopbackfs.GetAttr(configfile.ConfReverseName, context) +		return rfs.inoAwareStat(configfile.ConfReverseName)  	}  	if isDirIV(relPath) {  		return rfs.dirIVAttr(relPath, context) @@ -110,11 +154,11 @@ func (rfs *reverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr  	if rfs.isFiltered(relPath) {  		return nil, fuse.EPERM  	} -	relPath, err := rfs.decryptPath(relPath) +	cPath, err := rfs.decryptPath(relPath)  	if err != nil {  		return nil, fuse.ToStatus(err)  	} -	a, status := rfs.loopbackfs.GetAttr(relPath, context) +	a, status := rfs.inoAwareStat(cPath)  	if !status.Ok() {  		return nil, status  	} @@ -189,3 +233,8 @@ func (rfs *reverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.  	return entries, fuse.OK  } + +// StatFs - FUSE call +func (rfs *reverseFS) StatFs(name string) *fuse.StatfsOut { +	return rfs.loopbackfs.StatFs(name) +} @@ -349,6 +349,9 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi  	mOpts.Options = append(mOpts.Options, "fsname="+args.cipherdir)  	// Second column, "Type", will be shown as "fuse." + Name  	mOpts.Name = "gocryptfs" +	if args.reverse { +		mOpts.Name += "-reverse" +	}  	// The kernel enforces read-only operation, we just have to pass "ro".  	// Reverse mounts are always read-only | 
