Skip to content

Commit

Permalink
vfs: allows ftruncate to open a file even if it has been deleted (#5664)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhijian-pro authored Feb 24, 2025
1 parent b223663 commit e820195
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 5 deletions.
40 changes: 40 additions & 0 deletions cmd/mount_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package cmd

import (
"context"
"errors"
"fmt"
"io"
"net/http"
Expand All @@ -27,6 +28,7 @@ import (
"runtime"
"strings"
"sync"
"syscall"
"testing"
"time"

Expand Down Expand Up @@ -167,6 +169,44 @@ func TestMount(t *testing.T) {
}
}

func TestFtruncate(t *testing.T) {
mountTemp(t, nil, nil, nil)
defer umountTemp(t)

fpath := fmt.Sprintf("%s/f1.txt", testMountPoint)
if err := os.WriteFile(fpath, []byte("test"), 0644); err != nil {
t.Fatalf("write file failed: %s", err)
}
file, err := os.OpenFile(fpath, os.O_RDWR, 0644)
if err != nil {
t.Fatalf("open file failed: %s", err)
}
if err = syscall.Ftruncate(int(file.Fd()), 1024); err != nil {
t.Fatalf("ftruncate failed: %s", err)
}
fileInfo, err := os.Stat(fpath)
if err != nil {
t.Fatalf("stat file failed: %s", err)
}
if fileInfo.Size() != 1024 {
t.Fatalf("ftruncate failed: file size is %d, expect 1024", fileInfo.Size())
}
if err = os.Remove(fpath); err != nil {
t.Fatalf("remove file failed: %s", err)
}
if _, err = os.Stat(fpath); !errors.Is(err, syscall.ENOENT) {
t.Fatalf("file still exists after delete: %s", err)
}
err = syscall.Ftruncate(int(file.Fd()), 2048)
if err != nil {
t.Fatalf("ftruncate failed: %s", err)
}
file.Close()
_, err = os.Stat(fpath)
if !errors.Is(err, syscall.ENOENT) {
t.Fatalf("file still exists after close: %s", err)
}
}
func TestUpdateFstab(t *testing.T) {
if runtime.GOOS != "linux" {
t.SkipNow()
Expand Down
1 change: 1 addition & 0 deletions pkg/meta/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const (
SetAttrCtime
SetAttrAtimeNow
SetAttrMtimeNow
SetAttrCtimeNow
SetAttrFlag = 1 << 15
)

Expand Down
2 changes: 1 addition & 1 deletion pkg/meta/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,7 @@ func (m *redisMeta) doTruncate(ctx Context, inode Ino, flags uint8, length uint6
return err
}
m.parseAttr(a, &t)
if t.Typ != TypeFile || t.Flags&(FlagImmutable|FlagAppend) != 0 || t.Parent > TrashInode {
if t.Typ != TypeFile || t.Flags&(FlagImmutable|FlagAppend) != 0 || (flags == 0 && t.Parent > TrashInode) {
return syscall.EPERM
}
if !skipPermCheck {
Expand Down
2 changes: 1 addition & 1 deletion pkg/meta/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -1280,7 +1280,7 @@ func (m *dbMeta) doTruncate(ctx Context, inode Ino, flags uint8, length uint64,
if !ok {
return syscall.ENOENT
}
if nodeAttr.Type != TypeFile || nodeAttr.Flags&(FlagImmutable|FlagAppend) != 0 || nodeAttr.Parent > TrashInode {
if nodeAttr.Type != TypeFile || nodeAttr.Flags&(FlagImmutable|FlagAppend) != 0 || (flags == 0 && nodeAttr.Parent > TrashInode) {
return syscall.EPERM
}
m.parseAttr(&nodeAttr, attr)
Expand Down
2 changes: 1 addition & 1 deletion pkg/meta/tkv.go
Original file line number Diff line number Diff line change
Expand Up @@ -963,7 +963,7 @@ func (m *kvMeta) doTruncate(ctx Context, inode Ino, flags uint8, length uint64,
}
t := Attr{}
m.parseAttr(a, &t)
if t.Typ != TypeFile || t.Flags&(FlagImmutable|t.Flags&FlagAppend) != 0 || t.Parent > TrashInode {
if t.Typ != TypeFile || t.Flags&(FlagImmutable|t.Flags&FlagAppend) != 0 || (flags == 0 && t.Parent > TrashInode) {
return syscall.EPERM
}
if !skipPermCheck {
Expand Down
3 changes: 2 additions & 1 deletion pkg/vfs/vfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,8 @@ func (v *VFS) Truncate(ctx Context, ino Ino, size int64, fh uint64, attr *Attr)
err = syscall.EACCES
return
}
err = v.Meta.Truncate(ctx, ino, 0, uint64(size), attr, true)
// flags = 1 means the file is opened, so we don't need to check if it's in the trash
err = v.Meta.Truncate(ctx, ino, 1, uint64(size), attr, true)
}
if err == 0 {
v.writer.Truncate(ino, uint64(size))
Expand Down
6 changes: 5 additions & 1 deletion pkg/vfs/vfs_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,17 @@ func (v *VFS) SetAttr(ctx Context, ino Ino, set int, fh uint64, mode, uid, gid u
}
return
}
err = syscall.EINVAL
var attr = &Attr{}
if set&meta.SetAttrSize != 0 {
err = v.Truncate(ctx, ino, int64(size), fh, attr)
if err != 0 {
return
}
if (set &^ (meta.SetAttrSize | meta.SetAttrCtime | meta.SetAttrCtimeNow)) == 0 {
v.UpdateLength(ino, attr)
entry = &meta.Entry{Inode: ino, Attr: attr}
return
}
}
if set&meta.SetAttrMode != 0 {
attr.Mode = uint16(mode & 07777)
Expand Down

0 comments on commit e820195

Please sign in to comment.