diff options
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | cryptfs/file.go | 12 | ||||
| -rw-r--r-- | cryptfs/fs.go | 26 | ||||
| -rw-r--r-- | frontend/dir.go | 10 | ||||
| -rw-r--r-- | frontend/file.go | 4 | ||||
| -rw-r--r-- | frontend/fs.go | 29 | ||||
| -rw-r--r-- | frontend/node.go | 108 | ||||
| -rw-r--r-- | main.go | 394 | 
8 files changed, 64 insertions, 521 deletions
| diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d65432 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Binary +/gocryptfs diff --git a/cryptfs/file.go b/cryptfs/file.go index 77262d4..5645f3c 100644 --- a/cryptfs/file.go +++ b/cryptfs/file.go @@ -8,7 +8,7 @@ import (  	"crypto/cipher"  ) -type File struct { +type CryptFile struct {  	file *os.File  	gcm cipher.AEAD  	plainBS	int64 @@ -17,7 +17,7 @@ type File struct {  // readCipherBlock - Read ciphertext block number "blockNo", decrypt,  // return plaintext -func (be *File) readCipherBlock(blockNo int64) ([]byte, error) { +func (be *CryptFile) readCipherBlock(blockNo int64) ([]byte, error) {  	off := blockNo * int64(be.cipherBS)  	buf := make([]byte, be.cipherBS) @@ -64,7 +64,7 @@ type intraBlock struct {  }  // Split a plaintext byte range into (possible partial) blocks -func (be *File) splitRange(offset int64, length int64) []intraBlock { +func (be *CryptFile) splitRange(offset int64, length int64) []intraBlock {  	var b intraBlock  	var parts []intraBlock @@ -79,7 +79,7 @@ func (be *File) splitRange(offset int64, length int64) []intraBlock {  	return parts  } -func (be *File) min64(x int64, y int64) int64 { +func (be *CryptFile) min64(x int64, y int64) int64 {  	if x < y {  		return x  	} @@ -87,7 +87,7 @@ func (be *File) min64(x int64, y int64) int64 {  }  // writeCipherBlock - Encrypt plaintext and write it to file block "blockNo" -func (be *File) writeCipherBlock(blockNo int64, plain []byte) error { +func (be *CryptFile) writeCipherBlock(blockNo int64, plain []byte) error {  	if int64(len(plain)) > be.plainBS {  		panic("writeCipherBlock: Cannot write block that is larger than plainBS") @@ -109,7 +109,7 @@ func (be *File) writeCipherBlock(blockNo int64, plain []byte) error {  // Perform RMW cycle on block  // Write "data" into file location specified in "b" -func (be *File) rmwWrite(b intraBlock, data []byte, f *os.File) error { +func (be *CryptFile) rmwWrite(b intraBlock, data []byte, f *os.File) error {  	if b.length != int64(len(data)) {  		panic("Length mismatch")  	} diff --git a/cryptfs/fs.go b/cryptfs/fs.go index 9050b30..f5781e2 100644 --- a/cryptfs/fs.go +++ b/cryptfs/fs.go @@ -19,14 +19,14 @@ const (  	DECRYPT = false  ) -type FS struct { +type CryptFS struct {  	blockCipher cipher.Block  	gcm cipher.AEAD  	plainBS	int64  	cipherBS int64  } -func NewFS(key [16]byte) *FS { +func NewCryptFS(key [16]byte) *CryptFS {  	b, err := aes.NewCipher(key[:])  	if err != nil { @@ -38,7 +38,7 @@ func NewFS(key [16]byte) *FS {  		panic(err)  	} -	return &FS{ +	return &CryptFS{  		blockCipher: b,  		gcm: g,  		plainBS: DEFAULT_PLAINBS, @@ -46,8 +46,8 @@ func NewFS(key [16]byte) *FS {  	}  } -func (fs *FS) NewFile(f *os.File) *File { -	return &File { +func (fs *CryptFS) NewFile(f *os.File) *CryptFile { +	return &CryptFile {  		file: f,  		gcm: fs.gcm,  		plainBS: fs.plainBS, @@ -56,7 +56,7 @@ func (fs *FS) NewFile(f *os.File) *File {  }  // DecryptName - decrypt filename -func (be *FS) decryptName(cipherName string) (string, error) { +func (be *CryptFS) decryptName(cipherName string) (string, error) {  	bin, err := base64.URLEncoding.DecodeString(cipherName)  	if err != nil { @@ -81,7 +81,7 @@ func (be *FS) decryptName(cipherName string) (string, error) {  }  // EncryptName - encrypt filename -func (be *FS) encryptName(plainName string) string { +func (be *CryptFS) encryptName(plainName string) string {  	bin := []byte(plainName)  	bin = be.pad16(bin) @@ -97,7 +97,7 @@ func (be *FS) encryptName(plainName string) string {  // TranslatePath - encrypt or decrypt path. Just splits the string on "/"  // and hands the parts to EncryptName() / DecryptName() -func (be *FS) translatePath(path string, op bool) (string, error) { +func (be *CryptFS) translatePath(path string, op bool) (string, error) {  	var err error  	// Empty string means root directory @@ -125,18 +125,18 @@ func (be *FS) translatePath(path string, op bool) (string, error) {  }  // EncryptPath - encrypt filename or path. Just hands it to TranslatePath(). -func (be *FS) EncryptPath(path string) string { +func (be *CryptFS) EncryptPath(path string) string {  	newPath, _ := be.translatePath(path, ENCRYPT)  	return newPath  }  // DecryptPath - decrypt filename or path. Just hands it to TranslatePath(). -func (be *FS) DecryptPath(path string) (string, error) { +func (be *CryptFS) DecryptPath(path string) (string, error) {  	return be.translatePath(path, DECRYPT)  }  // plainSize - calculate plaintext size from ciphertext size -func (be *FS) PlainSize(s int64) int64 { +func (be *CryptFS) PlainSize(s int64) int64 {  	// Zero sized files stay zero-sized  	if s > 0 {  		// Number of blocks @@ -149,7 +149,7 @@ func (be *FS) PlainSize(s int64) int64 {  // pad16 - pad filename to 16 byte blocks using standard PKCS#7 padding  // https://tools.ietf.org/html/rfc5652#section-6.3 -func (be *FS) pad16(orig []byte) (padded []byte) { +func (be *CryptFS) pad16(orig []byte) (padded []byte) {  	oldLen := len(orig)  	if oldLen == 0 {  		panic("Padding zero-length string makes no sense") @@ -169,7 +169,7 @@ func (be *FS) pad16(orig []byte) (padded []byte) {  }  // unPad16 - remove padding -func (be *FS) unPad16(orig []byte) ([]byte, error) { +func (be *CryptFS) unPad16(orig []byte) ([]byte, error) {  	oldLen := len(orig)  	if oldLen % aes.BlockSize != 0 {  		return nil, errors.New("Unaligned size") diff --git a/frontend/dir.go b/frontend/dir.go new file mode 100644 index 0000000..4703df9 --- /dev/null +++ b/frontend/dir.go @@ -0,0 +1,10 @@ +package frontend + +import ( +	//"github.com/rfjakob/gocryptfs/cryptfs" +	"github.com/rfjakob/cluefs/lib/cluefs" +) + +type Dir struct { +	*cluefs.Dir +} diff --git a/frontend/file.go b/frontend/file.go index 7b292bd..81590e8 100644 --- a/frontend/file.go +++ b/frontend/file.go @@ -2,8 +2,10 @@ package frontend  import (  	"github.com/rfjakob/gocryptfs/cryptfs" +	"github.com/rfjakob/cluefs/lib/cluefs"  )  type File struct { -	cryptfs.File +	*cryptfs.CryptFile +	*cluefs.File  } diff --git a/frontend/fs.go b/frontend/fs.go index 637d134..ba6ad09 100644 --- a/frontend/fs.go +++ b/frontend/fs.go @@ -2,25 +2,26 @@ package frontend  import (  	"github.com/rfjakob/gocryptfs/cryptfs" -	"bazil.org/fuse/fs" +	"github.com/rfjakob/cluefs/lib/cluefs"  )  type FS struct { -	*cryptfs.FS -	backing string +	*cryptfs.CryptFS +	*cluefs.ClueFS  } -func New(key [16]byte, b string) *FS { -	return &FS { -		FS: cryptfs.NewFS(key), -		backing: b, -	} -} +type nullTracer struct {} -func (fs *FS) Root() (fs.Node, error) { -	n := Node{ -		backing: "", -		parentFS: fs, +func (nullTracer) Trace(op cluefs.FsOperTracer) {} + +func NewFS(key [16]byte, backing string) *FS { +	var nt nullTracer +	clfs, err := cluefs.NewClueFS(backing, nt) +	if err != nil { +		panic(err) +	} +	return &FS { +		CryptFS: cryptfs.NewCryptFS(key), +		ClueFS: clfs,  	} -	return n, nil  } diff --git a/frontend/node.go b/frontend/node.go index 833be2e..7218d54 100644 --- a/frontend/node.go +++ b/frontend/node.go @@ -1,113 +1,9 @@  package frontend  import ( -	"fmt" -	"os" -	"time" -	"syscall" -	"io/ioutil" -	"path" - -	"golang.org/x/net/context" - -	//"github.com/rfjakob/gocryptfs/cryptfs" -	"bazil.org/fuse" -	"bazil.org/fuse/fs" +	"github.com/rfjakob/cluefs/lib/cluefs"  ) -  type Node struct { -	fs.NodeRef -	backing string -	parentFS *FS -} - -// FileModeFromStat - create os.FileMode from stat value -// For some reason, they use different constants. -// Adapted from https://golang.org/src/os/stat_linux.go -func FileModeFromStat(st *syscall.Stat_t) os.FileMode { -	fileMode := os.FileMode(st.Mode & 0777) -	switch st.Mode & syscall.S_IFMT { -	case syscall.S_IFBLK: -		fileMode |= os.ModeDevice -	case syscall.S_IFCHR: -		fileMode |= os.ModeDevice | os.ModeCharDevice -	case syscall.S_IFDIR: -		fileMode |= os.ModeDir -	case syscall.S_IFIFO: -		fileMode |= os.ModeNamedPipe -	case syscall.S_IFLNK: -		fileMode |= os.ModeSymlink -	case syscall.S_IFREG: -		// nothing to do -	case syscall.S_IFSOCK: -		fileMode |= os.ModeSocket -	} -	if st.Mode & syscall.S_ISGID != 0 { -		fileMode |= os.ModeSetgid -	} -	if st.Mode & syscall.S_ISUID != 0 { -		fileMode |= os.ModeSetuid -	} -	if st.Mode & syscall.S_ISVTX != 0 { -		fileMode |= os.ModeSticky -	} -	return fileMode -} - - -func StatToAttr(s *syscall.Stat_t, a *fuse.Attr) { -	a.Inode = s.Ino -	a.Size = uint64(s.Size) -	a.Blocks = uint64(s.Blocks) -	a.Atime = time.Unix(s.Atim.Sec, s.Atim.Nsec) -	a.Mtime = time.Unix(s.Mtim.Sec, s.Mtim.Nsec) -	a.Ctime = time.Unix(s.Ctim.Sec, s.Ctim.Nsec) -	a.Mode = FileModeFromStat(s) -	a.Nlink = uint32(s.Nlink) -	a.Uid = uint32(s.Uid) -	a.Gid = uint32(s.Gid) -	a.Rdev = uint32(s.Rdev) -} - -func (n Node) Attr(ctx context.Context, attr *fuse.Attr) error { -	var err error -	var st syscall.Stat_t -	if n.backing == "" { -		// When GetAttr is called for the toplevel directory, we always want -		// to look through symlinks. -		fmt.Printf("Attr %s\n", n.parentFS.backing) -		//err = syscall.Stat(n.parentFS.backing, &st) -		err = syscall.Stat("/", &st) -	} else { -		fmt.Printf("Attr %s\n", path.Join(n.parentFS.backing, n.backing)) -		p := path.Join(n.parentFS.backing, n.backing) -		err = syscall.Lstat(p, &st) -	} -	if err != nil { -		return err -	} -	StatToAttr(&st, attr) -	return nil -} - -func (n *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { -	entries, err := ioutil.ReadDir(n.backing) -	if err != nil { -		return nil, err -	} -	var fuseEntries []fuse.Dirent -	for _, e := range entries { -		var d fuse.Dirent -		d.Name = e.Name() -		fuseEntries = append(fuseEntries, d) -	} -	return fuseEntries, err -} - -func (n *Node) Lookup(ctx context.Context, name string) (fs.Node, error) { -	if name == "hello" { -		return Node{}, nil -	} -	return nil, fuse.ENOENT +	*cluefs.Node  } @@ -1,396 +1,28 @@ -// Memfs implements an in-memory file system.  package main  import ( -	"flag" -	"fmt" -	"log" -	"os" -	"sync" -	"sync/atomic" -	"time" - -	"bazil.org/fuse" -	"bazil.org/fuse/fs" -	"golang.org/x/net/context" - +	"github.com/rfjakob/cluefs/lib/cluefs"  	"github.com/rfjakob/gocryptfs/frontend" +	"os"  ) -// debug flag enables logging of debug messages to stderr. -var debug = flag.Bool("debug", true, "enable debug log messages to stderr") - -func usage() { -	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) -	fmt.Fprintf(os.Stderr, "  %s BACKING MOUNTPOINT\n", os.Args[0]) -	flag.PrintDefaults() -} - -func debugLog(msg interface{}) { -	fmt.Fprintf(os.Stderr, "%v\n", msg) -} -  func main() { -	flag.Usage = usage -	flag.Parse() - -	if flag.NArg() != 2 { -		usage() -		os.Exit(2) -	} - -	backing := flag.Arg(0) -	mountpoint := flag.Arg(1) -	c, err := fuse.Mount( -		mountpoint, -		fuse.FSName("gocryptfs"), -		fuse.Subtype("gocryptfs"), -		fuse.LocalVolume(), -		fuse.VolumeName("gocryptfs"), -	) +	// Parse command line arguments +	conf, err := cluefs.ParseArguments()  	if err != nil { -		log.Fatal(err) -	} -	defer c.Close() - -	cfg := &fs.Config{} -	if *debug { -		cfg.Debug = debugLog +		os.Exit(1)  	} -	srv := fs.New(c, cfg) +	// Create the file system object  	var key [16]byte -	filesys := frontend.New(key, backing) - -	if err := srv.Serve(filesys); err != nil { -		log.Fatal(err) -	} - -	// Check if the mount process has an error to report. -	<-c.Ready -	if err := c.MountError; err != nil { -		log.Fatal(err) -	} -} - -type MemFS struct { -	root   *Dir -	nodeId uint64 - -	nodeCount uint64 -	size      int64 -} - -// Compile-time interface checks. -var _ fs.FS = (*MemFS)(nil) -var _ fs.FSStatfser = (*MemFS)(nil) - -var _ fs.Node = (*Dir)(nil) -var _ fs.NodeCreater = (*Dir)(nil) -var _ fs.NodeMkdirer = (*Dir)(nil) -var _ fs.NodeRemover = (*Dir)(nil) -var _ fs.NodeRenamer = (*Dir)(nil) -var _ fs.NodeStringLookuper = (*Dir)(nil) - -var _ fs.HandleReadAller = (*File)(nil) -var _ fs.HandleWriter = (*File)(nil) -var _ fs.Node = (*File)(nil) -var _ fs.NodeOpener = (*File)(nil) -var _ fs.NodeSetattrer = (*File)(nil) - -func NewMemFS() *MemFS { -	fs := &MemFS{ -		nodeCount: 1, -	} -	fs.root = fs.newDir(os.ModeDir | 0777) -	if fs.root.attr.Inode != 1 { -		panic("Root node should have been assigned id 1") -	} -	return fs -} - -func (m *MemFS) nextId() uint64 { -	return atomic.AddUint64(&m.nodeId, 1) -} - -func (m *MemFS) newDir(mode os.FileMode) *Dir { -	n := time.Now() -	return &Dir{ -		attr: fuse.Attr{ -			Inode:  m.nextId(), -			Atime:  n, -			Mtime:  n, -			Ctime:  n, -			Crtime: n, -			Mode:   os.ModeDir | mode, -		}, -		fs:    m, -		nodes: make(map[string]fs.Node), -	} -} - -func (m *MemFS) newFile(mode os.FileMode) *File { -	n := time.Now() -	return &File{ -		attr: fuse.Attr{ -			Inode:  m.nextId(), -			Atime:  n, -			Mtime:  n, -			Ctime:  n, -			Crtime: n, -			Mode:   mode, -		}, -		data: make([]byte, 0), -	} -} - -type Dir struct { -	sync.RWMutex -	attr fuse.Attr - -	fs     *MemFS -	parent *Dir -	nodes  map[string]fs.Node -} - -type File struct { -	sync.RWMutex -	attr fuse.Attr - -	fs   *MemFS -	data []byte -} - -func (f *MemFS) Root() (fs.Node, error) { -	return f.root, nil -} - -func (f *MemFS) Statfs(ctx context.Context, req *fuse.StatfsRequest, -	resp *fuse.StatfsResponse) error { -	resp.Blocks = uint64((atomic.LoadInt64(&f.size) + 511) / 512) -	resp.Bsize = 512 -	resp.Files = atomic.LoadUint64(&f.nodeCount) -	return nil -} - -func (f *File) Attr(ctx context.Context, o *fuse.Attr) error { -	f.RLock() -	*o = f.attr -	f.RUnlock() -	return nil -} - -func (d *Dir) Attr(ctx context.Context, o *fuse.Attr) error { -	d.RLock() -	*o = d.attr -	d.RUnlock() -	return nil -} - -func (d *Dir) Lookup(ctx context.Context, name string) (fs.Node, error) { -	d.RLock() -	n, exist := d.nodes[name] -	d.RUnlock() - -	if !exist { -		return nil, fuse.ENOENT -	} -	return n, nil -} - -func (d *Dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { -	d.RLock() -	dirs := make([]fuse.Dirent, len(d.nodes)+2) +	cfs := frontend.NewFS(key, conf.GetShadowDir()) -	// Add special references. -	dirs[0] = fuse.Dirent{ -		Name:  ".", -		Inode: d.attr.Inode, -		Type:  fuse.DT_Dir, +	// Mount and serve file system requests +	if err = cfs.MountAndServe(conf.GetMountPoint(), conf.GetReadOnly()); err != nil { +		cluefs.ErrlogMain.Printf("could not mount file system [%s]", err) +		os.Exit(3)  	} -	dirs[1] = fuse.Dirent{ -		Name: "..", -		Type: fuse.DT_Dir, -	} -	if d.parent != nil { -		dirs[1].Inode = d.parent.attr.Inode -	} else { -		dirs[1].Inode = d.attr.Inode -	} - -	// Add remaining files. -	idx := 2 -	for name, node := range d.nodes { -		ent := fuse.Dirent{ -			Name: name, -		} -		switch n := node.(type) { -		case *File: -			ent.Inode = n.attr.Inode -			ent.Type = fuse.DT_File -		case *Dir: -			ent.Inode = n.attr.Inode -			ent.Type = fuse.DT_Dir -		} -		dirs[idx] = ent -		idx++ -	} -	d.RUnlock() -	return dirs, nil -} - -func (d *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) { -	d.Lock() -	defer d.Unlock() - -	if _, exists := d.nodes[req.Name]; exists { -		return nil, fuse.EEXIST -	} - -	n := d.fs.newDir(req.Mode) -	d.nodes[req.Name] = n -	atomic.AddUint64(&d.fs.nodeCount, 1) - -	return n, nil -} - -func (d *Dir) Create(ctx context.Context, req *fuse.CreateRequest, -	resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) { -	d.Lock() -	defer d.Unlock() - -	if _, exists := d.nodes[req.Name]; exists { -		return nil, nil, fuse.EEXIST -	} - -	n := d.fs.newFile(req.Mode) -	n.fs = d.fs -	d.nodes[req.Name] = n -	atomic.AddUint64(&d.fs.nodeCount, 1) - -	resp.Attr = n.attr - -	return n, n, nil -} - -func (d *Dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error { -	nd := newDir.(*Dir) -	if d.attr.Inode == nd.attr.Inode { -		d.Lock() -		defer d.Unlock() -	} else if d.attr.Inode < nd.attr.Inode { -		d.Lock() -		defer d.Unlock() -		nd.Lock() -		defer nd.Unlock() -	} else { -		nd.Lock() -		defer nd.Unlock() -		d.Lock() -		defer d.Unlock() -	} - -	if _, exists := d.nodes[req.OldName]; !exists { -		return fuse.ENOENT -	} - -	// Rename can be used as an atomic replace, override an existing file. -	if old, exists := nd.nodes[req.NewName]; exists { -		atomic.AddUint64(&d.fs.nodeCount, ^uint64(0)) // decrement by one -		if oldFile, ok := old.(*File); !ok { -			atomic.AddInt64(&d.fs.size, -int64(oldFile.attr.Size)) -		} -	} - -	nd.nodes[req.NewName] = d.nodes[req.OldName] -	delete(d.nodes, req.OldName) -	return nil -} - -func (d *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error { -	d.Lock() -	defer d.Unlock() - -	if n, exists := d.nodes[req.Name]; !exists { -		return fuse.ENOENT -	} else if req.Dir && len(n.(*Dir).nodes) > 0 { -		return fuse.ENOTEMPTY -	} - -	delete(d.nodes, req.Name) -	atomic.AddUint64(&d.fs.nodeCount, ^uint64(0)) // decrement by one -	return nil -} - -func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, -	error) { -	return f, nil -} - -func (f *File) ReadAll(ctx context.Context) ([]byte, error) { -	f.RLock() -	out := make([]byte, len(f.data)) -	copy(out, f.data) -	f.RUnlock() - -	return out, nil -} - -func (f *File) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error { -	f.Lock() - -	l := len(req.Data) -	end := int(req.Offset) + l -	if end > len(f.data) { -		delta := end - len(f.data) -		f.data = append(f.data, make([]byte, delta)...) -		f.attr.Size = uint64(len(f.data)) -		atomic.AddInt64(&f.fs.size, int64(delta)) -	} -	copy(f.data[req.Offset:end], req.Data) -	resp.Size = l - -	f.Unlock() -	return nil -} - -func (f *File) Setattr(ctx context.Context, req *fuse.SetattrRequest, -	resp *fuse.SetattrResponse) error { -	f.Lock() - -	if req.Valid.Size() { -		delta := int(req.Size) - len(f.data) -		if delta > 0 { -			f.data = append(f.data, make([]byte, delta)...) -		} else { -			f.data = f.data[0:req.Size] -		} -		f.attr.Size = req.Size -		atomic.AddInt64(&f.fs.size, int64(delta)) -	} - -	if req.Valid.Mode() { -		f.attr.Mode = req.Mode -	} - -	if req.Valid.Atime() { -		f.attr.Atime = req.Atime -	} - -	if req.Valid.AtimeNow() { -		f.attr.Atime = time.Now() -	} - -	if req.Valid.Mtime() { -		f.attr.Mtime = req.Mtime -	} - -	if req.Valid.MtimeNow() { -		f.attr.Mtime = time.Now() -	} - -	resp.Attr = f.attr -	f.Unlock() -	return nil +	// We are done +	os.Exit(0)  } | 
