aboutsummaryrefslogtreecommitdiff
path: root/internal/fusefrontend_reverse/virtualnode.go
blob: 732564ac1d5d659ea97437dbbafdb36b792de5df (plain)
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
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)
	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
}