mirror of
https://github.com/hanzoai/go-fuse.git
synced 2026-10-30 14:46:18 +00:00
248 lines
6.1 KiB
Go
248 lines
6.1 KiB
Go
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package test
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
"github.com/hanzoai/go-fuse/v2/fuse"
|
|
)
|
|
|
|
var enableOverlayfsTest bool
|
|
var enableOverlayfsTestFlag string = "test.overlayfs"
|
|
|
|
func init() {
|
|
// flag to enable test with overayfs. This would not work on kernel 5.15
|
|
// so don't enable on that kernel.
|
|
flag.BoolVar(&enableOverlayfsTest, enableOverlayfsTestFlag, false, "enable tests with overlayfs (would fail on kernel 5.15)")
|
|
}
|
|
|
|
func TestTouch(t *testing.T) {
|
|
ts := NewTestCase(t)
|
|
defer ts.Cleanup()
|
|
|
|
contents := []byte{1, 2, 3}
|
|
err := os.WriteFile(ts.origFile, []byte(contents), 0700)
|
|
if err != nil {
|
|
t.Fatalf("WriteFile failed: %v", err)
|
|
}
|
|
err = os.Chtimes(ts.mountFile, time.Unix(42, 0), time.Unix(43, 0))
|
|
if err != nil {
|
|
t.Fatalf("Chtimes failed: %v", err)
|
|
}
|
|
|
|
var stat syscall.Stat_t
|
|
err = syscall.Lstat(ts.mountFile, &stat)
|
|
if err != nil {
|
|
t.Fatalf("Lstat failed: %v", err)
|
|
}
|
|
if stat.Atim.Sec != 42 {
|
|
t.Errorf("Got atime.sec %d, want 42. Stat_t was %#v", stat.Atim.Sec, stat)
|
|
}
|
|
if stat.Mtim.Sec != 43 {
|
|
t.Errorf("Got mtime.sec %d, want 43. Stat_t was %#v", stat.Mtim.Sec, stat)
|
|
}
|
|
}
|
|
|
|
func TestNegativeTime(t *testing.T) {
|
|
ts := NewTestCase(t)
|
|
defer ts.Cleanup()
|
|
|
|
_, err := os.Create(ts.origFile)
|
|
if err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
var stat syscall.Stat_t
|
|
|
|
// set negative nanosecond will occur errors on UtimesNano as invalid argument
|
|
ut := time.Date(1960, time.January, 10, 23, 0, 0, 0, time.UTC)
|
|
tim := []syscall.Timespec{
|
|
syscall.NsecToTimespec(ut.UnixNano()),
|
|
syscall.NsecToTimespec(ut.UnixNano()),
|
|
}
|
|
err = syscall.UtimesNano(ts.mountFile, tim)
|
|
if err != nil {
|
|
t.Fatalf("UtimesNano failed: %v", err)
|
|
}
|
|
err = syscall.Lstat(ts.mountFile, &stat)
|
|
if err != nil {
|
|
t.Fatalf("Lstat failed: %v", err)
|
|
}
|
|
|
|
if stat.Atim.Sec >= 0 || stat.Mtim.Sec >= 0 {
|
|
t.Errorf("Got wrong timestamps %v", stat)
|
|
}
|
|
}
|
|
|
|
// Setting nanoseconds should work for dates after 1970
|
|
func TestUtimesNano(t *testing.T) {
|
|
tc := NewTestCase(t)
|
|
defer tc.Cleanup()
|
|
|
|
path := tc.mountFile
|
|
err := os.WriteFile(path, []byte("xyz"), 0600)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ts := make([]syscall.Timespec, 2)
|
|
// atime
|
|
ts[0].Sec = 1
|
|
ts[0].Nsec = 2
|
|
// mtime
|
|
ts[1].Sec = 3
|
|
ts[1].Nsec = 4
|
|
err = syscall.UtimesNano(path, ts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var st syscall.Stat_t
|
|
err = syscall.Stat(path, &st)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if st.Atim != ts[0] {
|
|
t.Errorf("Wrong atime: %v, want: %v", st.Atim, ts[0])
|
|
}
|
|
if st.Mtim != ts[1] {
|
|
t.Errorf("Wrong mtime: %v, want: %v", st.Mtim, ts[1])
|
|
}
|
|
}
|
|
|
|
func clearStatfs(s *syscall.Statfs_t) {
|
|
empty := syscall.Statfs_t{}
|
|
s.Type = 0
|
|
s.Fsid = empty.Fsid
|
|
s.Spare = empty.Spare
|
|
// TODO - figure out what this is for.
|
|
s.Flags = 0
|
|
}
|
|
|
|
// Check that fuse mount can serve as a overlayfs lowerdir.
|
|
func TestOverlayfs(t *testing.T) {
|
|
if !enableOverlayfsTest {
|
|
t.Skipf("this test must be enabled through the flag %q", enableOverlayfsTestFlag)
|
|
}
|
|
if os.Getuid() != 0 {
|
|
t.Skip("this test requires root")
|
|
}
|
|
tc := NewTestCase(t)
|
|
defer tc.Cleanup()
|
|
|
|
testfile := "test"
|
|
content := randomData(125)
|
|
tc.Mkdir(tc.origSubdir, 0777)
|
|
tc.WriteFile(filepath.Join(tc.origSubdir, testfile), content, 0700)
|
|
|
|
tmpMergedDir := t.TempDir()
|
|
tmpWorkDir := t.TempDir()
|
|
tmpUpperDir := t.TempDir()
|
|
defer os.RemoveAll(tmpUpperDir)
|
|
if err := unix.Mount("overlay", tmpMergedDir, "overlay", 0,
|
|
fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", tc.mnt, tmpUpperDir, tmpWorkDir)); err != nil {
|
|
t.Fatalf("failed to mount overlay: %v", err)
|
|
}
|
|
defer unix.Unmount(tmpMergedDir, 0)
|
|
|
|
err := os.Chtimes(filepath.Join(tmpMergedDir, "subdir", testfile), time.Unix(42, 0), time.Unix(43, 0))
|
|
if err != nil {
|
|
t.Fatalf("Chtimes failed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestFallocate(t *testing.T) {
|
|
ts := NewTestCase(t)
|
|
defer ts.Cleanup()
|
|
if ts.state.KernelSettings().Minor < 19 {
|
|
t.Log("FUSE does not support Fallocate.")
|
|
return
|
|
}
|
|
|
|
rwFile, err := os.OpenFile(ts.mnt+"/file", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
|
|
if err != nil {
|
|
t.Fatalf("OpenFile failed: %v", err)
|
|
}
|
|
defer rwFile.Close()
|
|
err = syscall.Fallocate(int(rwFile.Fd()), 0, 1024, 4096)
|
|
if err != nil {
|
|
t.Fatalf("FUSE Fallocate failed: %v", err)
|
|
}
|
|
fi, err := os.Lstat(ts.orig + "/file")
|
|
if err != nil {
|
|
t.Fatalf("Lstat failed: %v", err)
|
|
}
|
|
if fi.Size() < (1024 + 4096) {
|
|
t.Fatalf("fallocate should have changed file size. Got %d bytes",
|
|
fi.Size())
|
|
}
|
|
}
|
|
|
|
// Check that "." and ".." exists. unix.Getdents is linux specific.
|
|
func TestSpecialEntries(t *testing.T) {
|
|
tc := NewTestCase(t)
|
|
defer tc.Cleanup()
|
|
|
|
d, err := os.Open(tc.mnt)
|
|
if err != nil {
|
|
t.Fatalf("Open failed: %v", err)
|
|
}
|
|
defer d.Close()
|
|
buf := make([]byte, 100)
|
|
n, err := unix.Getdents(int(d.Fd()), buf)
|
|
if n == 0 {
|
|
t.Errorf("directory is empty, entries '.' and '..' are missing")
|
|
}
|
|
}
|
|
|
|
// Check that readdir(3) returns valid inode numbers in the directory entries
|
|
func TestReaddirInodes(t *testing.T) {
|
|
tc := NewTestCase(t)
|
|
defer tc.Cleanup()
|
|
// create "hello.txt"
|
|
filename := "hello.txt"
|
|
path := tc.orig + "/" + filename
|
|
err := os.WriteFile(path, []byte("xyz"), 0600)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// open mountpoint dir
|
|
d, err := os.Open(tc.mnt)
|
|
if err != nil {
|
|
t.Fatalf("Open failed: %v", err)
|
|
}
|
|
defer d.Close()
|
|
buf := make([]byte, 100)
|
|
// readdir(3) use getdents64(2) internally which returns linux_dirent64
|
|
// structures. We don't have readdir(3) so we call getdents64(2) directly.
|
|
n, err := unix.Getdents(int(d.Fd()), buf)
|
|
if n == 0 {
|
|
t.Error("empty directory - we need at least one file")
|
|
}
|
|
buf = buf[:n]
|
|
entries := parseDirents(buf)
|
|
t.Logf("parseDirents returned %d entries", len(entries))
|
|
// Find "hello.txt" and check inode number.
|
|
for _, entry := range entries {
|
|
if entry.name != filename {
|
|
continue
|
|
}
|
|
if entry.ino != 0 && entry.ino != fuse.FUSE_UNKNOWN_INO {
|
|
// Inode number looks good, we are done.
|
|
return
|
|
}
|
|
t.Errorf("got invalid inode number: %d = 0x%x", entry.ino, entry.ino)
|
|
}
|
|
t.Errorf("%q not found in directory listing", filename)
|
|
}
|