Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0489d08

Browse files
committed
fusefrontend: get the file ID from the open files table
This fixes the problem that a truncate can reset the fileID without the other open FDs noticing it.
1 parente04dc05 commit0489d08

File tree

3 files changed

+106
-84
lines changed

3 files changed

+106
-84
lines changed

‎internal/fusefrontend/file.go‎

Lines changed: 65 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ type file struct {
4040
contentEnc*contentenc.ContentEnc
4141
// Device and inode number uniquely identify the backing file
4242
devInoDevInoStruct
43-
//File header
44-
header*contentenc.FileHeader
43+
//Entry in the open file map
44+
fileTableEntry*openFileEntryT
4545
// go-fuse nodefs.loopbackFile
4646
loopbackFile nodefs.File
47-
// Storewhat the last byte was written
47+
// Storewhere the last byte was written
4848
lastWrittenOffsetint64
4949
// The opCount is used to judge whether "lastWrittenOffset" is still
5050
// guaranteed to be correct.
@@ -60,14 +60,15 @@ func NewFile(fd *os.File, writeOnly bool, contentEnc *contentenc.ContentEnc) (no
6060
returnnil,fuse.ToStatus(err)
6161
}
6262
di:=DevInoFromStat(&st)
63-
wlock.register(di)
63+
t:=openFileMap.register(di)
6464

6565
return&file{
66-
fd:fd,
67-
writeOnly:writeOnly,
68-
contentEnc:contentEnc,
69-
devIno:di,
70-
loopbackFile:nodefs.NewLoopbackFile(fd),
66+
fd:fd,
67+
writeOnly:writeOnly,
68+
contentEnc:contentEnc,
69+
devIno:di,
70+
fileTableEntry:t,
71+
loopbackFile:nodefs.NewLoopbackFile(fd),
7172
},fuse.OK
7273
}
7374

