aboutsummaryrefslogtreecommitdiff
path: root/internal/fusefrontend
diff options
context:
space:
mode:
authorJakob Unterwurzacher2023-06-09 14:34:20 +0200
committerJakob Unterwurzacher2024-12-04 19:53:15 +0100
commit504dbe9f2df8ed0e136bd60539226f7ab6fc7e41 (patch)
tree65e450d33b91429c4528c9cfb267220d0ad0d52b /internal/fusefrontend
parent29fa6941c74456d109faa63107dd086a5f7ffd36 (diff)
fusefrontend: sharedstorage: add file content byte-range locks
As we must write complete ciphertext blocks (except at EOF), non-overlapping plaintext writes can overlap in the ciphertext. And because overlapping writes can turn the data into data soup (see TestPoCTornWrite) we serialize them using fcntl locking.
Diffstat (limited to 'internal/fusefrontend')
-rw-r--r--internal/fusefrontend/file.go14
1 files changed, 13 insertions, 1 deletions
diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go
index 5be0872..abb675c 100644
--- a/internal/fusefrontend/file.go
+++ b/internal/fusefrontend/file.go
@@ -277,6 +277,7 @@ func (f *File) doWrite(data []byte, off int64) (uint32, syscall.Errno) {
// If the file ID is not cached, read it from disk
if f.fileTableEntry.ID == nil {
if err := f.LockSharedStorage(unix.F_WRLCK, 0, contentenc.HeaderLen); err != nil {
+ tlog.Warn.Printf("ino%d: LockSharedStorage(F_WRLCK, %d, %d)failed: %v", 0, f.qIno.Ino, contentenc.HeaderLen, err)
return 0, fs.ToErrno(err)
}
var err error
@@ -302,7 +303,19 @@ func (f *File) doWrite(data []byte, off int64) (uint32, syscall.Errno) {
// Handle payload data
dataBuf := bytes.NewBuffer(data)
blocks := f.contentEnc.ExplodePlainRange(uint64(off), uint64(len(data)))
+ cOff, lkLen := blocks[0].JointCiphertextRange(blocks)
toEncrypt := make([][]byte, len(blocks))
+
+ // As we must write complete ciphertext blocks (except at EOF), non-overlapping
+ // plaintext writes can overlap in the ciphertext.
+ // And because overlapping writes can turn the data into data soup (see
+ // TestPoCTornWrite) we serialize them using fcntl locking.
+ if err := f.LockSharedStorage(unix.F_WRLCK, int64(cOff), int64(lkLen)); err != nil {
+ tlog.Warn.Printf("ino%d: LockSharedStorage(F_WRLCK, %d, %d) failed: %v", f.qIno.Ino, cOff, int64(lkLen), err)
+ return 0, fs.ToErrno(err)
+ }
+ defer f.LockSharedStorage(unix.F_UNLCK, int64(cOff), int64(lkLen))
+
for i, b := range blocks {
blockData := dataBuf.Next(int(b.Length))
// Incomplete block -> Read-Modify-Write
@@ -327,7 +340,6 @@ func (f *File) doWrite(data []byte, off int64) (uint32, syscall.Errno) {
// Preallocate so we cannot run out of space in the middle of the write.
// This prevents partially written (=corrupt) blocks.
var err error
- cOff := blocks[0].BlockCipherOff()
// f.fd.WriteAt & syscallcompat.EnospcPrealloc take int64 offsets!
if cOff > math.MaxInt64 {
return 0, syscall.EFBIG