Fix race condition when registering files

This commit is contained in:
Matéo Duparc 2022-04-22 12:07:12 +02:00
parent 985d852343
commit 9e98192442
Signed by: hardcoresushi
GPG Key ID: AFE384344A45E13A
2 changed files with 47 additions and 55 deletions

85
file.go
View File

@ -38,20 +38,21 @@ func mangleOpenFlags(flags uint32) (newFlags int) {
} }
func (volume *Volume) registerFileHandle(fd int, cName, path string) int { func (volume *Volume) registerFileHandle(fd int, cName, path string) int {
handleID := -1 volume.handlesLock.Lock()
c := 0 c := 0
for handleID == -1 { for {
_, ok := volume.file_handles.Load(c) _, ok := volume.fileHandles[c]
if !ok { if !ok {
handleID = c break
} }
c++ c++
} }
volume.file_handles.Store(handleID, &File { volume.fileHandles[c] = &File {
fd: os.NewFile(uintptr(fd), cName), fd: os.NewFile(uintptr(fd), cName),
path: string([]byte(path[:])), path: string([]byte(path[:])),
}) }
return handleID volume.handlesLock.Unlock()
return c
} }
// readFileID loads the file header from disk and extracts the file ID. // readFileID loads the file header from disk and extracts the file ID.
@ -102,12 +103,7 @@ func createHeader(fd *os.File) (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 (volume *Volume) doRead(handleID int, dst []byte, off uint64, length uint64) ([]byte, bool) { func (volume *Volume) doRead(f *File, dst []byte, off uint64, length uint64) ([]byte, bool) {
value, ok := volume.file_handles.Load(handleID)
if !ok {
return nil, false
}
f := value.(*File)
fd := f.fd fd := f.fd
// 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
@ -183,11 +179,9 @@ func (volume *Volume) doRead(handleID int, dst []byte, off uint64, length uint64
// //
// Empty writes do nothing and are allowed. // Empty writes do nothing and are allowed.
func (volume *Volume) doWrite(handleID int, data []byte, off uint64) (uint32, bool) { func (volume *Volume) doWrite(handleID int, data []byte, off uint64) (uint32, bool) {
value, ok := volume.file_handles.Load(handleID) volume.handlesLock.RLock()
if !ok { f := volume.fileHandles[handleID]
return 0, false volume.handlesLock.RUnlock()
}
f := value.(*File)
fd := f.fd fd := f.fd
fileWasEmpty := false fileWasEmpty := false
var fileID []byte var fileID []byte
@ -216,7 +210,7 @@ func (volume *Volume) doWrite(handleID int, data []byte, off uint64) (uint32, bo
// Incomplete block -> Read-Modify-Write // Incomplete block -> Read-Modify-Write
if b.IsPartial() { if b.IsPartial() {
// Read // Read
oldData, success := volume.doRead(handleID, nil, b.BlockPlainOff(), volume.contentEnc.PlainBS()) oldData, success := volume.doRead(f, nil, b.BlockPlainOff(), volume.contentEnc.PlainBS())
if !success { if !success {
return 0, false return 0, false
} }
@ -299,11 +293,9 @@ func (volume *Volume) truncateGrowFile(handleID int, oldPlainSz uint64, newPlain
// 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.
if newPlainSz%volume.contentEnc.PlainBS() == 0 { if newPlainSz%volume.contentEnc.PlainBS() == 0 {
value, ok := volume.file_handles.Load(handleID) volume.handlesLock.RLock()
if !ok { f := volume.fileHandles[handleID]
return false volume.handlesLock.RUnlock()
}
f := value.(*File)
// The file was empty, so it did not have a header. Create one. // The file was empty, so it did not have a header. Create one.
if oldPlainSz == 0 { if oldPlainSz == 0 {
id, err := createHeader(f.fd) id, err := createHeader(f.fd)
@ -324,11 +316,9 @@ func (volume *Volume) truncateGrowFile(handleID int, oldPlainSz uint64, newPlain
} }
func (volume *Volume) truncate(handleID int, newSize uint64) bool { func (volume *Volume) truncate(handleID int, newSize uint64) bool {
value, ok := volume.file_handles.Load(handleID) volume.handlesLock.RLock()
if !ok { f := volume.fileHandles[handleID]
return false volume.handlesLock.RUnlock()
}
f := value.(*File)
fileFD := int(f.fd.Fd()) fileFD := int(f.fd.Fd())
var err error var err error
// Common case first: Truncate to zero // Common case first: Truncate to zero
@ -359,7 +349,7 @@ func (volume *Volume) truncate(handleID int, newSize uint64) bool {
lastBlockLen := newSize - plainOff lastBlockLen := newSize - plainOff
var data []byte var data []byte
if lastBlockLen > 0 { if lastBlockLen > 0 {
data, success = volume.doRead(handleID, nil, plainOff, lastBlockLen) data, success = volume.doRead(f, nil, plainOff, lastBlockLen)
if !success { if !success {
return false return false
} }
@ -459,16 +449,14 @@ func gcf_read_file(sessionID, handleID int, offset uint64, dst_buff []byte) uint
} }
volume := value.(*Volume) volume := value.(*Volume)
value, ok = volume.file_handles.Load(handleID) volume.handlesLock.RLock()
if !ok { f := volume.fileHandles[handleID]
return 0 volume.handlesLock.RUnlock()
}
f := value.(*File)
f.fdLock.RLock() f.fdLock.RLock()
defer f.fdLock.RUnlock() defer f.fdLock.RUnlock()
f.contentLock.RLock() f.contentLock.RLock()
defer f.contentLock.RUnlock() defer f.contentLock.RUnlock()
out, success := volume.doRead(handleID, dst_buff[:0], offset, uint64(length)) out, success := volume.doRead(f, dst_buff[:0], offset, uint64(length))
if !success { if !success {
return 0 return 0
} else { } else {
@ -490,13 +478,11 @@ func gcf_write_file(sessionID, handleID int, offset uint64, data []byte) uint32
} }
volume := value.(*Volume) volume := value.(*Volume)
value, ok = volume.file_handles.Load(handleID) volume.handlesLock.RLock()
if !ok { f := volume.fileHandles[handleID]
return 0 volume.handlesLock.RUnlock()
}
f := value.(*File)
f.fdLock.RLock() f.fdLock.RLock()
defer f.fdLock.RUnlock() defer f.fdLock.RUnlock()
f.contentLock.Lock() f.contentLock.Lock()
defer f.contentLock.Unlock() defer f.contentLock.Unlock()
n, _ := volume.doWrite(handleID, data, offset) n, _ := volume.doWrite(handleID, data, offset)
@ -510,14 +496,13 @@ func gcf_close_file(sessionID, handleID int) {
return return
} }
volume := value.(*Volume) volume := value.(*Volume)
value, ok = volume.file_handles.Load(handleID) volume.handlesLock.Lock()
if ok { f := volume.fileHandles[handleID]
f := value.(*File) f.fdLock.Lock()
f.fdLock.Lock() f.fd.Close()
f.fd.Close() delete(volume.fileHandles, handleID)
volume.file_handles.Delete(handleID) volume.handlesLock.Unlock()
f.fdLock.Unlock() f.fdLock.Unlock()
}
} }
//export gcf_remove_file //export gcf_remove_file

View File

@ -44,7 +44,8 @@ type Volume struct {
cryptoCore *cryptocore.CryptoCore cryptoCore *cryptocore.CryptoCore
contentEnc *contentenc.ContentEnc contentEnc *contentenc.ContentEnc
dirCache dirCache dirCache dirCache
file_handles sync.Map handlesLock sync.RWMutex
fileHandles map[int]*File
} }
var OpenedVolumes sync.Map var OpenedVolumes sync.Map
@ -93,6 +94,7 @@ func registerNewVolume(rootCipherDir string, masterkey []byte, cf *configfile.Co
ivLen = 0 ivLen = 0
} }
newVolume.dirCache = dirCache{ivLen: ivLen} newVolume.dirCache = dirCache{ivLen: ivLen}
newVolume.fileHandles = make(map[int]*File)
//find unused volumeID //find unused volumeID
volumeID := -1 volumeID := -1
@ -130,10 +132,15 @@ func gcf_close(volumeID int) {
} }
volume := value.(*Volume) volume := value.(*Volume)
volume.cryptoCore.Wipe() volume.cryptoCore.Wipe()
volume.file_handles.Range(func (handleID, _ interface {}) bool { volume.handlesLock.RLock()
gcf_close_file(volumeID, handleID.(int)) fileHandles := make([]int, 0, len(volume.fileHandles))
return true for i := range volume.fileHandles {
}) fileHandles = append(fileHandles, i)
}
volume.handlesLock.RUnlock()
for i := range fileHandles {
gcf_close_file(volumeID, i)
}
volume.dirCache.Clear() volume.dirCache.Clear()
OpenedVolumes.Delete(volumeID) OpenedVolumes.Delete(volumeID)
} }