fusefrontend: get the file ID from the open files table
This fixes the problem that a truncate can reset the file ID without the other open FDs noticing it.
This commit is contained in:
parent
e04dc05012
commit
0489d08ae2
@ -40,11 +40,11 @@ type file struct {
|
|||||||
contentEnc *contentenc.ContentEnc
|
contentEnc *contentenc.ContentEnc
|
||||||
// Device and inode number uniquely identify the backing file
|
// Device and inode number uniquely identify the backing file
|
||||||
devIno DevInoStruct
|
devIno DevInoStruct
|
||||||
// File header
|
// Entry in the open file map
|
||||||
header *contentenc.FileHeader
|
fileTableEntry *openFileEntryT
|
||||||
// go-fuse nodefs.loopbackFile
|
// go-fuse nodefs.loopbackFile
|
||||||
loopbackFile nodefs.File
|
loopbackFile nodefs.File
|
||||||
// Store what the last byte was written
|
// Store where the last byte was written
|
||||||
lastWrittenOffset int64
|
lastWrittenOffset int64
|
||||||
// The opCount is used to judge whether "lastWrittenOffset" is still
|
// The opCount is used to judge whether "lastWrittenOffset" is still
|
||||||
// guaranteed to be correct.
|
// guaranteed to be correct.
|
||||||
@ -60,13 +60,14 @@ func NewFile(fd *os.File, writeOnly bool, contentEnc *contentenc.ContentEnc) (no
|
|||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
di := DevInoFromStat(&st)
|
di := DevInoFromStat(&st)
|
||||||
wlock.register(di)
|
t := openFileMap.register(di)
|
||||||
|
|
||||||
return &file{
|
return &file{
|
||||||
fd: fd,
|
fd: fd,
|
||||||
writeOnly: writeOnly,
|
writeOnly: writeOnly,
|
||||||
contentEnc: contentEnc,
|
contentEnc: contentEnc,
|
||||||
devIno: di,
|
devIno: di,
|
||||||
|
fileTableEntry: t,
|
||||||
loopbackFile: nodefs.NewLoopbackFile(fd),
|
loopbackFile: nodefs.NewLoopbackFile(fd),
|
||||||
}, fuse.OK
|
}, fuse.OK
|
||||||
}
|
}
|
||||||
@ -84,44 +85,39 @@ func (f *file) InnerFile() nodefs.File {
|
|||||||
func (f *file) SetInode(n *nodefs.Inode) {
|
func (f *file) SetInode(n *nodefs.Inode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// readHeader - load the file header from disk
|
// readFileID loads the file header from disk and extracts the file ID.
|
||||||
//
|
// Returns io.EOF if the file is empty.
|
||||||
// Returns io.EOF if the file is empty
|
func (f *file) readFileID() ([]byte, error) {
|
||||||
func (f *file) readHeader() error {
|
|
||||||
buf := make([]byte, contentenc.HeaderLen)
|
buf := make([]byte, contentenc.HeaderLen)
|
||||||
_, err := f.fd.ReadAt(buf, 0)
|
_, err := f.fd.ReadAt(buf, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
h, err := contentenc.ParseHeader(buf)
|
h, err := contentenc.ParseHeader(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
f.header = h
|
return h.ID, nil
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createHeader - create a new random header and write it to disk
|
// createHeader creates a new random header and writes it to disk.
|
||||||
func (f *file) createHeader() error {
|
// Returns the new file ID.
|
||||||
|
// The caller must hold fileIDLock.Lock().
|
||||||
|
func (f *file) createHeader() (fileID []byte, err error) {
|
||||||
h := contentenc.RandomHeader()
|
h := contentenc.RandomHeader()
|
||||||
buf := h.Pack()
|
buf := h.Pack()
|
||||||
|
|
||||||
// Prevent partially written (=corrupt) header by preallocating the space beforehand
|
// Prevent partially written (=corrupt) header by preallocating the space beforehand
|
||||||
err := syscallcompat.EnospcPrealloc(int(f.fd.Fd()), 0, contentenc.HeaderLen)
|
err = syscallcompat.EnospcPrealloc(int(f.fd.Fd()), 0, contentenc.HeaderLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.devIno.ino, err.Error())
|
tlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.devIno.ino, err.Error())
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually write header
|
// Actually write header
|
||||||
_, err = f.fd.WriteAt(buf, 0)
|
_, err = f.fd.WriteAt(buf, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
f.header = h
|
return h.ID, err
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *file) String() string {
|
func (f *file) String() string {
|
||||||
@ -137,18 +133,30 @@ func (f *file) String() string {
|
|||||||
// Called by Read() for normal reading,
|
// Called by Read() for normal reading,
|
||||||
// by Write() and Truncate() for Read-Modify-Write
|
// by Write() and Truncate() for Read-Modify-Write
|
||||||
func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) {
|
func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) {
|
||||||
|
// Make sure we have the file ID.
|
||||||
// Read file header
|
f.fileTableEntry.IDLock.RLock()
|
||||||
if f.header == nil {
|
if f.fileTableEntry.ID == nil {
|
||||||
err := f.readHeader()
|
f.fileTableEntry.IDLock.RUnlock()
|
||||||
|
// Yes, somebody else may take the lock before we can. This will get
|
||||||
|
// the header read twice, but causes no harm otherwise.
|
||||||
|
f.fileTableEntry.IDLock.Lock()
|
||||||
|
tmpID, err := f.readFileID()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
|
f.fileTableEntry.IDLock.Unlock()
|
||||||
return nil, fuse.OK
|
return nil, fuse.OK
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
f.fileTableEntry.IDLock.Unlock()
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
|
f.fileTableEntry.ID = tmpID
|
||||||
|
// Downgrade the lock.
|
||||||
|
f.fileTableEntry.IDLock.Unlock()
|
||||||
|
// The file ID may change in here. This does no harm because we
|
||||||
|
// re-read it after the RLock().
|
||||||
|
f.fileTableEntry.IDLock.RLock()
|
||||||
}
|
}
|
||||||
|
fileID := f.fileTableEntry.ID
|
||||||
// Read the backing ciphertext in one go
|
// Read the backing ciphertext in one go
|
||||||
blocks := f.contentEnc.ExplodePlainRange(off, length)
|
blocks := f.contentEnc.ExplodePlainRange(off, length)
|
||||||
alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks)
|
alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks)
|
||||||
@ -156,6 +164,8 @@ func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) {
|
|||||||
tlog.Debug.Printf("JointCiphertextRange(%d, %d) -> %d, %d, %d", off, length, alignedOffset, alignedLength, skip)
|
tlog.Debug.Printf("JointCiphertextRange(%d, %d) -> %d, %d, %d", off, length, alignedOffset, alignedLength, skip)
|
||||||
ciphertext := make([]byte, int(alignedLength))
|
ciphertext := make([]byte, int(alignedLength))
|
||||||
n, err := f.fd.ReadAt(ciphertext, int64(alignedOffset))
|
n, err := f.fd.ReadAt(ciphertext, int64(alignedOffset))
|
||||||
|
// We don't care if the file ID changes after we have read the data. Drop the lock.
|
||||||
|
f.fileTableEntry.IDLock.RUnlock()
|
||||||
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, fuse.ToStatus(err)
|
||||||
@ -167,7 +177,7 @@ func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) {
|
|||||||
tlog.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d", alignedOffset, firstBlockNo, alignedLength, n)
|
tlog.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d", alignedOffset, firstBlockNo, alignedLength, n)
|
||||||
|
|
||||||
// Decrypt it
|
// Decrypt it
|
||||||
plaintext, err := f.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, f.header.ID)
|
plaintext, err := f.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, fileID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext)))
|
curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext)))
|
||||||
tlog.Warn.Printf("ino%d: doRead: corrupt block #%d: %v", f.devIno.ino, curruptBlockNo, err)
|
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
|
|||||||
//
|
//
|
||||||
// Empty writes do nothing and are allowed.
|
// Empty writes do nothing and are allowed.
|
||||||
func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
||||||
|
|
||||||
// Read header from disk, create a new one if the file is empty
|
// Read header from disk, create a new one if the file is empty
|
||||||
if f.header == nil {
|
f.fileTableEntry.IDLock.RLock()
|
||||||
err := f.readHeader()
|
if f.fileTableEntry.ID == nil {
|
||||||
|
f.fileTableEntry.IDLock.RUnlock()
|
||||||
|
// Somebody else may write the header here, but this would do no harm.
|
||||||
|
f.fileTableEntry.IDLock.Lock()
|
||||||
|
tmpID, err := f.readFileID()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
err = f.createHeader()
|
tmpID, err = f.createHeader()
|
||||||
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
f.fileTableEntry.IDLock.Unlock()
|
||||||
return 0, fuse.ToStatus(err)
|
return 0, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
|
f.fileTableEntry.ID = tmpID
|
||||||
|
f.fileTableEntry.IDLock.Unlock()
|
||||||
|
// The file ID may change in here. This does no harm because we
|
||||||
|
// re-read it after the RLock().
|
||||||
|
f.fileTableEntry.IDLock.RLock()
|
||||||
}
|
}
|
||||||
|
fileID := f.fileTableEntry.ID
|
||||||
|
defer f.fileTableEntry.IDLock.RUnlock()
|
||||||
|
|
||||||
var written uint32
|
var written uint32
|
||||||
status := fuse.OK
|
status := fuse.OK
|
||||||
@ -261,7 +281,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
|
|
||||||
// Encrypt
|
// Encrypt
|
||||||
blockOffset := b.BlockCipherOff()
|
blockOffset := b.BlockCipherOff()
|
||||||
blockData = f.contentEnc.EncryptBlock(blockData, b.BlockNo, f.header.ID)
|
blockData = f.contentEnc.EncryptBlock(blockData, b.BlockNo, fileID)
|
||||||
tlog.Debug.Printf("ino%d: Writing %d bytes to block #%d",
|
tlog.Debug.Printf("ino%d: Writing %d bytes to block #%d",
|
||||||
f.devIno.ino, uint64(len(blockData))-f.contentEnc.BlockOverhead(), b.BlockNo)
|
f.devIno.ino, uint64(len(blockData))-f.contentEnc.BlockOverhead(), b.BlockNo)
|
||||||
|
|
||||||
@ -292,7 +312,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
// Stat() call is very expensive.
|
// Stat() call is very expensive.
|
||||||
// The caller must "wlock.lock(f.devIno.ino)" otherwise this check would be racy.
|
// The caller must "wlock.lock(f.devIno.ino)" otherwise this check would be racy.
|
||||||
func (f *file) isConsecutiveWrite(off int64) bool {
|
func (f *file) isConsecutiveWrite(off int64) bool {
|
||||||
opCount := atomic.LoadUint64(&wlock.opCount)
|
opCount := atomic.LoadUint64(&openFileMap.opCount)
|
||||||
return opCount == f.lastOpCount+1 && off == f.lastWrittenOffset+1
|
return opCount == f.lastOpCount+1 && off == f.lastWrittenOffset+1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,8 +329,8 @@ func (f *file) Write(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
tlog.Warn.Printf("ino%d fh%d: Write on released file", f.devIno.ino, f.intFd())
|
tlog.Warn.Printf("ino%d fh%d: Write on released file", f.devIno.ino, f.intFd())
|
||||||
return 0, fuse.EBADF
|
return 0, fuse.EBADF
|
||||||
}
|
}
|
||||||
wlock.lock(f.devIno)
|
f.fileTableEntry.writeLock.Lock()
|
||||||
defer wlock.unlock(f.devIno)
|
defer f.fileTableEntry.writeLock.Unlock()
|
||||||
tlog.Debug.Printf("ino%d: FUSE Write: offset=%d length=%d", f.devIno.ino, off, len(data))
|
tlog.Debug.Printf("ino%d: FUSE Write: offset=%d length=%d", f.devIno.ino, off, len(data))
|
||||||
// If the write creates a file hole, we have to zero-pad the last block.
|
// If the write creates a file hole, we have to zero-pad the last block.
|
||||||
// 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
|
||||||
@ -323,7 +343,7 @@ func (f *file) Write(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
}
|
}
|
||||||
n, status := f.doWrite(data, off)
|
n, status := f.doWrite(data, off)
|
||||||
if status.Ok() {
|
if status.Ok() {
|
||||||
f.lastOpCount = atomic.LoadUint64(&wlock.opCount)
|
f.lastOpCount = atomic.LoadUint64(&openFileMap.opCount)
|
||||||
f.lastWrittenOffset = off + int64(len(data)) - 1
|
f.lastWrittenOffset = off + int64(len(data)) - 1
|
||||||
}
|
}
|
||||||
return n, status
|
return n, status
|
||||||
@ -339,7 +359,7 @@ func (f *file) Release() {
|
|||||||
f.released = true
|
f.released = true
|
||||||
f.fdLock.Unlock()
|
f.fdLock.Unlock()
|
||||||
|
|
||||||
wlock.unregister(f.devIno)
|
openFileMap.unregister(f.devIno)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush - FUSE call
|
// Flush - FUSE call
|
||||||
|
@ -50,8 +50,8 @@ func (f *file) Allocate(off uint64, sz uint64, mode uint32) fuse.Status {
|
|||||||
if f.released {
|
if f.released {
|
||||||
return fuse.EBADF
|
return fuse.EBADF
|
||||||
}
|
}
|
||||||
wlock.lock(f.devIno)
|
f.fileTableEntry.writeLock.Lock()
|
||||||
defer wlock.unlock(f.devIno)
|
defer f.fileTableEntry.writeLock.Unlock()
|
||||||
|
|
||||||
blocks := f.contentEnc.ExplodePlainRange(off, sz)
|
blocks := f.contentEnc.ExplodePlainRange(off, sz)
|
||||||
firstBlock := blocks[0]
|
firstBlock := blocks[0]
|
||||||
@ -100,8 +100,8 @@ func (f *file) Truncate(newSize uint64) fuse.Status {
|
|||||||
tlog.Warn.Printf("ino%d fh%d: Truncate on released file", f.devIno.ino, f.intFd())
|
tlog.Warn.Printf("ino%d fh%d: Truncate on released file", f.devIno.ino, f.intFd())
|
||||||
return fuse.EBADF
|
return fuse.EBADF
|
||||||
}
|
}
|
||||||
wlock.lock(f.devIno)
|
f.fileTableEntry.writeLock.Lock()
|
||||||
defer wlock.unlock(f.devIno)
|
defer f.fileTableEntry.writeLock.Unlock()
|
||||||
var err error
|
var err error
|
||||||
// Common case first: Truncate to zero
|
// Common case first: Truncate to zero
|
||||||
if newSize == 0 {
|
if newSize == 0 {
|
||||||
@ -111,7 +111,9 @@ func (f *file) Truncate(newSize uint64) fuse.Status {
|
|||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
// Truncate to zero kills the file header
|
// Truncate to zero kills the file header
|
||||||
f.header = nil
|
f.fileTableEntry.IDLock.Lock()
|
||||||
|
f.fileTableEntry.ID = nil
|
||||||
|
f.fileTableEntry.IDLock.Unlock()
|
||||||
return fuse.OK
|
return fuse.OK
|
||||||
}
|
}
|
||||||
// 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
|
||||||
@ -184,7 +186,9 @@ func (f *file) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) fuse.Statu
|
|||||||
var err error
|
var err error
|
||||||
// File was empty, create new header
|
// File was empty, create new header
|
||||||
if oldPlainSz == 0 {
|
if oldPlainSz == 0 {
|
||||||
err = f.createHeader()
|
f.fileTableEntry.IDLock.Lock()
|
||||||
|
_, err = f.createHeader()
|
||||||
|
f.fileTableEntry.IDLock.Unlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ func DevInoFromStat(st *syscall.Stat_t) DevInoStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
wlock.inodeLocks = make(map[DevInoStruct]*refCntMutex)
|
openFileMap.entries = make(map[DevInoStruct]*openFileEntryT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// wlock - serializes write accesses to each file (identified by inode number)
|
// wlock - serializes write accesses to each file (identified by inode number)
|
||||||
@ -30,14 +30,14 @@ func init() {
|
|||||||
// really don't want concurrent writes there.
|
// really don't want concurrent writes there.
|
||||||
// Concurrent full-block writes could actually be allowed, but are not to
|
// Concurrent full-block writes could actually be allowed, but are not to
|
||||||
// keep the locking simple.
|
// keep the locking simple.
|
||||||
var wlock wlockMap
|
var openFileMap openFileMapT
|
||||||
|
|
||||||
// wlockMap - usage:
|
// wlockMap - usage:
|
||||||
// 1) register
|
// 1) register
|
||||||
// 2) lock ... unlock ...
|
// 2) lock ... unlock ...
|
||||||
// 3) unregister
|
// 3) unregister
|
||||||
type wlockMap struct {
|
type openFileMapT struct {
|
||||||
// opCount counts lock() calls. As every operation that modifies a file should
|
// opCount counts writeLock.Lock() calls. As every operation that modifies a file should
|
||||||
// call it, this effectively serves as a write-operation counter.
|
// call it, this effectively serves as a write-operation counter.
|
||||||
// The variable is accessed without holding any locks so atomic operations
|
// The variable is accessed without holding any locks so atomic operations
|
||||||
// must be used. It must be the first element of the struct to guarantee
|
// must be used. It must be the first element of the struct to guarantee
|
||||||
@ -45,58 +45,56 @@ type wlockMap struct {
|
|||||||
opCount uint64
|
opCount uint64
|
||||||
// Protects map access
|
// Protects map access
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
inodeLocks map[DevInoStruct]*refCntMutex
|
entries map[DevInoStruct]*openFileEntryT
|
||||||
|
}
|
||||||
|
|
||||||
|
type opCountMutex struct {
|
||||||
|
sync.Mutex
|
||||||
|
// Points to the opCount variable of the parent openFileMapT
|
||||||
|
opCount *uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *opCountMutex) Lock() {
|
||||||
|
o.Mutex.Lock()
|
||||||
|
atomic.AddUint64(o.opCount, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// refCntMutex - mutex with reference count
|
// refCntMutex - mutex with reference count
|
||||||
type refCntMutex struct {
|
type openFileEntryT struct {
|
||||||
// Write lock for this inode
|
|
||||||
sync.Mutex
|
|
||||||
// Reference count
|
// Reference count
|
||||||
refCnt int
|
refCnt int
|
||||||
|
// Write lock for this inode
|
||||||
|
writeLock *opCountMutex
|
||||||
|
// ID is the file ID in the file header.
|
||||||
|
ID []byte
|
||||||
|
IDLock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// register creates an entry for "ino", or incrementes the reference count
|
// register creates an entry for "ino", or incrementes the reference count
|
||||||
// if the entry already exists.
|
// if the entry already exists.
|
||||||
func (w *wlockMap) register(di DevInoStruct) {
|
func (w *openFileMapT) register(di DevInoStruct) *openFileEntryT {
|
||||||
w.Lock()
|
w.Lock()
|
||||||
defer w.Unlock()
|
defer w.Unlock()
|
||||||
|
|
||||||
r := w.inodeLocks[di]
|
r := w.entries[di]
|
||||||
if r == nil {
|
if r == nil {
|
||||||
r = &refCntMutex{}
|
o := opCountMutex{opCount: &w.opCount}
|
||||||
w.inodeLocks[di] = r
|
r = &openFileEntryT{writeLock: &o}
|
||||||
|
w.entries[di] = r
|
||||||
}
|
}
|
||||||
r.refCnt++
|
r.refCnt++
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// unregister decrements the reference count for "di" and deletes the entry if
|
// unregister decrements the reference count for "di" and deletes the entry if
|
||||||
// the reference count has reached 0.
|
// the reference count has reached 0.
|
||||||
func (w *wlockMap) unregister(di DevInoStruct) {
|
func (w *openFileMapT) unregister(di DevInoStruct) {
|
||||||
w.Lock()
|
w.Lock()
|
||||||
defer w.Unlock()
|
defer w.Unlock()
|
||||||
|
|
||||||
r := w.inodeLocks[di]
|
r := w.entries[di]
|
||||||
r.refCnt--
|
r.refCnt--
|
||||||
if r.refCnt == 0 {
|
if r.refCnt == 0 {
|
||||||
delete(w.inodeLocks, di)
|
delete(w.entries, di)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// lock retrieves the entry for "di" and locks it.
|
|
||||||
func (w *wlockMap) lock(di DevInoStruct) {
|
|
||||||
atomic.AddUint64(&w.opCount, 1)
|
|
||||||
w.Lock()
|
|
||||||
r := w.inodeLocks[di]
|
|
||||||
w.Unlock()
|
|
||||||
// this can take a long time - execute outside the wlockMap lock
|
|
||||||
r.Lock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// unlock retrieves the entry for "di" and unlocks it.
|
|
||||||
func (w *wlockMap) unlock(di DevInoStruct) {
|
|
||||||
w.Lock()
|
|
||||||
r := w.inodeLocks[di]
|
|
||||||
w.Unlock()
|
|
||||||
r.Unlock()
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user