aboutsummaryrefslogtreecommitdiff
path: root/internal/fusefrontend/file.go
diff options
context:
space:
mode:
authorJakob Unterwurzacher2023-06-09 14:34:20 +0200
committerJakob Unterwurzacher2024-12-13 21:21:15 +0100
commit321471f513646663fcb6b9037dd85ffef52f558a (patch)
treecd1a5968b219e3a7455805a221a408ecd5d3d12b /internal/fusefrontend/file.go
parent57bdab71237f76d1dd0bbb067e66076f970327d1 (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/file.go')
-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