libgocryptfs/internal/fusefrontend/root_node.go

140 lines
4.8 KiB
Go
Raw Normal View History

2020-06-21 12:42:18 +02:00
package fusefrontend
import (
2020-06-21 13:25:12 +02:00
"os"
2020-06-21 13:46:08 +02:00
"sync"
2020-06-21 13:25:12 +02:00
"sync/atomic"
"syscall"
2020-06-21 12:42:18 +02:00
"time"
2020-06-21 13:25:12 +02:00
"github.com/rfjakob/gocryptfs/internal/configfile"
"github.com/rfjakob/gocryptfs/internal/contentenc"
"github.com/rfjakob/gocryptfs/internal/inomap"
"github.com/rfjakob/gocryptfs/internal/nametransform"
2020-06-21 13:25:12 +02:00
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
2020-06-21 12:42:18 +02:00
"github.com/rfjakob/gocryptfs/internal/tlog"
)
// RootNode is the root of the filesystem tree of Nodes.
type RootNode struct {
Node
// args stores configuration arguments
args Args
2020-06-21 13:46:08 +02:00
// dirIVLock: Lock()ed if any "gocryptfs.diriv" file is modified
// Readers must RLock() it to prevent them from seeing intermediate
// states
dirIVLock sync.RWMutex
// Filename encryption helper
nameTransform nametransform.NameTransformer
// Content encryption helper
contentEnc *contentenc.ContentEnc
// MitigatedCorruptions is used to report data corruption that is internally
// mitigated by ignoring the corrupt item. For example, when OpenDir() finds
// a corrupt filename, we still return the other valid filenames.
// The corruption is logged to syslog to inform the user, and in addition,
// the corrupt filename is logged to this channel via
// reportMitigatedCorruption().
// "gocryptfs -fsck" reads from the channel to also catch these transparently-
// mitigated corruptions.
MitigatedCorruptions chan string
// IsIdle flag is set to zero each time fs.isFiltered() is called
// (uint32 so that it can be reset with CompareAndSwapUint32).
// When -idle was used when mounting, idleMonitor() sets it to 1
// periodically.
IsIdle uint32
// inoMap translates inode numbers from different devices to unique inode
// numbers.
inoMap *inomap.InoMap
}
func NewRootNode(args Args, c *contentenc.ContentEnc, n nametransform.NameTransformer) *RootNode {
// TODO
return &RootNode{
args: args,
nameTransform: n,
contentEnc: c,
inoMap: inomap.New(),
}
}
2020-06-21 13:25:12 +02:00
// mangleOpenFlags is used by Create() and Open() to convert the open flags the user
// wants to the flags we internally use to open the backing file.
// The returned flags always contain O_NOFOLLOW.
func (rn *RootNode) mangleOpenFlags(flags uint32) (newFlags int) {
newFlags = int(flags)
// Convert WRONLY to RDWR. We always need read access to do read-modify-write cycles.
if (newFlags & syscall.O_ACCMODE) == syscall.O_WRONLY {
newFlags = newFlags ^ os.O_WRONLY | os.O_RDWR
}
// We also cannot open the file in append mode, we need to seek back for RMW
newFlags = newFlags &^ os.O_APPEND
// O_DIRECT accesses must be aligned in both offset and length. Due to our
// crypto header, alignment will be off, even if userspace makes aligned
// accesses. Running xfstests generic/013 on ext4 used to trigger lots of
// EINVAL errors due to missing alignment. Just fall back to buffered IO.
newFlags = newFlags &^ syscallcompat.O_DIRECT
// Create and Open are two separate FUSE operations, so O_CREAT should not
// be part of the open flags.
newFlags = newFlags &^ syscall.O_CREAT
// We always want O_NOFOLLOW to be safe against symlink races
newFlags |= syscall.O_NOFOLLOW
return newFlags
}
2020-06-21 12:42:18 +02:00
// reportMitigatedCorruption is used to report a corruption that was transparently
// mitigated and did not return an error to the user. Pass the name of the corrupt
// item (filename for OpenDir(), xattr name for ListXAttr() etc).
// See the MitigatedCorruptions channel for more info.
func (rn *RootNode) reportMitigatedCorruption(item string) {
if rn.MitigatedCorruptions == nil {
return
}
select {
case rn.MitigatedCorruptions <- item:
case <-time.After(1 * time.Second):
tlog.Warn.Printf("BUG: reportCorruptItem: timeout")
//debug.PrintStack()
return
}
}
2020-06-21 13:25:12 +02:00
// isFiltered - check if plaintext "path" should be forbidden
//
// Prevents name clashes with internal files when file names are not encrypted
func (rn *RootNode) isFiltered(path string) bool {
atomic.StoreUint32(&rn.IsIdle, 0)
if !rn.args.PlaintextNames {
return false
}
// gocryptfs.conf in the root directory is forbidden
if path == configfile.ConfDefaultName {
tlog.Info.Printf("The name /%s is reserved when -plaintextnames is used\n",
configfile.ConfDefaultName)
return true
}
// Note: gocryptfs.diriv is NOT forbidden because diriv and plaintextnames
// are exclusive
return false
}
2020-07-04 20:32:02 +02:00
// decryptSymlinkTarget: "cData64" is base64-decoded and decrypted
// like file contents (GCM).
// The empty string decrypts to the empty string.
//
// This function does not do any I/O and is hence symlink-safe.
func (rn *RootNode) decryptSymlinkTarget(cData64 string) (string, error) {
if cData64 == "" {
return "", nil
}
cData, err := rn.nameTransform.B64DecodeString(cData64)
if err != nil {
return "", err
}
data, err := rn.contentEnc.DecryptBlock([]byte(cData), 0, nil)
if err != nil {
return "", err
}
return string(data), nil
}