diff --git a/cmd/mount_test.go b/cmd/mount_test.go index ac98219ce49f..e30c6aad07ec 100644 --- a/cmd/mount_test.go +++ b/cmd/mount_test.go @@ -18,6 +18,7 @@ package cmd import ( "context" + "errors" "fmt" "io" "net/http" @@ -27,6 +28,7 @@ import ( "runtime" "strings" "sync" + "syscall" "testing" "time" @@ -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() diff --git a/pkg/meta/interface.go b/pkg/meta/interface.go index b1cc04ba3e60..69854dc77738 100644 --- a/pkg/meta/interface.go +++ b/pkg/meta/interface.go @@ -89,6 +89,7 @@ const ( SetAttrCtime SetAttrAtimeNow SetAttrMtimeNow + SetAttrCtimeNow SetAttrFlag = 1 << 15 ) diff --git a/pkg/meta/redis.go b/pkg/meta/redis.go index 3563ceae555d..61491880268e 100644 --- a/pkg/meta/redis.go +++ b/pkg/meta/redis.go @@ -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 { diff --git a/pkg/meta/sql.go b/pkg/meta/sql.go index ff21a3843593..b8ccac5bf3a1 100644 --- a/pkg/meta/sql.go +++ b/pkg/meta/sql.go @@ -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) diff --git a/pkg/meta/tkv.go b/pkg/meta/tkv.go index 08a07b1fe5a3..771a08a0487d 100644 --- a/pkg/meta/tkv.go +++ b/pkg/meta/tkv.go @@ -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 { diff --git a/pkg/vfs/vfs.go b/pkg/vfs/vfs.go index 9afe3d41727d..fecb1ed6414f 100644 --- a/pkg/vfs/vfs.go +++ b/pkg/vfs/vfs.go @@ -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)) diff --git a/pkg/vfs/vfs_unix.go b/pkg/vfs/vfs_unix.go index dfe0e9eb8619..66f5c846e3b2 100644 --- a/pkg/vfs/vfs_unix.go +++ b/pkg/vfs/vfs_unix.go @@ -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)