Beispiel #1
0
func (constor *Constor) Open(input *fuse.OpenIn, out *fuse.OpenOut) (status fuse.Status) {
	pathl, err := constor.getPath(input.NodeId)
	if err != nil {
		constor.error("%s", err)
		return fuse.ToStatus(err)
	}
	inode, err := constor.inodemap.findInode(input.NodeId)
	if err != nil {
		constor.error("%s", err)
		return fuse.ToStatus(err)
	}
	constor.log("%s %d %d %d", pathl, input.Flags, input.Uid, input.Gid)
	fd, err := syscall.Open(pathl, int(input.Flags), 0)
	if err != nil {
		constor.error("%s", err)
		return fuse.ToStatus(err)
	}
	F := new(FD)
	F.fd = fd
	F.flags = int(input.Flags)
	F.layer = inode.layer
	constor.putfd(F)
	out.Fh = uint64(uintptr(unsafe.Pointer(F)))
	out.OpenFlags = 0
	constor.log("%d", out.Fh)
	return fuse.OK
}
Beispiel #2
0
func TestCreationChecks(t *testing.T) {
	wd, clean := setup(t)
	defer clean()

	err := os.Mkdir(wd+"/store/foo", 0755)
	CheckSuccess(err)
	os.Symlink(wd+"/ro", wd+"/store/foo/READONLY")
	CheckSuccess(err)

	err = os.Mkdir(wd+"/store/ws2", 0755)
	CheckSuccess(err)
	os.Symlink(wd+"/ro", wd+"/store/ws2/READONLY")
	CheckSuccess(err)

	err = os.Symlink(wd+"/store/foo", wd+"/mnt/config/bar")
	CheckSuccess(err)

	err = os.Symlink(wd+"/store/foo", wd+"/mnt/config/foo")
	code := fuse.ToStatus(err)
	if code != fuse.EBUSY {
		t.Error("Should return EBUSY", err)
	}

	err = os.Symlink(wd+"/store/ws2", wd+"/mnt/config/config")
	code = fuse.ToStatus(err)
	if code != fuse.EINVAL {
		t.Error("Should return EINVAL", err)
	}
}
Beispiel #3
0
func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
	if fs.isFiltered(newPath) {
		return fuse.EPERM
	}
	cOldPath, err := fs.getBackingPath(oldPath)
	if err != nil {
		return fuse.ToStatus(err)
	}
	cNewPath, err := fs.getBackingPath(newPath)
	if err != nil {
		return fuse.ToStatus(err)
	}
	// The Rename may cause a directory to take the place of another directory.
	// That directory may still be in the DirIV cache, clear it.
	fs.CryptFS.DirIVCacheEnc.Clear()

	err = os.Rename(cOldPath, cNewPath)

	if lerr, ok := err.(*os.LinkError); ok && lerr.Err == syscall.ENOTEMPTY {
		// If an empty directory is overwritten we will always get
		// ENOTEMPTY as the "empty" directory will still contain gocryptfs.diriv.
		// Handle that case by removing the target directory and trying again.
		cryptfs.Debug.Printf("Rename: Handling ENOTEMPTY")
		if fs.Rmdir(newPath, context) == fuse.OK {
			err = os.Rename(cOldPath, cNewPath)
		}
	}

	return fuse.ToStatus(err)
}
Beispiel #4
0
// Unlink implements pathfs.Filesystem.
func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) {
	if fs.isFiltered(path) {
		return fuse.EPERM
	}
	cPath, err := fs.getBackingPath(path)
	if err != nil {
		return fuse.ToStatus(err)
	}

	cName := filepath.Base(cPath)
	if nametransform.IsLongContent(cName) {
		var dirfd *os.File
		dirfd, err = os.Open(filepath.Dir(cPath))
		if err != nil {
			return fuse.ToStatus(err)
		}
		defer dirfd.Close()
		// Delete content
		err = syscallcompat.Unlinkat(int(dirfd.Fd()), cName)
		if err != nil {
			return fuse.ToStatus(err)
		}
		// Delete ".name"
		err = nametransform.DeleteLongName(dirfd, cName)
		if err != nil {
			tlog.Warn.Printf("Unlink: could not delete .name file: %v", err)
		}
		return fuse.ToStatus(err)
	}

	err = syscall.Unlink(cPath)
	return fuse.ToStatus(err)
}
Beispiel #5
0
// implements nodefs.File
func (this *hookFile) Fsync(flags int) fuse.Status {
	hook, hookEnabled := this.hook.(HookOnFsync)
	var prehookErr, posthookErr error
	var prehooked, posthooked bool
	var prehookCtx HookContext

	if hookEnabled {
		prehookErr, prehooked, prehookCtx = hook.PreFsync(this.name, uint32(flags))
		if prehooked {
			log.WithFields(log.Fields{
				"this":       this,
				"prehookErr": prehookErr,
				"prehookCtx": prehookCtx,
			}).Debug("Fsync: Prehooked")
			return fuse.ToStatus(prehookErr)
		}
	}

	lowerCode := this.file.Fsync(flags)
	if hookEnabled {
		posthookErr, posthooked = hook.PostFsync(int32(lowerCode), prehookCtx)
		if posthooked {
			log.WithFields(log.Fields{
				"this":        this,
				"posthookErr": posthookErr,
			}).Debug("Fsync: Posthooked")
			return fuse.ToStatus(posthookErr)
		}
	}

	return lowerCode
}
Beispiel #6
0
func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) {
	cryptfs.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName)
	if fs.isFiltered(linkName) {
		return fuse.EPERM
	}
	cPath, err := fs.getBackingPath(linkName)
	if err != nil {
		return fuse.ToStatus(err)
	}
	// Old filesystem: symlinks are encrypted like paths (CBC)
	if !fs.args.DirIV {
		cTarget, err := fs.encryptPath(target)
		if err != nil {
			cryptfs.Warn.Printf("Symlink: BUG: we should not get an error here: %v", err)
			return fuse.ToStatus(err)
		}
		err = os.Symlink(cTarget, cPath)
		return fuse.ToStatus(err)
	}
	// Since gocryptfs v0.5 symlinks are encrypted like file contents (GCM)
	cBinTarget := fs.CryptFS.EncryptBlock([]byte(target), 0, nil)
	cTarget := base64.URLEncoding.EncodeToString(cBinTarget)

	err = os.Symlink(cTarget, cPath)
	cryptfs.Debug.Printf("Symlink: os.Symlink(%s, %s) = %v", cTarget, cPath, err)
	return fuse.ToStatus(err)
}
Beispiel #7
0
func (constor *Constor) Create(input *fuse.CreateIn, name string, out *fuse.CreateOut) (code fuse.Status) {
	dirpath, err := constor.dentrymap.getPath(input.NodeId)
	if err != nil {
		constor.error("%s", err)
		return fuse.ToStatus(err)
	}
	constor.log("%s%s %d %d %d", dirpath, name, input.Mode, input.Uid, input.Gid)
	if err := constor.createPath(dirpath); err != nil {
		constor.error("%s", err)
		return fuse.ToStatus(err)
	}
	pathl := Path.Join(constor.layers[0], dirpath, name)
	// remove any deleted place holder entries
	if constor.isdeleted(pathl) {
		syscall.Unlink(pathl)
	}
	fd, err := syscall.Open(pathl, syscall.O_CREAT|syscall.O_RDWR, input.Mode)
	if err != nil {
		constor.error("%s", err)
		return fuse.ToStatus(err)
	}

	err = syscall.Chown(pathl, int(input.Uid), int(input.Gid))
	if err != nil {
		constor.error("%s", err)
		return fuse.ToStatus(err)
	}
	F := new(FD)
	F.fd = fd
	F.layer = 0
	constor.putfd(F)
	out.Fh = uint64(uintptr(unsafe.Pointer(F)))
	constor.log("%d", out.Fh)
	return constor.Lookup((*fuse.InHeader)(unsafe.Pointer(input)), name, &out.EntryOut)
}
Beispiel #8
0
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
}
Beispiel #9
0
// Readlink implements pathfs.Filesystem.
func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status fuse.Status) {
	cPath, err := fs.getBackingPath(path)
	if err != nil {
		return "", fuse.ToStatus(err)
	}
	cTarget, err := os.Readlink(cPath)
	if err != nil {
		return "", fuse.ToStatus(err)
	}
	if fs.args.PlaintextNames {
		return cTarget, fuse.OK
	}
	// Symlinks are encrypted like file contents (GCM) and base64-encoded
	cBinTarget, err := base64.URLEncoding.DecodeString(cTarget)
	if err != nil {
		tlog.Warn.Printf("Readlink: %v", err)
		return "", fuse.EIO
	}
	target, err := fs.contentEnc.DecryptBlock([]byte(cBinTarget), 0, nil)
	if err != nil {
		tlog.Warn.Printf("Readlink: %v", err)
		return "", fuse.EIO
	}
	return string(target), fuse.OK
}
Beispiel #10
0
func (constor *Constor) Access(input *fuse.AccessIn) (code fuse.Status) {
	constor.log("%d", input.NodeId)
	// FIXME: oops fix this
	path, err := constor.getPath(input.NodeId)
	if err != nil {
		return fuse.ToStatus(err)
	}
	return fuse.ToStatus(syscall.Access(path, input.Mask))
}
Beispiel #11
0
// doRead - returns "length" plaintext bytes from plaintext offset "off".
// Arguments "length" and "off" do not have to be block-aligned.
//
// doRead reads the corresponding ciphertext blocks from disk, decrypts them and
// returns the requested part of the plaintext.
//
// Called by Read() for normal reading,
// by Write() and Truncate() for Read-Modify-Write
func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) {

	// Read file header
	if f.header == nil {
		err := f.readHeader()
		if err == io.EOF {
			return nil, fuse.OK
		}
		if err != nil {
			return nil, fuse.ToStatus(err)
		}
	}

	// Read the backing ciphertext in one go
	blocks := f.cfs.ExplodePlainRange(off, length)
	alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks)
	skip := blocks[0].Skip
	cryptfs.Debug.Printf("JointCiphertextRange(%d, %d) -> %d, %d, %d\n", off, length, alignedOffset, alignedLength, skip)
	ciphertext := make([]byte, int(alignedLength))
	f.fdLock.Lock()
	n, err := f.fd.ReadAt(ciphertext, int64(alignedOffset))
	f.fdLock.Unlock()
	if err != nil && err != io.EOF {
		cryptfs.Warn.Printf("read: ReadAt: %s\n", err.Error())
		return nil, fuse.ToStatus(err)
	}
	// Truncate ciphertext buffer down to actually read bytes
	ciphertext = ciphertext[0:n]

	firstBlockNo := blocks[0].BlockNo
	cryptfs.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d\n", alignedOffset, firstBlockNo, alignedLength, n)

	// Decrypt it
	plaintext, err := f.cfs.DecryptBlocks(ciphertext, firstBlockNo, f.header.Id)
	if err != nil {
		curruptBlockNo := firstBlockNo + f.cfs.PlainOffToBlockNo(uint64(len(plaintext)))
		cipherOff := f.cfs.BlockNoToCipherOff(curruptBlockNo)
		plainOff := f.cfs.BlockNoToPlainOff(curruptBlockNo)
		cryptfs.Warn.Printf("ino%d: doRead: corrupt block #%d (plainOff=%d, cipherOff=%d)\n",
			f.ino, curruptBlockNo, plainOff, cipherOff)
		return nil, fuse.EIO
	}

	// Crop down to the relevant part
	var out []byte
	lenHave := len(plaintext)
	lenWant := int(skip + length)
	if lenHave > lenWant {
		out = plaintext[skip:lenWant]
	} else if lenHave > int(skip) {
		out = plaintext[skip:lenHave]
	}
	// else: out stays empty, file was smaller than the requested offset

	return out, fuse.OK
}
Beispiel #12
0
func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) {
	if fs.isFiltered(path) {
		return fuse.EPERM
	}
	cPath, err := fs.getBackingPath(path)
	if err != nil {
		return fuse.ToStatus(err)
	}
	return fuse.ToStatus(syscall.Unlink(cPath))
}
Beispiel #13
0
func (fs *FS) Access(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
	if fs.isFiltered(path) {
		return fuse.EPERM
	}
	cPath, err := fs.getBackingPath(path)
	if err != nil {
		return fuse.ToStatus(err)
	}
	return fuse.ToStatus(syscall.Access(cPath, mode))
}
Beispiel #14
0
// Chown implements pathfs.Filesystem.
func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
	if fs.isFiltered(path) {
		return fuse.EPERM
	}
	cPath, err := fs.getBackingPath(path)
	if err != nil {
		return fuse.ToStatus(err)
	}
	return fuse.ToStatus(os.Lchown(cPath, int(uid), int(gid)))
}
// Allocate - FUSE call for fallocate(2)
//
// mode=FALLOC_FL_KEEP_SIZE is implemented directly.
//
// mode=FALLOC_DEFAULT is implemented as a two-step process:
//
//   (1) Allocate the space using FALLOC_FL_KEEP_SIZE
//   (2) Set the file size using ftruncate (via truncateGrowFile)
//
// This allows us to reuse the file grow mechanics from Truncate as they are
// complicated and hard to get right.
//
// Other modes (hole punching, zeroing) are not supported.
func (f *file) Allocate(off uint64, sz uint64, mode uint32) fuse.Status {
	if mode != FALLOC_DEFAULT && mode != FALLOC_FL_KEEP_SIZE {
		f := func() {
			tlog.Warn.Print("fallocate: only mode 0 (default) and 1 (keep size) are supported")
		}
		allocateWarnOnce.Do(f)
		return fuse.Status(syscall.EOPNOTSUPP)
	}

	f.fdLock.RLock()
	defer f.fdLock.RUnlock()
	if f.released {
		return fuse.EBADF
	}
	f.fileTableEntry.writeLock.Lock()
	defer f.fileTableEntry.writeLock.Unlock()

	blocks := f.contentEnc.ExplodePlainRange(off, sz)
	firstBlock := blocks[0]
	lastBlock := blocks[len(blocks)-1]

	// Step (1): Allocate the space the user wants using FALLOC_FL_KEEP_SIZE.
	// This will fill file holes and/or allocate additional space past the end of
	// the file.
	cipherOff := firstBlock.BlockCipherOff()
	cipherSz := lastBlock.BlockCipherOff() - cipherOff +
		f.contentEnc.PlainSizeToCipherSize(lastBlock.Skip+lastBlock.Length)
	err := syscallcompat.Fallocate(f.intFd(), FALLOC_FL_KEEP_SIZE, int64(cipherOff), int64(cipherSz))
	tlog.Debug.Printf("Allocate off=%d sz=%d mode=%x cipherOff=%d cipherSz=%d\n",
		off, sz, mode, cipherOff, cipherSz)
	if err != nil {
		return fuse.ToStatus(err)
	}
	if mode == FALLOC_FL_KEEP_SIZE {
		// The user did not want to change the apparent size. We are done.
		return fuse.OK
	}
	// Step (2): Grow the apparent file size
	// We need the old file size to determine if we are growing the file at all.
	newPlainSz := off + sz
	oldPlainSz, err := f.statPlainSize()
	if err != nil {
		return fuse.ToStatus(err)
	}
	if newPlainSz <= oldPlainSz {
		// The new size is smaller (or equal). Fallocate with mode = 0 never
		// truncates a file, so we are done.
		return fuse.OK
	}
	// The file grows. The space has already been allocated in (1), so what is
	// left to do is to pad the first and last block and call truncate.
	// truncateGrowFile does just that.
	return f.truncateGrowFile(oldPlainSz, newPlainSz)
}
Beispiel #16
0
// doWrite - encrypt "data" and write it to plaintext offset "off"
//
// Arguments do not have to be block-aligned, read-modify-write is
// performed internally as neccessary
//
// Called by Write() for normal writing,
// and by Truncate() to rewrite the last file block.
func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {

	// Read header from disk, create a new one if the file is empty
	if f.header == nil {
		err := f.readHeader()
		if err == io.EOF {
			err = f.createHeader()

		}
		if err != nil {
			return 0, fuse.ToStatus(err)
		}
	}

	var written uint32
	status := fuse.OK
	dataBuf := bytes.NewBuffer(data)
	blocks := f.cfs.ExplodePlainRange(uint64(off), uint64(len(data)))
	for _, b := range blocks {

		blockData := dataBuf.Next(int(b.Length))

		// Incomplete block -> Read-Modify-Write
		if b.IsPartial() {
			// Read
			o, _ := b.PlaintextRange()
			oldData, status := f.doRead(o, f.cfs.PlainBS())
			if status != fuse.OK {
				cryptfs.Warn.Printf("RMW read failed: %s\n", status.String())
				return written, status
			}
			// Modify
			blockData = f.cfs.MergeBlocks(oldData, blockData, int(b.Skip))
			cryptfs.Debug.Printf("len(oldData)=%d len(blockData)=%d\n", len(oldData), len(blockData))
		}

		// Write
		blockOffset, _ := b.CiphertextRange()
		blockData = f.cfs.EncryptBlock(blockData, b.BlockNo, f.header.Id)
		cryptfs.Debug.Printf("ino%d: Writing %d bytes to block #%d, md5=%s\n",
			f.ino, len(blockData)-cryptfs.BLOCK_OVERHEAD, b.BlockNo, cryptfs.Debug.Md5sum(blockData))
		f.fdLock.Lock()
		_, err := f.fd.WriteAt(blockData, int64(blockOffset))
		f.fdLock.Unlock()

		if err != nil {
			cryptfs.Warn.Printf("Write failed: %s\n", err.Error())
			status = fuse.ToStatus(err)
			break
		}
		written += uint32(b.Length)
	}
	return written, status
}
Beispiel #17
0
func (constor *Constor) Unlink(header *fuse.InHeader, name string) (code fuse.Status) {
	var stat syscall.Stat_t

	parent := constor.inodemap.findInodePtr(header.NodeId)
	if parent == nil {
		constor.error("parent == nil")
		return fuse.ENOENT
	}
	constor.log("%s %s", parent.id, name)
	id, err := constor.getid(-1, parent.id, name)
	if err != nil {
		constor.error("getid failed %s %s", parent.id, name)
		return fuse.ToStatus(err)
	}
	inode := constor.inodemap.findInodeId(id)
	if inode == nil {
		constor.error("%s %s : inode == nil", parent.id, name)
		return fuse.ENOENT
	}
	if inode.layer == 0 {
		linkcnt, err := constor.declinkscnt(inode.id)
		if err != nil {
			constor.error("declinkscnt %s : %s", inode.id, err)
			return fuse.ToStatus(err)
		}
		if linkcnt == 0 {
			path := constor.getPath(0, inode.id)
			if err := syscall.Unlink(path); err != nil {
				constor.error("Unlink failed for %s : %s", path, err)
				return fuse.ToStatus(err)
			}
			inode.layer = -1
		}
	}
	err = constor.copyup(parent)
	if err != nil {
		constor.error("copyup failed on %s : ", parent.id, err)
		return fuse.ToStatus(err)
	}
	// if there is an entry path, delete it
	entrypath := Path.Join(constor.getPath(0, parent.id), name)
	if err := syscall.Lstat(entrypath, &stat); err == nil {
		if err := syscall.Unlink(entrypath); err != nil {
			constor.error("Unlink failed for %s : %s", entrypath, err)
			return fuse.ToStatus(err)
		}
	}
	// if the file is in a lower layer then create a deleted place holder file
	if _, err := constor.getid(-1, parent.id, name); err == nil {
		constor.setdeleted(entrypath)
	}
	return fuse.OK
}
Beispiel #18
0
func (b *DirNode) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
	e := b.Dir.IsChildEmpty(name)
	if e != nil {
		return fuse.ToStatus(e)
	}
	e = b.Dir.Delete(name, true)
	if e != nil {
		return fuse.ToStatus(e)
	}
	b.Inode().RmChild(name)
	return fuse.OK
}
Beispiel #19
0
func (constor *Constor) Readlink(header *fuse.InHeader) (out []byte, code fuse.Status) {
	constor.log("%d", header.NodeId)
	pathl, err := constor.getPath(header.NodeId)
	if err != nil {
		return []byte{}, fuse.ToStatus(err)
	}
	link, err := os.Readlink(pathl)
	if err != nil {
		return []byte{}, fuse.ToStatus(err)
	}
	return []byte(link), fuse.OK
}
Beispiel #20
0
// Create implements pathfs.Filesystem.
func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) {
	if fs.isFiltered(path) {
		return nil, fuse.EPERM
	}
	iflags, writeOnly := fs.mangleOpenFlags(flags)
	cPath, err := fs.getBackingPath(path)
	if err != nil {
		return nil, fuse.ToStatus(err)
	}

	var fd *os.File
	cName := filepath.Base(cPath)

	// Handle long file name
	if nametransform.IsLongContent(cName) {
		var dirfd *os.File
		dirfd, err = os.Open(filepath.Dir(cPath))
		if err != nil {
			return nil, fuse.ToStatus(err)
		}
		defer dirfd.Close()

		// Create ".name"
		err = fs.nameTransform.WriteLongName(dirfd, cName, path)
		if err != nil {
			return nil, fuse.ToStatus(err)
		}

		// Create content
		var fdRaw int
		fdRaw, err = syscallcompat.Openat(int(dirfd.Fd()), cName, iflags|os.O_CREATE, mode)
		if err != nil {
			nametransform.DeleteLongName(dirfd, cName)
			return nil, fuse.ToStatus(err)
		}
		fd = os.NewFile(uintptr(fdRaw), cName)
	} else {
		// Normal (short) file name
		fd, err = os.OpenFile(cPath, iflags|os.O_CREATE, os.FileMode(mode))
		if err != nil {
			return nil, fuse.ToStatus(err)
		}
	}
	// Set owner
	if fs.args.PreserveOwner {
		err = fd.Chown(int(context.Owner.Uid), int(context.Owner.Gid))
		if err != nil {
			tlog.Warn.Printf("Create: fd.Chown failed: %v", err)
		}
	}
	return NewFile(fd, writeOnly, fs)
}
Beispiel #21
0
// Symlink implements pathfs.Filesystem.
func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) {
	tlog.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName)
	if fs.isFiltered(linkName) {
		return fuse.EPERM
	}
	cPath, err := fs.getBackingPath(linkName)
	if err != nil {
		return fuse.ToStatus(err)
	}
	if fs.args.PlaintextNames {
		err = os.Symlink(target, cPath)
		return fuse.ToStatus(err)
	}
	// Symlinks are encrypted like file contents (GCM) and base64-encoded
	cBinTarget := fs.contentEnc.EncryptBlock([]byte(target), 0, nil)
	cTarget := base64.URLEncoding.EncodeToString(cBinTarget)
	// Handle long file name
	cName := filepath.Base(cPath)
	if nametransform.IsLongContent(cName) {
		var dirfd *os.File
		dirfd, err = os.Open(filepath.Dir(cPath))
		if err != nil {
			return fuse.ToStatus(err)
		}
		defer dirfd.Close()
		// Create ".name" file
		err = fs.nameTransform.WriteLongName(dirfd, cName, linkName)
		if err != nil {
			return fuse.ToStatus(err)
		}
		// Create "gocryptfs.longfile." symlink
		// TODO use syscall.Symlinkat once it is available in Go
		err = syscall.Symlink(cTarget, cPath)
		if err != nil {
			nametransform.DeleteLongName(dirfd, cName)
		}
	} else {
		// Create symlink
		err = os.Symlink(cTarget, cPath)
	}
	if err != nil {
		return fuse.ToStatus(err)
	}
	// Set owner
	if fs.args.PreserveOwner {
		err = os.Lchown(cPath, int(context.Owner.Uid), int(context.Owner.Gid))
		if err != nil {
			tlog.Warn.Printf("Mknod: Lchown failed: %v", err)
		}
	}
	return fuse.OK
}
Beispiel #22
0
func (constor *Constor) Write(input *fuse.WriteIn, data []byte) (written uint32, code fuse.Status) {
	constor.log("%d %d", input.Fh, len(data))
	ptr := uintptr(input.Fh)
	offset := input.Offset
	wdata := data

	F := constor.getfd(ptr)
	if F == nil {
		constor.error("F == nil")
		return 0, fuse.EIO
	}
	if F.flags&syscall.O_DIRECT != 0 {
		wdata = directio.AlignedBlock(len(data))
		copy(wdata, data)
	}
	inode := constor.inodemap.findInodePtr(input.NodeId)
	if inode == nil {
		return 0, fuse.ENOENT
	}
	if F.layer != 0 && inode.layer != 0 {
		err := constor.copyup(inode)
		if err != nil {
			constor.error("%s", err)
			return 0, fuse.ToStatus(err)
		}
		path := constor.getPath(0, inode.id)
		syscall.Close(F.fd)
		fd, err := syscall.Open(path, F.flags, 0)
		if err != nil {
			constor.error("%s", err)
			return 0, fuse.ToStatus(err)
		}
		F.fd = fd
		F.layer = 0
		constor.log("reset fd for %s", path)
	} else if F.layer != 0 && inode.layer == 0 {
		syscall.Close(F.fd)
		path := constor.getPath(0, inode.id)
		fd, err := syscall.Open(path, F.flags, 0)
		if err != nil {
			constor.error("%s", err)
			return 0, fuse.ToStatus(err)
		}
		F.fd = fd
		F.layer = 0
		constor.log("reset fd for %s", path)
	}

	fd := F.fd
	n, err := syscall.Pwrite(fd, wdata, int64(offset))
	return uint32(n), fuse.ToStatus(err)
}
Beispiel #23
0
// Access - FUSE call
func (rfs *ReverseFS) Access(relPath string, mode uint32, context *fuse.Context) fuse.Status {
	if rfs.isTranslatedConfig(relPath) {
		return fuse.OK
	}
	if rfs.isDirIV(relPath) {
		return fuse.OK
	}
	absPath, err := rfs.abs(rfs.decryptPath(relPath))
	if err != nil {
		return fuse.ToStatus(err)
	}
	return fuse.ToStatus(syscall.Access(absPath, mode))
}
Beispiel #24
0
// Chmod implements pathfs.Filesystem.
func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
	if fs.isFiltered(path) {
		return fuse.EPERM
	}
	cPath, err := fs.getBackingPath(path)
	if err != nil {
		return fuse.ToStatus(err)
	}
	// os.Chmod goes through the "syscallMode" translation function that messes
	// up the suid and sgid bits. So use syscall.Chmod directly.
	err = syscall.Chmod(cPath, mode)
	return fuse.ToStatus(err)
}
Beispiel #25
0
func (f *AzukiFile) Flush() fuse.Status {
	f.lock.Lock()
	newFd, err := syscall.Dup(int(f.File.Fd()))
	f.lock.Unlock()

	if err != nil {
		return fuse.ToStatus(err)
	}

	err = syscall.Close(newFd)

	return fuse.ToStatus(err)
}
Beispiel #26
0
func (p *pendingFile) rwLoopback() (nodefs.File, fuse.Status) {
	if p.loopback == nil {
		if err := p.node.fetch(); err != nil {
			return nil, fuse.ToStatus(err)
		}
		f, err := os.OpenFile(p.node.backing, os.O_RDWR|os.O_CREATE, 0644)
		if err != nil {
			return nil, fuse.ToStatus(err)
		}

		p.loopback = nodefs.NewLoopbackFile(f)
	}
	return p.loopback, fuse.OK
}
Beispiel #27
0
// implements nodefs.File
func (this *hookFile) Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status) {
	hook, hookEnabled := this.hook.(HookOnRead)
	var prehookBuf, posthookBuf []byte
	var prehookErr, posthookErr error
	var prehooked, posthooked bool
	var prehookCtx HookContext

	if hookEnabled {
		prehookBuf, prehookErr, prehooked, prehookCtx = hook.PreRead(this.name, int64(len(dest)), off)
		if prehooked {
			log.WithFields(log.Fields{
				"this": this,
				// "prehookBuf": prehookBuf,
				"prehookErr": prehookErr,
				"prehookCtx": prehookCtx,
			}).Debug("Read: Prehooked")
			return fuse.ReadResultData(prehookBuf), fuse.ToStatus(prehookErr)
		}
	}

	lowerRR, lowerCode := this.file.Read(dest, off)
	if hookEnabled {
		lowerRRBuf, lowerRRBufStatus := lowerRR.Bytes(make([]byte, lowerRR.Size()))
		if lowerRRBufStatus != fuse.OK {
			log.WithField("error", lowerRRBufStatus).Panic("lowerRR.Bytes() should not cause an error")
		}
		posthookBuf, posthookErr, posthooked = hook.PostRead(int32(lowerCode), lowerRRBuf, prehookCtx)
		if posthooked {
			if len(posthookBuf) != len(lowerRRBuf) {
				log.WithFields(log.Fields{
					"this": this,
					// "posthookBuf": posthookBuf,
					"posthookErr":    posthookErr,
					"posthookBufLen": len(posthookBuf),
					"lowerRRBufLen":  len(lowerRRBuf),
					"destLen":        len(dest),
				}).Warn("Read: Posthooked, but posthookBuf length != lowerrRRBuf length. You may get a strange behavior.")
			}

			log.WithFields(log.Fields{
				"this": this,
				// "posthookBuf": posthookBuf,
				"posthookErr": posthookErr,
			}).Debug("Read: Posthooked")
			return fuse.ReadResultData(posthookBuf), fuse.ToStatus(posthookErr)
		}
	}

	return lowerRR, lowerCode
}
Beispiel #28
0
func (b *DirNode) Mkdir(name string, mode uint32, context *fuse.Context) (newNode *nodefs.Inode, code fuse.Status) {
	d, f, e := b.Dir.Create(name, true)
	if e != nil {
		return nil, fuse.ToStatus(e)
	}
	if d != nil {
		dn := &DirNode{nodefs.NewDefaultNode(), d}
		return b.Inode().NewChild(name, true, dn), fuse.OK
	} else if f != nil {
		fn := &FileNode{nodefs.NewDefaultNode(), f}
		return b.Inode().NewChild(name, false, fn), fuse.OK
	}
	return nil, fuse.ToStatus(syscall.EIO)
}
Beispiel #29
0
func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
	if fs.isFiltered(newPath) {
		return fuse.EPERM
	}
	cOldPath, err := fs.getBackingPath(oldPath)
	if err != nil {
		return fuse.ToStatus(err)
	}
	cNewPath, err := fs.getBackingPath(newPath)
	if err != nil {
		return fuse.ToStatus(err)
	}
	return fuse.ToStatus(os.Link(cOldPath, cNewPath))
}
Beispiel #30
0
func (constor *Constor) Lookup(header *fuse.InHeader, name string, out *fuse.EntryOut) fuse.Status {
	var stat syscall.Stat_t
	if len(name) > 255 {
		constor.error("name too long : %s", name)
		return fuse.Status(syscall.ENAMETOOLONG)
	}
	li := -1
	parent := constor.inodemap.findInodePtr(header.NodeId)
	if parent == nil {
		constor.error("Unable to find parent inode : %d", header.NodeId)
		return fuse.ENOENT
	}
	constor.log("%s(%s)", parent.id, name)
	id, err := constor.getid(-1, parent.id, name)
	if err != nil {
		// logging this error will produce too many logs as there will be too many
		// lookps on non-existant files
		return fuse.ToStatus(err)
	}
	inode := constor.inodemap.findInodeId(id)
	if inode != nil {
		li = inode.layer
	} else {
		li = constor.getLayer(id)
	}
	if li == -1 {
		constor.error("Unable to find inode for %s(%s) id %s", parent.id, name, id)
		return fuse.ENOENT
	}
	err = constor.Lstat(li, id, &stat)
	if err != nil {
		constor.error("Unable to Lstat inode for %s(%s) id %s", parent.id, name, id)
		return fuse.ToStatus(err)
	}
	if inode == nil {
		inode = NewInode(constor, id)
		inode.layer = li
		constor.inodemap.hashInode(inode)
	} else {
		inode.lookup()
	}
	attr := (*fuse.Attr)(&out.Attr)
	attr.FromStat(&stat)
	out.NodeId = uint64(uintptr(unsafe.Pointer(inode)))
	out.Ino = attr.Ino
	out.EntryValid = 1000
	out.AttrValid = 1000
	constor.log("%s", id)
	return fuse.OK
}