1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
package fusefrontend_reverse
import (
"context"
"log"
"syscall"
"github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/fuse"
"github.com/rfjakob/gocryptfs/v2/internal/configfile"
"github.com/rfjakob/gocryptfs/v2/internal/inomap"
"github.com/rfjakob/gocryptfs/v2/internal/nametransform"
)
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
// We use inomap's `Tag` feature to generate unique inode numbers for
// virtual files. These are the tags we use.
inoTagDirIV = 1
inoTagNameFile = 2
)
type fileType int
// Values returned by lookupFileType
const (
// A real file/directory/symlink in the backing plaintext directory
typeReal fileType = iota
// A DirIV (gocryptfs.diriv) file
typeDiriv
// A gocryptfs.longname.*.name file for a file with a long name
typeName
// The config file gocryptfs.conf
typeConfig
)
// lookupFileType returns the type of child file name
// (one of the fileType constants above). Called from Lookup().
func (n *Node) lookupFileType(cName string) fileType {
rn := n.rootNode()
// In -plaintextname mode, neither diriv nor longname files exist.
if !rn.args.PlaintextNames {
if !rn.args.DeterministicNames {
// Is it a gocryptfs.diriv file?
if cName == nametransform.DirIVFilename {
return typeDiriv
}
}
// Is it a gocryptfs.longname.*.name file?
if t := nametransform.NameType(cName); t == nametransform.LongNameFilename {
return typeName
}
}
// gocryptfs.conf in the root directory. This is passed through to
// .gocryptfs.reverse.conf in the backing plaintext directory.
if n.isRoot() && !rn.args.ConfigCustom && cName == configfile.ConfDefaultName {
return typeConfig
}
return typeReal
}
// VirtualMemNode is an in-memory node that does not have a representation
// on disk.
type VirtualMemNode struct {
fs.Inode
// file content
content []byte
// attributes for Getattr()
attr fuse.Attr
}
// newVirtualMemNode creates a new in-memory file that does not have a representation
// on disk. "content" is the file content. Timestamps and file owner are copied
// from "parentFile" (file descriptor).
// For a "gocryptfs.diriv" file, you would use the parent directory as
// "parentFile".
func (n *Node) newVirtualMemNode(content []byte, parentStat *syscall.Stat_t, inoTag uint8) (vf *VirtualMemNode, errno syscall.Errno) {
if inoTag == 0 {
log.Panicf("BUG: inoTag for virtual file is zero - this will cause ino collisions!")
}
// Adjust inode number and size
rn := n.rootNode()
st := parentStat
if inoTag == inoTagNameFile {
// No stable mapping for gocryptfs.longname.*.name files, instead use an
// incrementing counter. We don't want two of those files to ever have the
// same inode number, even for hard-linked files.
st.Ino = rn.inoMap.NextSpillIno()
} else {
q := inomap.NewQIno(uint64(st.Dev), inoTag, uint64(st.Ino))
st.Ino = rn.inoMap.Translate(q)
}
st.Size = int64(len(content))
st.Mode = virtualFileMode
st.Nlink = 1
var a fuse.Attr
a.FromStat(st)
// With inode number reuse and hard links, we could have returned
// wrong data for gocryptfs.diriv and gocryptfs.xyz.longname files, respectively
// (https://github.com/rfjakob/gocryptfs/issues/802).
//
// Now that this is fixed, ensure that rsync and similar tools pick up the new
// correct files by advancing mtime and ctime by 10 seconds, which should be more
// than any filesytems' timestamp granularity (FAT32 has 2 seconds).
a.Mtime += 10
a.Ctime += 10
if rn.args.ForceOwner != nil {
a.Owner = *rn.args.ForceOwner
}
vf = &VirtualMemNode{content: content, attr: a}
return
}
// Open - FUSE call
func (f *VirtualMemNode) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
return nil, fuse.FOPEN_KEEP_CACHE, 0
}
// GetAttr - FUSE call
func (f *VirtualMemNode) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
out.Attr = f.attr
return 0
}
// Read - FUSE call
func (f *VirtualMemNode) Read(ctx context.Context, fh fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
end := int(off) + len(dest)
if end > len(f.content) {
end = len(f.content)
}
return fuse.ReadResultData(f.content[off:end]), 0
}
|