Skip to content

Commit b00985d

Browse files
committed
Deny modifications of "invisible" deleted files
1 parent fdc2d17 commit b00985d

File tree

5 files changed

+50
-14
lines changed

5 files changed

+50
-14
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ List of non-POSIX behaviors/limitations for GeeseFS:
5151
- file modification time can't be set by user (for example with `cp --preserve`, `rsync -a` or utimes(2))
5252
* Does not support hard links
5353
* Does not support locking
54+
* Does not support "invisible" deleted files. If an app keeps an opened file descriptor
55+
after deleting the file it will get ENOENT errors from FS operations
5456

5557
In addition to the items above:
5658
* Default file size limit is 1.03 TB, achieved by splitting the file into 1000x 5MB parts,

internal/dir.go

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ func (dh *DirHandle) ReadDir(internalOffset int, offset fuseops.DirOffset) (en *
685685
childTmp := parent.dir.Children[i]
686686
if childTmp.AttrTime.Before(parent.dir.refreshStartTime) &&
687687
atomic.LoadInt32(&childTmp.fileHandles) == 0 &&
688-
atomic.LoadInt32(&childTmp.CacheState) == ST_CACHED &&
688+
atomic.LoadInt32(&childTmp.CacheState) <= ST_DEAD &&
689689
(!childTmp.isDir() || atomic.LoadInt64(&childTmp.dir.ModifiedChildren) == 0) {
690690
childTmp.mu.Lock()
691691
notifications = append(notifications, &fuseops.NotifyDelete{
@@ -1043,7 +1043,7 @@ func (inode *Inode) SendDelete() {
10431043
}
10441044
forget := false
10451045
if inode.CacheState == ST_DELETED {
1046-
inode.SetCacheState(ST_CACHED)
1046+
inode.SetCacheState(ST_DEAD)
10471047
// We don't remove directories until all children are deleted
10481048
// So that we don't revive the directory after removing it
10491049
// by fetching a list of files not all of which are actually deleted
@@ -1313,14 +1313,21 @@ func (inode *Inode) doUnlink() {
13131313
} else if inode.CacheState != ST_CREATED || inode.IsFlushing > 0 {
13141314
// resetCache will clear all buffers and abort the multipart upload
13151315
inode.resetCache()
1316-
inode.SetCacheState(ST_DELETED)
1317-
if parent.dir.DeletedChildren == nil {
1318-
parent.dir.DeletedChildren = make(map[string]*Inode)
1316+
if parent.dir.DeletedChildren == nil || parent.dir.DeletedChildren[inode.Name] == nil {
1317+
inode.SetCacheState(ST_DELETED)
1318+
if parent.dir.DeletedChildren == nil {
1319+
parent.dir.DeletedChildren = make(map[string]*Inode)
1320+
}
1321+
parent.dir.DeletedChildren[inode.Name] = inode
1322+
} else {
1323+
// A deleted file is already present, we can just reset the cache
1324+
inode.SetCacheState(ST_DEAD)
13191325
}
1320-
parent.dir.DeletedChildren[inode.Name] = inode
13211326
} else {
13221327
inode.resetCache()
1328+
inode.SetCacheState(ST_DEAD)
13231329
}
1330+
inode.Attributes.Size = 0
13241331

13251332
parent.removeChildUnlocked(inode)
13261333
}
@@ -1522,7 +1529,9 @@ func renameInCache(fromInode *Inode, newParent *Inode, to string) {
15221529
if parent.dir.DeletedChildren == nil {
15231530
parent.dir.DeletedChildren = make(map[string]*Inode)
15241531
}
1525-
parent.dir.DeletedChildren[fromInode.Name] = fromInode
1532+
if parent.dir.DeletedChildren[fromInode.Name] == nil {
1533+
parent.dir.DeletedChildren[fromInode.Name] = fromInode
1534+
}
15261535
fromInode.renamingTo = false
15271536
} else {
15281537
parent.addModified(-1)
@@ -1538,7 +1547,9 @@ func renameInCache(fromInode *Inode, newParent *Inode, to string) {
15381547
if parent.dir.DeletedChildren == nil {
15391548
parent.dir.DeletedChildren = make(map[string]*Inode)
15401549
}
1541-
parent.dir.DeletedChildren[fromInode.Name] = fromInode
1550+
if parent.dir.DeletedChildren[fromInode.Name] == nil {
1551+
parent.dir.DeletedChildren[fromInode.Name] = fromInode
1552+
}
15421553
if fromInode.CacheState == ST_CACHED {
15431554
// Was not modified and we remove it from current parent => add modified
15441555
parent.addModified(1)
@@ -1637,7 +1648,7 @@ func (parent *Inode) insertSubTree(path string, obj *BlobItemOutput, dirs map[*I
16371648
}
16381649
} else if !inode.isDir() {
16391650
// replace unmodified file item with a directory
1640-
if atomic.LoadInt32(&inode.CacheState) == ST_CACHED {
1651+
if atomic.LoadInt32(&inode.CacheState) <= ST_DEAD {
16411652
inode.mu.Lock()
16421653
atomic.StoreInt32(&inode.refreshed, -1)
16431654
parent.removeChildUnlocked(inode)

internal/file.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,13 @@ func (fh *FileHandle) WriteFile(offset int64, data []byte, copyData bool) (err e
457457

458458
fh.inode.mu.Lock()
459459

460+
if fh.inode.CacheState == ST_DELETED || fh.inode.CacheState == ST_DEAD {
461+
// Oops, it's a deleted file. We don't support changing invisible files
462+
fh.inode.fs.bufferPool.Use(-int64(len(data)), false)
463+
fh.inode.mu.Unlock()
464+
return fuse.ENOENT
465+
}
466+
460467
fh.inode.checkPauseWriters()
461468

462469
if fh.inode.Attributes.Size < end {
@@ -1161,7 +1168,7 @@ func (fh *FileHandle) Release() {
11611168
if n == -1 {
11621169
panic(fmt.Sprintf("Released more file handles than acquired, n = %v", n))
11631170
}
1164-
if n == 0 && atomic.LoadInt32(&fh.inode.CacheState) == ST_CACHED {
1171+
if n == 0 && atomic.LoadInt32(&fh.inode.CacheState) <= ST_DEAD {
11651172
fh.inode.Parent.addModified(-1)
11661173
}
11671174
fh.inode.fs.WakeupFlusher()
@@ -2085,7 +2092,7 @@ func (inode *Inode) SyncFile() (err error) {
20852092
for {
20862093
inode.mu.Lock()
20872094
inode.forceFlush = false
2088-
if inode.CacheState == ST_CACHED {
2095+
if inode.CacheState <= ST_DEAD {
20892096
inode.mu.Unlock()
20902097
break
20912098
}

internal/goofys.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1733,6 +1733,11 @@ func (fs *Goofys) SetInodeAttributes(
17331733

17341734
if op.Size != nil || op.Mode != nil || op.Mtime != nil || op.Uid != nil || op.Gid != nil {
17351735
inode.mu.Lock()
1736+
if inode.CacheState == ST_DELETED || inode.CacheState == ST_DEAD {
1737+
// Oops, it's a deleted file. We don't support changing invisible files
1738+
inode.mu.Unlock()
1739+
return fuse.ENOENT
1740+
}
17361741
}
17371742

17381743
modified := false

internal/handles.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ import (
3838

3939
const (
4040
ST_CACHED int32 = 0
41-
ST_CREATED int32 = 1
42-
ST_MODIFIED int32 = 2
43-
ST_DELETED int32 = 3
41+
ST_DEAD int32 = 1
42+
ST_CREATED int32 = 2
43+
ST_MODIFIED int32 = 3
44+
ST_DELETED int32 = 4
4445
)
4546

4647
type InodeAttributes struct {
@@ -611,6 +612,11 @@ func (inode *Inode) SetXattr(name string, value []byte, flags uint32) error {
611612
inode.mu.Lock()
612613
defer inode.mu.Unlock()
613614

615+
if inode.CacheState == ST_DELETED || inode.CacheState == ST_DEAD {
616+
// Oops, it's a deleted file. We don't support changing invisible files
617+
return fuse.ENOENT
618+
}
619+
614620
meta, name, err := inode.getXattrMap(name, true)
615621
if err != nil {
616622
return err
@@ -644,6 +650,11 @@ func (inode *Inode) RemoveXattr(name string) error {
644650
inode.mu.Lock()
645651
defer inode.mu.Unlock()
646652

653+
if inode.CacheState == ST_DELETED || inode.CacheState == ST_DEAD {
654+
// Oops, it's a deleted file. We don't support changing invisible files
655+
return fuse.ENOENT
656+
}
657+
647658
meta, name, err := inode.getXattrMap(name, true)
648659
if err != nil {
649660
return err

0 commit comments

Comments
 (0)