aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/fusefrontend/args.go4
-rw-r--r--internal/fusefrontend_reverse/ino_map.go19
-rw-r--r--internal/fusefrontend_reverse/rfs.go94
-rw-r--r--internal/fusefrontend_reverse/rpath.go5
-rw-r--r--internal/fusefrontend_reverse/virtualfile.go22
5 files changed, 61 insertions, 83 deletions
diff --git a/internal/fusefrontend/args.go b/internal/fusefrontend/args.go
index 4029913..ddfb9dc 100644
--- a/internal/fusefrontend/args.go
+++ b/internal/fusefrontend/args.go
@@ -6,7 +6,9 @@ import (
// Args is a container for arguments that are passed from main() to fusefrontend
type Args struct {
- Masterkey []byte
+ Masterkey []byte
+ // Cipherdir is the backing storage directory (absolute path).
+ // For reverse mode, Cipherdir actually contains *plaintext* files.
Cipherdir string
CryptoBackend cryptocore.AEADTypeEnum
PlaintextNames bool
diff --git a/internal/fusefrontend_reverse/ino_map.go b/internal/fusefrontend_reverse/ino_map.go
deleted file mode 100644
index dae8222..0000000
--- a/internal/fusefrontend_reverse/ino_map.go
+++ /dev/null
@@ -1,19 +0,0 @@
-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)
-}
diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go
index 18f7506..a94f448 100644
--- a/internal/fusefrontend_reverse/rfs.go
+++ b/internal/fusefrontend_reverse/rfs.go
@@ -5,7 +5,6 @@ import (
"log"
"os"
"path/filepath"
- "sync"
"syscall"
"github.com/hanwen/go-fuse/fuse"
@@ -20,12 +19,6 @@ import (
"github.com/rfjakob/gocryptfs/internal/tlog"
)
-const (
- // virtualFileMode is the mode to use for virtual files (gocryptfs.diriv and gocryptfs.longname.*.name)
- // they are always readable, as stated in func Access
- virtualFileMode = syscall.S_IFREG | 0444
-)
-
// ReverseFS implements the pathfs.FileSystem interface and provides an
// encrypted view of a plaintext directory.
type ReverseFS struct {
@@ -39,12 +32,6 @@ 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[fusefrontend.DevInoStruct]uint64
- // Protects map access
- inoMapLock sync.Mutex
}
var _ pathfs.FileSystem = &ReverseFS{}
@@ -68,8 +55,6 @@ func NewFS(args fusefrontend.Args) *ReverseFS {
args: args,
nameTransform: nameTransform,
contentEnc: contentEnc,
- inoGen: newInoGen(),
- inoMap: map[fusefrontend.DevInoStruct]uint64{},
}
}
@@ -102,7 +87,7 @@ func (rfs *ReverseFS) isNameFile(relPath string) bool {
return fileType == nametransform.LongNameFilename
}
-// isTranslatedConfig returns true if the default config file name is in
+// isTranslatedConfig returns true if the default config file name is in use
// and the ciphertext path is "gocryptfs.conf".
// "gocryptfs.conf" then maps to ".gocryptfs.reverse.conf" in the plaintext
// directory.
@@ -116,47 +101,22 @@ func (rfs *ReverseFS) isTranslatedConfig(relPath string) bool {
return false
}
-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 := fusefrontend.DevInoFromStat(st)
- 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
+// "relPath" is the relative ciphertext path
func (rfs *ReverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
+ // Handle "gocryptfs.conf"
if rfs.isTranslatedConfig(relPath) {
- return rfs.inoAwareStat(configfile.ConfReverseName)
+ absConfPath, _ := rfs.abs(configfile.ConfReverseName, nil)
+ var st syscall.Stat_t
+ err := syscall.Lstat(absConfPath, &st)
+ if err != nil {
+ return nil, fuse.ToStatus(err)
+ }
+ var a fuse.Attr
+ a.FromStat(&st)
+ return &a, fuse.OK
}
- // Handle virtual files
+ // Handle virtual files (gocryptfs.diriv, *.name)
var f nodefs.File
var status fuse.Status
virtual := false
@@ -177,15 +137,31 @@ func (rfs *ReverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr
status = f.GetAttr(&a)
return &a, status
}
-
- cPath, err := rfs.decryptPath(relPath)
+ // Decrypt path to "plaintext relative path"
+ pRelPath, err := rfs.decryptPath(relPath)
if err != nil {
return nil, fuse.ToStatus(err)
}
- a, status := rfs.inoAwareStat(cPath)
- if !status.Ok() {
- return nil, status
+ absPath, _ := rfs.abs(pRelPath, nil)
+ // Stat the backing file
+ var st syscall.Stat_t
+ if relPath == "" {
+ // Look through symlinks for the root dir
+ err = syscall.Stat(absPath, &st)
+ } else {
+ err = syscall.Lstat(absPath, &st)
+ }
+ if err != nil {
+ return nil, fuse.ToStatus(err)
+ }
+ // Instead of risking an inode number collision, we return an error.
+ if st.Ino > virtualInoBase {
+ tlog.Warn.Printf("GetAttr %q: backing file inode number %d crosses reserved space, max=%d. Returning EOVERFLOW.",
+ relPath, st.Ino, virtualInoBase)
+ return nil, fuse.ToStatus(syscall.EOVERFLOW)
}
+ var a fuse.Attr
+ a.FromStat(&st)
// Calculate encrypted file size
if a.IsRegular() {
a.Size = rfs.contentEnc.PlainSizeToCipherSize(a.Size)
@@ -200,7 +176,7 @@ func (rfs *ReverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr
a.Size = uint64(len(linkTarget))
}
- return a, fuse.OK
+ return &a, fuse.OK
}
// Access - FUSE call
diff --git a/internal/fusefrontend_reverse/rpath.go b/internal/fusefrontend_reverse/rpath.go
index 02f4e9a..5082d11 100644
--- a/internal/fusefrontend_reverse/rpath.go
+++ b/internal/fusefrontend_reverse/rpath.go
@@ -37,6 +37,11 @@ func derivePathIV(path string, purpose ivPurposeType) []byte {
return hash[:nametransform.DirIVLen]
}
+// abs basically returns storage dir + "/" + relPath.
+// It takes an error parameter so it can directly wrap decryptPath like this:
+// a, err := rfs.abs(rfs.decryptPath(relPath))
+// abs never generates an error on its own. In other words, abs(p, nil) never
+// fails.
func (rfs *ReverseFS) abs(relPath string, err error) (string, error) {
if err != nil {
return "", err
diff --git a/internal/fusefrontend_reverse/virtualfile.go b/internal/fusefrontend_reverse/virtualfile.go
index 2bd9e88..00fa726 100644
--- a/internal/fusefrontend_reverse/virtualfile.go
+++ b/internal/fusefrontend_reverse/virtualfile.go
@@ -9,6 +9,18 @@ import (
"github.com/rfjakob/gocryptfs/internal/tlog"
)
+const (
+ // virtualFileMode is the mode to use for virtual files (gocryptfs.diriv and
+ // *.name). They are always readable, as stated in func Access
+ virtualFileMode = syscall.S_IFREG | 0444
+ // virtualInoBase is the start of the inode number range that is used
+ // for virtual files.
+ // The value 10^19 is just below 2^60. A power of 10 has been chosen so the
+ // "ls -li" output (which is base-10) is easy to read.
+ // 10^19 is the largest power of 10 that is smaller than UINT64_MAX/2.
+ virtualInoBase = uint64(1000000000000000000)
+)
+
func (rfs *ReverseFS) newDirIVFile(cRelPath string) (nodefs.File, fuse.Status) {
cDir := saneDir(cRelPath)
absDir, err := rfs.abs(rfs.decryptPath(cDir))
@@ -25,8 +37,6 @@ type virtualFile struct {
content []byte
// absolute path to a parent file
parentFile string
- // inode number
- ino uint64
}
// newVirtualFile creates a new in-memory file that does not have a representation
@@ -38,7 +48,6 @@ func (rfs *ReverseFS) newVirtualFile(content []byte, parentFile string) (nodefs.
File: nodefs.NewDefaultFile(),
content: content,
parentFile: parentFile,
- ino: rfs.inoGen.next(),
}, fuse.OK
}
@@ -62,7 +71,12 @@ func (f *virtualFile) GetAttr(a *fuse.Attr) fuse.Status {
tlog.Debug.Printf("GetAttr: Lstat %q: %v\n", f.parentFile, err)
return fuse.ToStatus(err)
}
- st.Ino = f.ino
+ if st.Ino > virtualInoBase {
+ tlog.Warn.Printf("virtualFile.GetAttr: parent file inode number %d crosses reserved space, max=%d. Returning EOVERFLOW.",
+ st.Ino, virtualInoBase)
+ return fuse.ToStatus(syscall.EOVERFLOW)
+ }
+ st.Ino = st.Ino + virtualInoBase
st.Size = int64(len(f.content))
st.Mode = virtualFileMode
st.Nlink = 1