@@ -84,44 +85,39 @@ func (f *file) InnerFile() nodefs.File {
8485
func (f*file)SetInode(n*nodefs.Inode) {
8586
}
8687

87-
// readHeader - load the file header from disk
88-
//
89-
// Returns io.EOF if the file is empty
90-
func (f*file)readHeader()error {
88+
// readFileID loads the file header from disk and extracts the file ID.
89+
// Returns io.EOF if the file is empty.
90+
func (f*file)readFileID() ([]byte,error) {
9191
buf:=make([]byte,contentenc.HeaderLen)
9292
_,err:=f.fd.ReadAt(buf,0)
9393
iferr!=nil {
94-
returnerr
94+
returnnil,err
9595
}
9696
h,err:=contentenc.ParseHeader(buf)
9797
iferr!=nil {
98-
returnerr
98+
returnnil,err
9999
}
100-
f.header=h
101-
102-
returnnil
100+
returnh.ID,nil
103101
}
104102

105-
// createHeader - create a new random header and write it to disk
106-
func (f*file)createHeader()error {
103+
// createHeader creates a new random header and writes it to disk.
104+
// Returns the new file ID.
105+
// The caller must hold fileIDLock.Lock().
106+
func (f*file)createHeader() (fileID []byte,errerror) {
107107
h:=contentenc.RandomHeader()
108108
buf:=h.Pack()
109-
110109
// Prevent partially written (=corrupt) header by preallocating the space beforehand
111-
err:=syscallcompat.EnospcPrealloc(int(f.fd.Fd()),0,contentenc.HeaderLen)
110+
err=syscallcompat.EnospcPrealloc(int(f.fd.Fd()),0,contentenc.HeaderLen)
112111
iferr!=nil {
113112
tlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n",f.devIno.ino,err.Error())
114-
returnerr
113+
returnnil,err
115114
}
116-
117115
// Actually write header
118116
_,err=f.fd.WriteAt(buf,0)
119117
iferr!=nil {
120-
returnerr
118+
returnnil,err
121119
}
122-
f.header=h
123-
124-
returnnil
120+
returnh.ID,err
125121
}
126122

127123
func (f*file)String()string {
@@ -137,25 +133,39 @@ func (f *file) String() string {
137133
// Called by Read() for normal reading,
138134
// by Write() and Truncate() for Read-Modify-Write
139135
func (f*file)doRead(offuint64,lengthuint64) ([]byte, fuse.Status) {
140-
141-
// Read file header
142-
iff.header==nil {
143-
err:=f.readHeader()
136+
// Make sure we have the file ID.
137+
f.fileTableEntry.IDLock.RLock()
138+
iff.fileTableEntry.ID==nil {
139+
f.fileTableEntry.IDLock.RUnlock()
140+
// Yes, somebody else may take the lock before we can. This will get
141+
// the header read twice, but causes no harm otherwise.
142+
f.fileTableEntry.IDLock.Lock()
143+
tmpID,err:=f.readFileID()
144144
iferr==io.EOF {
145+
f.fileTableEntry.IDLock.Unlock()
145146
returnnil,fuse.OK
146147
}
147148
iferr!=nil {
149+
f.fileTableEntry.IDLock.Unlock()
148150
returnnil,fuse.ToStatus(err)
149151
}
152+
f.fileTableEntry.ID=tmpID
153+
// Downgrade the lock.
154+
f.fileTableEntry.IDLock.Unlock()
155+
// The file ID may change in here. This does no harm because we
156+
// re-read it after the RLock().
157+
f.fileTableEntry.IDLock.RLock()
150158
}
151-
159+
fileID:=f.fileTableEntry.ID
152160
// Read the backing ciphertext in one go
153161
blocks:=f.contentEnc.ExplodePlainRange(off,length)
154162
alignedOffset,alignedLength:=blocks[0].JointCiphertextRange(blocks)
155163
skip:=blocks[0].Skip
156164
tlog.Debug.Printf("JointCiphertextRange(%d, %d) -> %d, %d, %d",off,length,alignedOffset,alignedLength,skip)
157165
ciphertext:=make([]byte,int(alignedLength))
158166
n,err:=f.fd.ReadAt(ciphertext,int64(alignedOffset))
167+
// We don't care if the file ID changes after we have read the data. Drop the lock.
168+
f.fileTableEntry.IDLock.RUnlock()
159169
iferr!=nil&&err!=io.EOF {
160170
tlog.Warn.Printf("read: ReadAt: %s",err.Error())
161171
returnnil,fuse.ToStatus(err)
@@ -167,7 +177,7 @@ func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) {
167177
tlog.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d",alignedOffset,firstBlockNo,alignedLength,n)
168178

169179
// Decrypt it
170-
plaintext,err:=f.contentEnc.DecryptBlocks(ciphertext,firstBlockNo,f.header.ID)
180+
plaintext,err:=f.contentEnc.DecryptBlocks(ciphertext,firstBlockNo,fileID)
171181
iferr!=nil {
172182
curruptBlockNo:=firstBlockNo+f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext)))
173183
tlog.Warn.Printf("ino%d: doRead: corrupt block #%d: %v",f.devIno.ino,curruptBlockNo,err)
@@ -223,18 +233,28 @@ func (f *file) Read(buf []byte, off int64) (resultData fuse.ReadResult, code fus
223233
//
224234
// Empty writes do nothing and are allowed.
225235
func (f*file)doWrite(data []byte,offint64) (uint32, fuse.Status) {
226-
227236
// Read header from disk, create a new one if the file is empty
228-
iff.header==nil {
229-
err:=f.readHeader()
237+
f.fileTableEntry.IDLock.RLock()
238+
iff.fileTableEntry.ID==nil {
239+
f.fileTableEntry.IDLock.RUnlock()
240+
// Somebody else may write the header here, but this would do no harm.
241+
f.fileTableEntry.IDLock.Lock()
242+
tmpID,err:=f.readFileID()
230243
iferr==io.EOF {
231-
err=f.createHeader()
232-
244+
tmpID,err=f.createHeader()
233245
}
234246
iferr!=nil {
247+
f.fileTableEntry.IDLock.Unlock()
235248
return0,fuse.ToStatus(err)
236249
}
250+
f.fileTableEntry.ID=tmpID
251+
f.fileTableEntry.IDLock.Unlock()
252+
// The file ID may change in here. This does no harm because we
253+
// re-read it after the RLock().
254+
f.fileTableEntry.IDLock.RLock()
237255
}
256+
fileID:=f.fileTableEntry.ID
257+
deferf.fileTableEntry.IDLock.RUnlock()
238258

239259
varwrittenuint32
240260
status:=fuse.OK
@@ -261,7 +281,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
261281

262282
// Encrypt
263283
blockOffset:=b.BlockCipherOff()
264-
blockData=f.contentEnc.EncryptBlock(blockData,b.BlockNo,f.header.ID)
284+
blockData=f.contentEnc.EncryptBlock(blockData,b.BlockNo,fileID)
265285
tlog.Debug.Printf("ino%d: Writing %d bytes to block #%d",
266286
f.devIno.ino,uint64(len(blockData))-f.contentEnc.BlockOverhead(),b.BlockNo)
267287

@@ -292,7 +312,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
292312
// Stat() call is very expensive.
293313
// The caller must "wlock.lock(f.devIno.ino)" otherwise this check would be racy.
294314
func (f*file)isConsecutiveWrite(offint64)bool {
295-
opCount:=atomic.LoadUint64(&wlock.opCount)
315+
opCount:=atomic.LoadUint64(&openFileMap.opCount)
296316
returnopCount==f.lastOpCount+1&&off==f.lastWrittenOffset+1
297317
}
298318

@@ -309,8 +329,8 @@ func (f *file) Write(data []byte, off int64) (uint32, fuse.Status) {
309329
tlog.Warn.Printf("ino%d fh%d: Write on released file",f.devIno.ino,f.intFd())
310330
return0,fuse.EBADF
311331
}
312-
wlock.lock(f.devIno)
313-
deferwlock.unlock(f.devIno)
332+
f.fileTableEntry.writeLock.Lock()
333+
deferf.fileTableEntry.writeLock.Unlock()
314334
tlog.Debug.Printf("ino%d: FUSE Write: offset=%d length=%d",f.devIno.ino,off,len(data))
315335
// If the write creates a file hole, we have to zero-pad the last block.
316336
// But if the write directly follows an earlier write, it cannot create a
@@ -323,7 +343,7 @@ func (f *file) Write(data []byte, off int64) (uint32, fuse.Status) {
323343
}
324344
n,status:=f.doWrite(data,off)
325345
ifstatus.Ok() {
326-
f.lastOpCount=atomic.LoadUint64(&wlock.opCount)
346+
f.lastOpCount=atomic.LoadUint64(&openFileMap.opCount)
327347
f.lastWrittenOffset=off+int64(len(data))-1
328348
}
329349
returnn,status
@@ -339,7 +359,7 @@ func (f *file) Release() {
339359
f.released=true
340360
f.fdLock.Unlock()
341361

342-
wlock.unregister(f.devIno)
362+
openFileMap.unregister(f.devIno)
343363
}
344364

345365
// Flush - FUSE call

‎internal/fusefrontend/file_allocate_truncate.go‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ func (f *file) Allocate(off uint64, sz uint64, mode uint32) fuse.Status {
5050
iff.released {
5151
returnfuse.EBADF
5252
}
53-
wlock.lock(f.devIno)
54-
deferwlock.unlock(f.devIno)
53+
f.fileTableEntry.writeLock.Lock()
54+
deferf.fileTableEntry.writeLock.Unlock()
5555

5656
blocks:=f.contentEnc.ExplodePlainRange(off,sz)
5757
firstBlock:=blocks[0]
@@ -100,8 +100,8 @@ func (f *file) Truncate(newSize uint64) fuse.Status {
100100
tlog.Warn.Printf("ino%d fh%d: Truncate on released file",f.devIno.ino,f.intFd())
101101
returnfuse.EBADF
102102
}
103-
wlock.lock(f.devIno)
104-
deferwlock.unlock(f.devIno)
103+
f.fileTableEntry.writeLock.Lock()
104+
deferf.fileTableEntry.writeLock.Unlock()
105105
varerrerror
106106
// Common case first: Truncate to zero
107107
ifnewSize==0 {
@@ -111,7 +111,9 @@ func (f *file) Truncate(newSize uint64) fuse.Status {
111111
returnfuse.ToStatus(err)
112112
}
113113
// Truncate to zero kills the file header
114-
f.header=nil
114+
f.fileTableEntry.IDLock.Lock()
115+
f.fileTableEntry.ID=nil
116+
f.fileTableEntry.IDLock.Unlock()
115117
returnfuse.OK
116118
}
117119
// We need the old file size to determine if we are growing or shrinking
@@ -184,7 +186,9 @@ func (f *file) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) fuse.Statu
184186
varerrerror
185187
// File was empty, create new header
186188
ifoldPlainSz==0 {
187-
err=f.createHeader()
189+
f.fileTableEntry.IDLock.Lock()
190+
_,err=f.createHeader()
191+
f.fileTableEntry.IDLock.Unlock()
188192
iferr!=nil {
189193
returnfuse.ToStatus(err)
190194
}

‎internal/fusefrontend/write_lock.go‎

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -22,81 +22,79 @@ func DevInoFromStat(st *syscall.Stat_t) DevInoStruct {
2222
}
2323

2424
funcinit() {
25-
wlock.inodeLocks=make(map[DevInoStruct]*refCntMutex)
25+
openFileMap.entries=make(map[DevInoStruct]*openFileEntryT)
2626
}
2727

2828
// wlock - serializes write accesses to each file (identified by inode number)
2929
// Writing partial blocks means we have to do read-modify-write cycles. We
3030
// really don't want concurrent writes there.
3131
// Concurrent full-block writes could actually be allowed, but are not to
3232
// keep the locking simple.
33-
varwlockwlockMap
33+
varopenFileMapopenFileMapT
3434

3535
// wlockMap - usage:
3636
// 1) register
3737
// 2) lock ... unlock ...
3838
// 3) unregister
39-
typewlockMapstruct {
40-
// opCount countslock() calls. As every operation that modifies a file should
39+
typeopenFileMapTstruct {
40+
// opCount countswriteLock.Lock() calls. As every operation that modifies a file should
4141
// call it, this effectively serves as a write-operation counter.
4242
// The variable is accessed without holding any locks so atomic operations
4343
// must be used. It must be the first element of the struct to guarantee
4444
// 64-bit alignment.
4545
opCountuint64
4646
// Protects map access
4747
sync.Mutex
48-
inodeLocksmap[DevInoStruct]*refCntMutex
48+
entriesmap[DevInoStruct]*openFileEntryT
4949
}
5050

51-
// refCntMutex - mutex with reference count
52-
typerefCntMutexstruct {
53-
// Write lock for this inode
51+
typeopCountMutexstruct {
5452
sync.Mutex
53+
// Points to the opCount variable of the parent openFileMapT
54+
opCount*uint64
55+
}
56+
57+
func (o*opCountMutex)Lock() {
58+
o.Mutex.Lock()
59+
atomic.AddUint64(o.opCount,1)
60+
}
61+
62+
// refCntMutex - mutex with reference count
63+
typeopenFileEntryTstruct {
5564
// Reference count
5665
refCntint
66+
// Write lock for this inode
67+
writeLock*opCountMutex
68+
// ID is the file ID in the file header.
69+
ID []byte
70+
IDLock sync.RWMutex
5771
}
5872

5973
// register creates an entry for "ino", or incrementes the reference count
6074
// if the entry already exists.
61-
func (w*wlockMap)register(diDevInoStruct) {
75+
func (w*openFileMapT)register(diDevInoStruct)*openFileEntryT {
6276
w.Lock()
6377
deferw.Unlock()
6478

65-
r:=w.inodeLocks[di]
79+
r:=w.entries[di]
6680
ifr==nil {
67-
r=&refCntMutex{}
68-
w.inodeLocks[di]=r
81+
o:=opCountMutex{opCount:&w.opCount}
82+
r=&openFileEntryT{writeLock:&o}
83+
w.entries[di]=r
6984
}
7085
r.refCnt++
86+
returnr
7187
}
7288

7389
// unregister decrements the reference count for "di" and deletes the entry if
7490
// the reference count has reached 0.
75-
func (w*wlockMap)unregister(diDevInoStruct) {
91+
func (w*openFileMapT)unregister(diDevInoStruct) {
7692
w.Lock()
7793
deferw.Unlock()
7894

79-
r:=w.inodeLocks[di]
95+
r:=w.entries[di]
8096
r.refCnt--
8197
ifr.refCnt==0 {
82-
delete(w.inodeLocks,di)
98+
delete(w.entries,di)
8399
}
84100
}
85-
86-
// lock retrieves the entry for "di" and locks it.
87-
func (w*wlockMap)lock(diDevInoStruct) {
88-
atomic.AddUint64(&w.opCount,1)
89-
w.Lock()
90-
r:=w.inodeLocks[di]
91-
w.Unlock()
92-
// this can take a long time - execute outside the wlockMap lock
93-
r.Lock()
94-
}
95-
96-
// unlock retrieves the entry for "di" and unlocks it.
97-
func (w*wlockMap)unlock(diDevInoStruct) {
98-
w.Lock()
99-
r:=w.inodeLocks[di]
100-
w.Unlock()
101-
r.Unlock()
102-
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp