v2api: File2: implement Release, Read, Write, Fsync, Flush, Allocate
Fortunately, this just means fixing up the function signatures.
This commit is contained in:
parent
d539a4c214
commit
6196a5b5fe
@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// File2 implements the go-fuse v2 API (github.com/hanwen/go-fuse/v2/fs)
|
||||||
type File2 struct {
|
type File2 struct {
|
||||||
fd *os.File
|
fd *os.File
|
||||||
// Has Release() already been called on this file? This also means that the
|
// Has Release() already been called on this file? This also means that the
|
||||||
@ -129,7 +130,7 @@ func (f *File2) createHeader() (fileID []byte, err error) {
|
|||||||
//
|
//
|
||||||
// Called by Read() for normal reading,
|
// Called by Read() for normal reading,
|
||||||
// by Write() and Truncate() via doWrite() for Read-Modify-Write.
|
// by Write() and Truncate() via doWrite() for Read-Modify-Write.
|
||||||
func (f *File2) doRead(dst []byte, off uint64, length uint64) ([]byte, fuse.Status) {
|
func (f *File2) doRead(dst []byte, off uint64, length uint64) ([]byte, syscall.Errno) {
|
||||||
// Get the file ID, either from the open file table, or from disk.
|
// Get the file ID, either from the open file table, or from disk.
|
||||||
var fileID []byte
|
var fileID []byte
|
||||||
f.fileTableEntry.IDLock.Lock()
|
f.fileTableEntry.IDLock.Lock()
|
||||||
@ -144,7 +145,7 @@ func (f *File2) doRead(dst []byte, off uint64, length uint64) ([]byte, fuse.Stat
|
|||||||
f.fileTableEntry.IDLock.Unlock()
|
f.fileTableEntry.IDLock.Unlock()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
// Empty file
|
// Empty file
|
||||||
return nil, fuse.OK
|
return nil, 0
|
||||||
}
|
}
|
||||||
buf := make([]byte, 100)
|
buf := make([]byte, 100)
|
||||||
n, _ := f.fd.ReadAt(buf, 0)
|
n, _ := f.fd.ReadAt(buf, 0)
|
||||||
@ -152,7 +153,7 @@ func (f *File2) doRead(dst []byte, off uint64, length uint64) ([]byte, fuse.Stat
|
|||||||
hexdump := hex.EncodeToString(buf)
|
hexdump := hex.EncodeToString(buf)
|
||||||
tlog.Warn.Printf("doRead %d: corrupt header: %v\nFile hexdump (%d bytes): %s",
|
tlog.Warn.Printf("doRead %d: corrupt header: %v\nFile hexdump (%d bytes): %s",
|
||||||
f.qIno.Ino, err, n, hexdump)
|
f.qIno.Ino, err, n, hexdump)
|
||||||
return nil, fuse.EIO
|
return nil, syscall.EIO
|
||||||
}
|
}
|
||||||
// Save into the file table
|
// Save into the file table
|
||||||
f.fileTableEntry.ID = fileID
|
f.fileTableEntry.ID = fileID
|
||||||
@ -173,12 +174,12 @@ func (f *File2) doRead(dst []byte, off uint64, length uint64) ([]byte, fuse.Stat
|
|||||||
n, err := f.fd.ReadAt(ciphertext, int64(alignedOffset))
|
n, err := f.fd.ReadAt(ciphertext, int64(alignedOffset))
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
tlog.Warn.Printf("read: ReadAt: %s", err.Error())
|
tlog.Warn.Printf("read: ReadAt: %s", err.Error())
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
// The ReadAt came back empty. We can skip all the decryption and return early.
|
// The ReadAt came back empty. We can skip all the decryption and return early.
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
f.rootNode.contentEnc.CReqPool.Put(ciphertext)
|
f.rootNode.contentEnc.CReqPool.Put(ciphertext)
|
||||||
return dst, fuse.OK
|
return dst, 0
|
||||||
}
|
}
|
||||||
// Truncate ciphertext buffer down to actually read bytes
|
// Truncate ciphertext buffer down to actually read bytes
|
||||||
ciphertext = ciphertext[0:n]
|
ciphertext = ciphertext[0:n]
|
||||||
@ -198,7 +199,7 @@ func (f *File2) doRead(dst []byte, off uint64, length uint64) ([]byte, fuse.Stat
|
|||||||
} else {
|
} else {
|
||||||
curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext)))
|
curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext)))
|
||||||
tlog.Warn.Printf("doRead %d: corrupt block #%d: %v", f.qIno.Ino, curruptBlockNo, err)
|
tlog.Warn.Printf("doRead %d: corrupt block #%d: %v", f.qIno.Ino, curruptBlockNo, err)
|
||||||
return nil, fuse.EIO
|
return nil, syscall.EIO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,15 +217,15 @@ func (f *File2) doRead(dst []byte, off uint64, length uint64) ([]byte, fuse.Stat
|
|||||||
out = append(dst, out...)
|
out = append(dst, out...)
|
||||||
f.rootNode.contentEnc.PReqPool.Put(plaintext)
|
f.rootNode.contentEnc.PReqPool.Put(plaintext)
|
||||||
|
|
||||||
return out, fuse.OK
|
return out, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read - FUSE call
|
// Read - FUSE call
|
||||||
func (f *File2) Read(buf []byte, off int64) (resultData fuse.ReadResult, code fuse.Status) {
|
func (f *File2) Read(ctx context.Context, buf []byte, off int64) (resultData fuse.ReadResult, errno syscall.Errno) {
|
||||||
if len(buf) > fuse.MAX_KERNEL_WRITE {
|
if len(buf) > fuse.MAX_KERNEL_WRITE {
|
||||||
// This would crash us due to our fixed-size buffer pool
|
// This would crash us due to our fixed-size buffer pool
|
||||||
tlog.Warn.Printf("Read: rejecting oversized request with EMSGSIZE, len=%d", len(buf))
|
tlog.Warn.Printf("Read: rejecting oversized request with EMSGSIZE, len=%d", len(buf))
|
||||||
return nil, fuse.Status(syscall.EMSGSIZE)
|
return nil, syscall.EMSGSIZE
|
||||||
}
|
}
|
||||||
f.fdLock.RLock()
|
f.fdLock.RLock()
|
||||||
defer f.fdLock.RUnlock()
|
defer f.fdLock.RUnlock()
|
||||||
@ -236,15 +237,15 @@ func (f *File2) Read(buf []byte, off int64) (resultData fuse.ReadResult, code fu
|
|||||||
if f.rootNode.args.SerializeReads {
|
if f.rootNode.args.SerializeReads {
|
||||||
serialize_reads.Wait(off, len(buf))
|
serialize_reads.Wait(off, len(buf))
|
||||||
}
|
}
|
||||||
out, status := f.doRead(buf[:0], uint64(off), uint64(len(buf)))
|
out, errno := f.doRead(buf[:0], uint64(off), uint64(len(buf)))
|
||||||
if f.rootNode.args.SerializeReads {
|
if f.rootNode.args.SerializeReads {
|
||||||
serialize_reads.Done()
|
serialize_reads.Done()
|
||||||
}
|
}
|
||||||
if status != fuse.OK {
|
if errno != 0 {
|
||||||
return nil, status
|
return nil, errno
|
||||||
}
|
}
|
||||||
tlog.Debug.Printf("ino%d: Read: status %v, returning %d bytes", f.qIno.Ino, status, len(out))
|
tlog.Debug.Printf("ino%d: Read: errno=%d, returning %d bytes", f.qIno.Ino, errno, len(out))
|
||||||
return fuse.ReadResultData(out), status
|
return fuse.ReadResultData(out), errno
|
||||||
}
|
}
|
||||||
|
|
||||||
// doWrite - encrypt "data" and write it to plaintext offset "off"
|
// doWrite - encrypt "data" and write it to plaintext offset "off"
|
||||||
@ -256,7 +257,7 @@ func (f *File2) Read(buf []byte, off int64) (resultData fuse.ReadResult, code fu
|
|||||||
// and by Truncate() to rewrite the last file block.
|
// and by Truncate() to rewrite the last file block.
|
||||||
//
|
//
|
||||||
// Empty writes do nothing and are allowed.
|
// Empty writes do nothing and are allowed.
|
||||||
func (f *File2) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
func (f *File2) doWrite(data []byte, off int64) (uint32, syscall.Errno) {
|
||||||
fileWasEmpty := false
|
fileWasEmpty := false
|
||||||
// Get the file ID, create a new one if it does not exist yet.
|
// Get the file ID, create a new one if it does not exist yet.
|
||||||
var fileID []byte
|
var fileID []byte
|
||||||
@ -274,7 +275,7 @@ func (f *File2) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
fileWasEmpty = true
|
fileWasEmpty = true
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fuse.ToStatus(err)
|
return 0, fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
f.fileTableEntry.ID = fileID
|
f.fileTableEntry.ID = fileID
|
||||||
}
|
}
|
||||||
@ -287,10 +288,10 @@ func (f *File2) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
// Incomplete block -> Read-Modify-Write
|
// Incomplete block -> Read-Modify-Write
|
||||||
if b.IsPartial() {
|
if b.IsPartial() {
|
||||||
// Read
|
// Read
|
||||||
oldData, status := f.doRead(nil, b.BlockPlainOff(), f.contentEnc.PlainBS())
|
oldData, errno := f.doRead(nil, b.BlockPlainOff(), f.contentEnc.PlainBS())
|
||||||
if status != fuse.OK {
|
if errno != 0 {
|
||||||
tlog.Warn.Printf("ino%d fh%d: RMW read failed: %s", f.qIno.Ino, f.intFd(), status.String())
|
tlog.Warn.Printf("ino%d fh%d: RMW read failed: errno=%d", f.qIno.Ino, f.intFd(), errno)
|
||||||
return 0, status
|
return 0, errno
|
||||||
}
|
}
|
||||||
// Modify
|
// Modify
|
||||||
blockData = f.contentEnc.MergeBlocks(oldData, blockData, int(b.Skip))
|
blockData = f.contentEnc.MergeBlocks(oldData, blockData, int(b.Skip))
|
||||||
@ -321,7 +322,7 @@ func (f *File2) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
tlog.Warn.Printf("ino%d fh%d: doWrite: rollback failed: %v", f.qIno.Ino, f.intFd(), err2)
|
tlog.Warn.Printf("ino%d fh%d: doWrite: rollback failed: %v", f.qIno.Ino, f.intFd(), err2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0, fuse.ToStatus(err)
|
return 0, fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Write
|
// Write
|
||||||
@ -331,9 +332,9 @@ func (f *File2) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Warn.Printf("ino%d fh%d: doWrite: WriteAt off=%d len=%d failed: %v",
|
tlog.Warn.Printf("ino%d fh%d: doWrite: WriteAt off=%d len=%d failed: %v",
|
||||||
f.qIno.Ino, f.intFd(), cOff, len(ciphertext), err)
|
f.qIno.Ino, f.intFd(), cOff, len(ciphertext), err)
|
||||||
return 0, fuse.ToStatus(err)
|
return 0, fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
return uint32(len(data)), fuse.OK
|
return uint32(len(data)), 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// isConsecutiveWrite returns true if the current write
|
// isConsecutiveWrite returns true if the current write
|
||||||
@ -349,18 +350,18 @@ func (f *File2) isConsecutiveWrite(off int64) bool {
|
|||||||
// Write - FUSE call
|
// Write - FUSE call
|
||||||
//
|
//
|
||||||
// If the write creates a hole, pads the file to the next block boundary.
|
// If the write creates a hole, pads the file to the next block boundary.
|
||||||
func (f *File2) Write(data []byte, off int64) (uint32, fuse.Status) {
|
func (f *File2) Write(ctx context.Context, data []byte, off int64) (uint32, syscall.Errno) {
|
||||||
if len(data) > fuse.MAX_KERNEL_WRITE {
|
if len(data) > fuse.MAX_KERNEL_WRITE {
|
||||||
// This would crash us due to our fixed-size buffer pool
|
// This would crash us due to our fixed-size buffer pool
|
||||||
tlog.Warn.Printf("Write: rejecting oversized request with EMSGSIZE, len=%d", len(data))
|
tlog.Warn.Printf("Write: rejecting oversized request with EMSGSIZE, len=%d", len(data))
|
||||||
return 0, fuse.Status(syscall.EMSGSIZE)
|
return 0, syscall.EMSGSIZE
|
||||||
}
|
}
|
||||||
f.fdLock.RLock()
|
f.fdLock.RLock()
|
||||||
defer f.fdLock.RUnlock()
|
defer f.fdLock.RUnlock()
|
||||||
if f.released {
|
if f.released {
|
||||||
// The file descriptor has been closed concurrently
|
// The file descriptor has been closed concurrently
|
||||||
tlog.Warn.Printf("ino%d fh%d: Write on released file", f.qIno.Ino, f.intFd())
|
tlog.Warn.Printf("ino%d fh%d: Write on released file", f.qIno.Ino, f.intFd())
|
||||||
return 0, fuse.EBADF
|
return 0, syscall.EBADF
|
||||||
}
|
}
|
||||||
f.fileTableEntry.ContentLock.Lock()
|
f.fileTableEntry.ContentLock.Lock()
|
||||||
defer f.fileTableEntry.ContentLock.Unlock()
|
defer f.fileTableEntry.ContentLock.Unlock()
|
||||||
@ -369,21 +370,21 @@ func (f *File2) Write(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
// But if the write directly follows an earlier write, it cannot create a
|
// But if the write directly follows an earlier write, it cannot create a
|
||||||
// hole, and we can save one Stat() call.
|
// hole, and we can save one Stat() call.
|
||||||
if !f.isConsecutiveWrite(off) {
|
if !f.isConsecutiveWrite(off) {
|
||||||
status := f.writePadHole(off)
|
errno := f.writePadHole(off)
|
||||||
if !status.Ok() {
|
if errno != 0 {
|
||||||
return 0, status
|
return 0, errno
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n, status := f.doWrite(data, off)
|
n, errno := f.doWrite(data, off)
|
||||||
if status.Ok() {
|
if errno != 0 {
|
||||||
f.lastOpCount = openfiletable.WriteOpCount()
|
f.lastOpCount = openfiletable.WriteOpCount()
|
||||||
f.lastWrittenOffset = off + int64(len(data)) - 1
|
f.lastWrittenOffset = off + int64(len(data)) - 1
|
||||||
}
|
}
|
||||||
return n, status
|
return n, errno
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release - FUSE call, close file
|
// Release - FUSE call, close file
|
||||||
func (f *File2) Release() {
|
func (f *File2) Release(ctx context.Context) syscall.Errno {
|
||||||
f.fdLock.Lock()
|
f.fdLock.Lock()
|
||||||
if f.released {
|
if f.released {
|
||||||
log.Panicf("ino%d fh%d: double release", f.qIno.Ino, f.intFd())
|
log.Panicf("ino%d fh%d: double release", f.qIno.Ino, f.intFd())
|
||||||
@ -392,10 +393,11 @@ func (f *File2) Release() {
|
|||||||
openfiletable.Unregister(f.qIno)
|
openfiletable.Unregister(f.qIno)
|
||||||
f.fd.Close()
|
f.fd.Close()
|
||||||
f.fdLock.Unlock()
|
f.fdLock.Unlock()
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush - FUSE call
|
// Flush - FUSE call
|
||||||
func (f *File2) Flush() fuse.Status {
|
func (f *File2) Flush(ctx context.Context) syscall.Errno {
|
||||||
f.fdLock.RLock()
|
f.fdLock.RLock()
|
||||||
defer f.fdLock.RUnlock()
|
defer f.fdLock.RUnlock()
|
||||||
|
|
||||||
@ -405,18 +407,18 @@ func (f *File2) Flush() fuse.Status {
|
|||||||
newFd, err := syscall.Dup(f.intFd())
|
newFd, err := syscall.Dup(f.intFd())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fuse.ToStatus(err)
|
return fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
err = syscall.Close(newFd)
|
err = syscall.Close(newFd)
|
||||||
return fuse.ToStatus(err)
|
return fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fsync FUSE call
|
// Fsync FUSE call
|
||||||
func (f *File2) Fsync(flags int) (code fuse.Status) {
|
func (f *File2) Fsync(ctx context.Context, flags uint32) (errno syscall.Errno) {
|
||||||
f.fdLock.RLock()
|
f.fdLock.RLock()
|
||||||
defer f.fdLock.RUnlock()
|
defer f.fdLock.RUnlock()
|
||||||
|
|
||||||
return fuse.ToStatus(syscall.Fsync(f.intFd()))
|
return fs.ToErrno(syscall.Fsync(f.intFd()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getattr FUSE call (like stat)
|
// Getattr FUSE call (like stat)
|
||||||
|
@ -4,10 +4,11 @@ package fusefrontend
|
|||||||
// i.e. ftruncate and fallocate
|
// i.e. ftruncate and fallocate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/hanwen/go-fuse/v2/fuse"
|
"github.com/hanwen/go-fuse/v2/fs"
|
||||||
|
|
||||||
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
@ -26,19 +27,19 @@ import (
|
|||||||
// complicated and hard to get right.
|
// complicated and hard to get right.
|
||||||
//
|
//
|
||||||
// Other modes (hole punching, zeroing) are not supported.
|
// Other modes (hole punching, zeroing) are not supported.
|
||||||
func (f *File2) Allocate(off uint64, sz uint64, mode uint32) fuse.Status {
|
func (f *File2) Allocate(ctx context.Context, off uint64, sz uint64, mode uint32) syscall.Errno {
|
||||||
if mode != FALLOC_DEFAULT && mode != FALLOC_FL_KEEP_SIZE {
|
if mode != FALLOC_DEFAULT && mode != FALLOC_FL_KEEP_SIZE {
|
||||||
f := func() {
|
f := func() {
|
||||||
tlog.Info.Printf("fallocate: only mode 0 (default) and 1 (keep size) are supported")
|
tlog.Info.Printf("fallocate: only mode 0 (default) and 1 (keep size) are supported")
|
||||||
}
|
}
|
||||||
allocateWarnOnce.Do(f)
|
allocateWarnOnce.Do(f)
|
||||||
return fuse.Status(syscall.EOPNOTSUPP)
|
return syscall.EOPNOTSUPP
|
||||||
}
|
}
|
||||||
|
|
||||||
f.fdLock.RLock()
|
f.fdLock.RLock()
|
||||||
defer f.fdLock.RUnlock()
|
defer f.fdLock.RUnlock()
|
||||||
if f.released {
|
if f.released {
|
||||||
return fuse.EBADF
|
return syscall.EBADF
|
||||||
}
|
}
|
||||||
f.fileTableEntry.ContentLock.Lock()
|
f.fileTableEntry.ContentLock.Lock()
|
||||||
defer f.fileTableEntry.ContentLock.Unlock()
|
defer f.fileTableEntry.ContentLock.Unlock()
|
||||||
@ -57,23 +58,23 @@ func (f *File2) Allocate(off uint64, sz uint64, mode uint32) fuse.Status {
|
|||||||
tlog.Debug.Printf("Allocate off=%d sz=%d mode=%x cipherOff=%d cipherSz=%d\n",
|
tlog.Debug.Printf("Allocate off=%d sz=%d mode=%x cipherOff=%d cipherSz=%d\n",
|
||||||
off, sz, mode, cipherOff, cipherSz)
|
off, sz, mode, cipherOff, cipherSz)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fuse.ToStatus(err)
|
return fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
if mode == FALLOC_FL_KEEP_SIZE {
|
if mode == FALLOC_FL_KEEP_SIZE {
|
||||||
// The user did not want to change the apparent size. We are done.
|
// The user did not want to change the apparent size. We are done.
|
||||||
return fuse.OK
|
return 0
|
||||||
}
|
}
|
||||||
// Step (2): Grow the apparent file size
|
// Step (2): Grow the apparent file size
|
||||||
// We need the old file size to determine if we are growing the file at all.
|
// We need the old file size to determine if we are growing the file at all.
|
||||||
newPlainSz := off + sz
|
newPlainSz := off + sz
|
||||||
oldPlainSz, err := f.statPlainSize()
|
oldPlainSz, err := f.statPlainSize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fuse.ToStatus(err)
|
return fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
if newPlainSz <= oldPlainSz {
|
if newPlainSz <= oldPlainSz {
|
||||||
// The new size is smaller (or equal). Fallocate with mode = 0 never
|
// The new size is smaller (or equal). Fallocate with mode = 0 never
|
||||||
// truncates a file, so we are done.
|
// truncates a file, so we are done.
|
||||||
return fuse.OK
|
return 0
|
||||||
}
|
}
|
||||||
// The file grows. The space has already been allocated in (1), so what is
|
// 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.
|
// left to do is to pad the first and last block and call truncate.
|
||||||
@ -82,13 +83,13 @@ func (f *File2) Allocate(off uint64, sz uint64, mode uint32) fuse.Status {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// truncate - called from Setattr.
|
// truncate - called from Setattr.
|
||||||
func (f *File2) truncate(newSize uint64) fuse.Status {
|
func (f *File2) truncate(newSize uint64) (errno syscall.Errno) {
|
||||||
f.fdLock.RLock()
|
f.fdLock.RLock()
|
||||||
defer f.fdLock.RUnlock()
|
defer f.fdLock.RUnlock()
|
||||||
if f.released {
|
if f.released {
|
||||||
// The file descriptor has been closed concurrently.
|
// The file descriptor has been closed concurrently.
|
||||||
tlog.Warn.Printf("ino%d fh%d: Truncate on released file", f.qIno.Ino, f.intFd())
|
tlog.Warn.Printf("ino%d fh%d: Truncate on released file", f.qIno.Ino, f.intFd())
|
||||||
return fuse.EBADF
|
return syscall.EBADF
|
||||||
}
|
}
|
||||||
f.fileTableEntry.ContentLock.Lock()
|
f.fileTableEntry.ContentLock.Lock()
|
||||||
defer f.fileTableEntry.ContentLock.Unlock()
|
defer f.fileTableEntry.ContentLock.Unlock()
|
||||||
@ -98,17 +99,17 @@ func (f *File2) truncate(newSize uint64) fuse.Status {
|
|||||||
err = syscall.Ftruncate(int(f.fd.Fd()), 0)
|
err = syscall.Ftruncate(int(f.fd.Fd()), 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Warn.Printf("ino%d fh%d: Ftruncate(fd, 0) returned error: %v", f.qIno.Ino, f.intFd(), err)
|
tlog.Warn.Printf("ino%d fh%d: Ftruncate(fd, 0) returned error: %v", f.qIno.Ino, f.intFd(), err)
|
||||||
return fuse.ToStatus(err)
|
return fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
// Truncate to zero kills the file header
|
// Truncate to zero kills the file header
|
||||||
f.fileTableEntry.ID = nil
|
f.fileTableEntry.ID = nil
|
||||||
return fuse.OK
|
return 0
|
||||||
}
|
}
|
||||||
// We need the old file size to determine if we are growing or shrinking
|
// We need the old file size to determine if we are growing or shrinking
|
||||||
// the file
|
// the file
|
||||||
oldSize, err := f.statPlainSize()
|
oldSize, err := f.statPlainSize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fuse.ToStatus(err)
|
return fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
oldB := float32(oldSize) / float32(f.contentEnc.PlainBS())
|
oldB := float32(oldSize) / float32(f.contentEnc.PlainBS())
|
||||||
@ -117,7 +118,7 @@ func (f *File2) truncate(newSize uint64) fuse.Status {
|
|||||||
|
|
||||||
// File size stays the same - nothing to do
|
// File size stays the same - nothing to do
|
||||||
if newSize == oldSize {
|
if newSize == oldSize {
|
||||||
return fuse.OK
|
return 0
|
||||||
}
|
}
|
||||||
// File grows
|
// File grows
|
||||||
if newSize > oldSize {
|
if newSize > oldSize {
|
||||||
@ -131,25 +132,24 @@ func (f *File2) truncate(newSize uint64) fuse.Status {
|
|||||||
lastBlockLen := newSize - plainOff
|
lastBlockLen := newSize - plainOff
|
||||||
var data []byte
|
var data []byte
|
||||||
if lastBlockLen > 0 {
|
if lastBlockLen > 0 {
|
||||||
var status fuse.Status
|
data, errno = f.doRead(nil, plainOff, lastBlockLen)
|
||||||
data, status = f.doRead(nil, plainOff, lastBlockLen)
|
if errno != 0 {
|
||||||
if status != fuse.OK {
|
|
||||||
tlog.Warn.Printf("Truncate: shrink doRead returned error: %v", err)
|
tlog.Warn.Printf("Truncate: shrink doRead returned error: %v", err)
|
||||||
return status
|
return errno
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Truncate down to the last complete block
|
// Truncate down to the last complete block
|
||||||
err = syscall.Ftruncate(int(f.fd.Fd()), int64(cipherOff))
|
err = syscall.Ftruncate(int(f.fd.Fd()), int64(cipherOff))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Warn.Printf("Truncate: shrink Ftruncate returned error: %v", err)
|
tlog.Warn.Printf("Truncate: shrink Ftruncate returned error: %v", err)
|
||||||
return fuse.ToStatus(err)
|
return fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
// Append partial block
|
// Append partial block
|
||||||
if lastBlockLen > 0 {
|
if lastBlockLen > 0 {
|
||||||
_, status := f.doWrite(data, int64(plainOff))
|
_, status := f.doWrite(data, int64(plainOff))
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
return fuse.OK
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// statPlainSize stats the file and returns the plaintext size
|
// statPlainSize stats the file and returns the plaintext size
|
||||||
@ -167,7 +167,7 @@ func (f *File2) statPlainSize() (uint64, error) {
|
|||||||
// truncateGrowFile extends a file using seeking or ftruncate performing RMW on
|
// truncateGrowFile extends a file using seeking or ftruncate performing RMW on
|
||||||
// the first and last block as necessary. New blocks in the middle become
|
// the first and last block as necessary. New blocks in the middle become
|
||||||
// file holes unless they have been fallocate()'d beforehand.
|
// file holes unless they have been fallocate()'d beforehand.
|
||||||
func (f *File2) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) fuse.Status {
|
func (f *File2) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) syscall.Errno {
|
||||||
if newPlainSz <= oldPlainSz {
|
if newPlainSz <= oldPlainSz {
|
||||||
log.Panicf("BUG: newSize=%d <= oldSize=%d", newPlainSz, oldPlainSz)
|
log.Panicf("BUG: newSize=%d <= oldSize=%d", newPlainSz, oldPlainSz)
|
||||||
}
|
}
|
||||||
@ -179,17 +179,17 @@ func (f *File2) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) fuse.Stat
|
|||||||
// Write a single zero to the last byte and let doWrite figure out the RMW.
|
// Write a single zero to the last byte and let doWrite figure out the RMW.
|
||||||
if n1 == n2 {
|
if n1 == n2 {
|
||||||
buf := make([]byte, 1)
|
buf := make([]byte, 1)
|
||||||
_, status := f.doWrite(buf, int64(newEOFOffset))
|
_, errno := f.doWrite(buf, int64(newEOFOffset))
|
||||||
return status
|
return errno
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The truncate creates at least one new block.
|
// The truncate creates at least one new block.
|
||||||
//
|
//
|
||||||
// Make sure the old last block is padded to the block boundary. This call
|
// Make sure the old last block is padded to the block boundary. This call
|
||||||
// is a no-op if it is already block-aligned.
|
// is a no-op if it is already block-aligned.
|
||||||
status := f.zeroPad(oldPlainSz)
|
errno := f.zeroPad(oldPlainSz)
|
||||||
if !status.Ok() {
|
if errno != 0 {
|
||||||
return status
|
return errno
|
||||||
}
|
}
|
||||||
// The new size is block-aligned. In this case we can do everything ourselves
|
// The new size is block-aligned. In this case we can do everything ourselves
|
||||||
// and avoid the call to doWrite.
|
// and avoid the call to doWrite.
|
||||||
@ -198,7 +198,7 @@ func (f *File2) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) fuse.Stat
|
|||||||
if oldPlainSz == 0 {
|
if oldPlainSz == 0 {
|
||||||
id, err := f.createHeader()
|
id, err := f.createHeader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fuse.ToStatus(err)
|
return fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
f.fileTableEntry.ID = id
|
f.fileTableEntry.ID = id
|
||||||
}
|
}
|
||||||
@ -207,11 +207,11 @@ func (f *File2) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) fuse.Stat
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Warn.Printf("Truncate: grow Ftruncate returned error: %v", err)
|
tlog.Warn.Printf("Truncate: grow Ftruncate returned error: %v", err)
|
||||||
}
|
}
|
||||||
return fuse.ToStatus(err)
|
return fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
// The new size is NOT aligned, so we need to write a partial block.
|
// The new size is NOT aligned, so we need to write a partial block.
|
||||||
// Write a single zero to the last byte and let doWrite figure it out.
|
// Write a single zero to the last byte and let doWrite figure it out.
|
||||||
buf := make([]byte, 1)
|
buf := make([]byte, 1)
|
||||||
_, status = f.doWrite(buf, int64(newEOFOffset))
|
_, errno = f.doWrite(buf, int64(newEOFOffset))
|
||||||
return status
|
return errno
|
||||||
}
|
}
|
||||||
|
@ -7,17 +7,17 @@ import (
|
|||||||
// Check that we have implemented the fs.File* interfaces
|
// Check that we have implemented the fs.File* interfaces
|
||||||
var _ = (fs.FileGetattrer)((*File2)(nil))
|
var _ = (fs.FileGetattrer)((*File2)(nil))
|
||||||
var _ = (fs.FileSetattrer)((*File2)(nil))
|
var _ = (fs.FileSetattrer)((*File2)(nil))
|
||||||
|
|
||||||
/* TODO
|
|
||||||
var _ = (fs.FileHandle)((*File2)(nil))
|
|
||||||
var _ = (fs.FileReleaser)((*File2)(nil))
|
var _ = (fs.FileReleaser)((*File2)(nil))
|
||||||
var _ = (fs.FileReader)((*File2)(nil))
|
var _ = (fs.FileReader)((*File2)(nil))
|
||||||
var _ = (fs.FileWriter)((*File2)(nil))
|
var _ = (fs.FileWriter)((*File2)(nil))
|
||||||
|
var _ = (fs.FileFsyncer)((*File2)(nil))
|
||||||
|
var _ = (fs.FileFlusher)((*File2)(nil))
|
||||||
|
var _ = (fs.FileAllocater)((*File2)(nil))
|
||||||
|
|
||||||
|
/* TODO
|
||||||
|
var _ = (fs.FileHandle)((*File2)(nil))
|
||||||
var _ = (fs.FileGetlker)((*File2)(nil))
|
var _ = (fs.FileGetlker)((*File2)(nil))
|
||||||
var _ = (fs.FileSetlker)((*File2)(nil))
|
var _ = (fs.FileSetlker)((*File2)(nil))
|
||||||
var _ = (fs.FileSetlkwer)((*File2)(nil))
|
var _ = (fs.FileSetlkwer)((*File2)(nil))
|
||||||
var _ = (fs.FileLseeker)((*File2)(nil))
|
var _ = (fs.FileLseeker)((*File2)(nil))
|
||||||
var _ = (fs.FileFlusher)((*File2)(nil))
|
|
||||||
var _ = (fs.FileFsyncer)((*File2)(nil))
|
|
||||||
var _ = (fs.FileAllocater)((*File2)(nil))
|
|
||||||
*/
|
*/
|
||||||
|
@ -6,19 +6,19 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/hanwen/go-fuse/v2/fuse"
|
"github.com/hanwen/go-fuse/v2/fs"
|
||||||
|
|
||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Will a write to plaintext offset "targetOff" create a file hole in the
|
// Will a write to plaintext offset "targetOff" create a file hole in the
|
||||||
// ciphertext? If yes, zero-pad the last ciphertext block.
|
// ciphertext? If yes, zero-pad the last ciphertext block.
|
||||||
func (f *File2) writePadHole(targetOff int64) fuse.Status {
|
func (f *File2) writePadHole(targetOff int64) syscall.Errno {
|
||||||
// Get the current file size.
|
// Get the current file size.
|
||||||
fi, err := f.fd.Stat()
|
fi, err := f.fd.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Warn.Printf("checkAndPadHole: Fstat failed: %v", err)
|
tlog.Warn.Printf("checkAndPadHole: Fstat failed: %v", err)
|
||||||
return fuse.ToStatus(err)
|
return fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
plainSize := f.contentEnc.CipherSizeToPlainSize(uint64(fi.Size()))
|
plainSize := f.contentEnc.CipherSizeToPlainSize(uint64(fi.Size()))
|
||||||
// Appending a single byte to the file (equivalent to writing to
|
// Appending a single byte to the file (equivalent to writing to
|
||||||
@ -29,31 +29,31 @@ func (f *File2) writePadHole(targetOff int64) fuse.Status {
|
|||||||
// The write goes into an existing block or (if the last block was full)
|
// The write goes into an existing block or (if the last block was full)
|
||||||
// starts a new one directly after the last block. Nothing to do.
|
// starts a new one directly after the last block. Nothing to do.
|
||||||
if targetBlock <= nextBlock {
|
if targetBlock <= nextBlock {
|
||||||
return fuse.OK
|
return 0
|
||||||
}
|
}
|
||||||
// The write goes past the next block. nextBlock has
|
// The write goes past the next block. nextBlock has
|
||||||
// to be zero-padded to the block boundary and (at least) nextBlock+1
|
// to be zero-padded to the block boundary and (at least) nextBlock+1
|
||||||
// will contain a file hole in the ciphertext.
|
// will contain a file hole in the ciphertext.
|
||||||
status := f.zeroPad(plainSize)
|
errno := f.zeroPad(plainSize)
|
||||||
if status != fuse.OK {
|
if errno != 0 {
|
||||||
return status
|
return errno
|
||||||
}
|
}
|
||||||
return fuse.OK
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero-pad the file of size plainSize to the next block boundary. This is a no-op
|
// Zero-pad the file of size plainSize to the next block boundary. This is a no-op
|
||||||
// if the file is already block-aligned.
|
// if the file is already block-aligned.
|
||||||
func (f *File2) zeroPad(plainSize uint64) fuse.Status {
|
func (f *File2) zeroPad(plainSize uint64) syscall.Errno {
|
||||||
lastBlockLen := plainSize % f.contentEnc.PlainBS()
|
lastBlockLen := plainSize % f.contentEnc.PlainBS()
|
||||||
if lastBlockLen == 0 {
|
if lastBlockLen == 0 {
|
||||||
// Already block-aligned
|
// Already block-aligned
|
||||||
return fuse.OK
|
return 0
|
||||||
}
|
}
|
||||||
missing := f.contentEnc.PlainBS() - lastBlockLen
|
missing := f.contentEnc.PlainBS() - lastBlockLen
|
||||||
pad := make([]byte, missing)
|
pad := make([]byte, missing)
|
||||||
tlog.Debug.Printf("zeroPad: Writing %d bytes\n", missing)
|
tlog.Debug.Printf("zeroPad: Writing %d bytes\n", missing)
|
||||||
_, status := f.doWrite(pad, int64(plainSize))
|
_, errno := f.doWrite(pad, int64(plainSize))
|
||||||
return status
|
return errno
|
||||||
}
|
}
|
||||||
|
|
||||||
// SeekData calls the lseek syscall with SEEK_DATA. It returns the offset of the
|
// SeekData calls the lseek syscall with SEEK_DATA. It returns the offset of the
|
||||||
|
@ -286,7 +286,7 @@ func (n *Node) Setattr(ctx context.Context, f fs.FileHandle, in *fuse.SetAttrIn,
|
|||||||
return errno
|
return errno
|
||||||
}
|
}
|
||||||
f2 = f.(*File2)
|
f2 = f.(*File2)
|
||||||
defer f2.Release()
|
defer f2.Release(ctx)
|
||||||
}
|
}
|
||||||
return f2.Setattr(ctx, in, out)
|
return f2.Setattr(ctx, in, out)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user