1// Copyright 2019 the Go-FUSE Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package fs
6
7import (
8	"bytes"
9	"io/ioutil"
10	"os"
11	"reflect"
12	"sync"
13	"syscall"
14	"testing"
15	"time"
16
17	"github.com/hanwen/go-fuse/v2/fuse"
18	"github.com/hanwen/go-fuse/v2/internal/testutil"
19	"github.com/kylelemons/godebug/pretty"
20	"golang.org/x/sys/unix"
21)
22
23func TestRenameExchange(t *testing.T) {
24	tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
25	defer tc.Clean()
26
27	if err := os.Mkdir(tc.origDir+"/dir", 0755); err != nil {
28		t.Fatalf("Mkdir: %v", err)
29	}
30	tc.writeOrig("file", "hello", 0644)
31	tc.writeOrig("dir/file", "x", 0644)
32
33	f1, err := syscall.Open(tc.mntDir+"/", syscall.O_DIRECTORY, 0)
34	if err != nil {
35		t.Fatalf("open 1: %v", err)
36	}
37	defer syscall.Close(f1)
38	f2, err := syscall.Open(tc.mntDir+"/dir", syscall.O_DIRECTORY, 0)
39	if err != nil {
40		t.Fatalf("open 2: %v", err)
41	}
42	defer syscall.Close(f2)
43
44	var before1, before2 unix.Stat_t
45	if err := unix.Fstatat(f1, "file", &before1, 0); err != nil {
46		t.Fatalf("Fstatat: %v", err)
47	}
48	if err := unix.Fstatat(f2, "file", &before2, 0); err != nil {
49		t.Fatalf("Fstatat: %v", err)
50	}
51
52	if err := unix.Renameat2(f1, "file", f2, "file", unix.RENAME_EXCHANGE); err != nil {
53		t.Errorf("rename EXCHANGE: %v", err)
54	}
55
56	var after1, after2 unix.Stat_t
57	if err := unix.Fstatat(f1, "file", &after1, 0); err != nil {
58		t.Fatalf("Fstatat: %v", err)
59	}
60	if err := unix.Fstatat(f2, "file", &after2, 0); err != nil {
61		t.Fatalf("Fstatat: %v", err)
62	}
63	clearCtime := func(s *unix.Stat_t) {
64		s.Ctim.Sec = 0
65		s.Ctim.Nsec = 0
66	}
67
68	clearCtime(&after1)
69	clearCtime(&after2)
70	clearCtime(&before2)
71	clearCtime(&before1)
72	if diff := pretty.Compare(after1, before2); diff != "" {
73		t.Errorf("after1, before2: %s", diff)
74	}
75	if !reflect.DeepEqual(after2, before1) {
76		t.Errorf("after2, before1: %#v, %#v", after2, before1)
77	}
78}
79
80func TestRenameNoOverwrite(t *testing.T) {
81	tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
82	defer tc.Clean()
83
84	if err := os.Mkdir(tc.origDir+"/dir", 0755); err != nil {
85		t.Fatalf("Mkdir: %v", err)
86	}
87	tc.writeOrig("file", "hello", 0644)
88	tc.writeOrig("dir/file", "x", 0644)
89
90	f1, err := syscall.Open(tc.mntDir+"/", syscall.O_DIRECTORY, 0)
91	if err != nil {
92		t.Fatalf("open 1: %v", err)
93	}
94	defer syscall.Close(f1)
95	f2, err := syscall.Open(tc.mntDir+"/dir", syscall.O_DIRECTORY, 0)
96	if err != nil {
97		t.Fatalf("open 2: %v", err)
98	}
99	defer syscall.Close(f2)
100
101	if err := unix.Renameat2(f1, "file", f2, "file", unix.RENAME_NOREPLACE); err == nil {
102		t.Errorf("rename NOREPLACE succeeded")
103	} else if err != syscall.EEXIST {
104		t.Errorf("got %v (%T) want EEXIST", err, err)
105	}
106}
107
108func TestXAttr(t *testing.T) {
109	tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
110	defer tc.Clean()
111
112	tc.writeOrig("file", "", 0644)
113
114	buf := make([]byte, 1024)
115	attr := "user.xattrtest"
116	if _, err := syscall.Getxattr(tc.mntDir+"/file", attr, buf); err == syscall.ENOTSUP {
117		t.Skip("$TMP does not support xattrs. Rerun this test with a $TMPDIR override")
118	}
119
120	if _, err := syscall.Getxattr(tc.mntDir+"/file", attr, buf); err != syscall.ENODATA {
121		t.Fatalf("got %v want ENOATTR", err)
122	}
123	value := []byte("value")
124	if err := syscall.Setxattr(tc.mntDir+"/file", attr, value, 0); err != nil {
125		t.Fatalf("Setxattr: %v", err)
126	}
127
128	sz, err := syscall.Listxattr(tc.mntDir+"/file", nil)
129	if err != nil {
130		t.Fatalf("Listxattr: %v", err)
131	}
132	buf = make([]byte, sz)
133	if _, err := syscall.Listxattr(tc.mntDir+"/file", buf); err != nil {
134		t.Fatalf("Listxattr: %v", err)
135	} else {
136		attributes := bytes.Split(buf[:sz], []byte{0})
137		found := false
138		for _, a := range attributes {
139			if string(a) == attr {
140				found = true
141				break
142			}
143		}
144
145		if !found {
146			t.Fatalf("Listxattr: %q (not found: %q", buf[:sz], attr)
147		}
148	}
149
150	sz, err = syscall.Getxattr(tc.mntDir+"/file", attr, buf)
151	if err != nil {
152		t.Fatalf("Getxattr: %v", err)
153	}
154	if bytes.Compare(buf[:sz], value) != 0 {
155		t.Fatalf("Getxattr got %q want %q", buf[:sz], value)
156	}
157	if err := syscall.Removexattr(tc.mntDir+"/file", attr); err != nil {
158		t.Fatalf("Removexattr: %v", err)
159	}
160
161	if _, err := syscall.Getxattr(tc.mntDir+"/file", attr, buf); err != syscall.ENODATA {
162		t.Fatalf("got %v want ENOATTR", err)
163	}
164}
165
166// TestXAttrSymlink verifies that we did not forget to use Lgetxattr instead
167// of Getxattr. This test is Linux-specific because it depends on the behavoir
168// of the `security` namespace.
169//
170// On Linux, symlinks can not have xattrs in the `user` namespace, so we
171// try to read something from `security`. Writing would need root rights,
172// so don't even bother. See `man 7 xattr` for more info.
173func TestXAttrSymlink(t *testing.T) {
174	tc := newTestCase(t, nil)
175	defer tc.Clean()
176
177	path := tc.mntDir + "/symlink"
178	if err := syscall.Symlink("target/does/not/exist", path); err != nil {
179		t.Fatal(err)
180	}
181	buf := make([]byte, 10)
182	_, err := unix.Lgetxattr(path, "security.foo", buf)
183	if err != unix.ENODATA {
184		t.Errorf("want %d=ENODATA, got error %d=%q instead", unix.ENODATA, err, err)
185	}
186}
187
188func TestCopyFileRange(t *testing.T) {
189	tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
190	defer tc.Clean()
191
192	if !tc.server.KernelSettings().SupportsVersion(7, 28) {
193		t.Skip("need v7.28 for CopyFileRange")
194	}
195
196	tc.writeOrig("src", "01234567890123456789", 0644)
197	tc.writeOrig("dst", "abcdefghijabcdefghij", 0644)
198
199	f1, err := syscall.Open(tc.mntDir+"/src", syscall.O_RDONLY, 0)
200	if err != nil {
201		t.Fatalf("Open src: %v", err)
202	}
203	defer func() {
204		// syscall.Close() is treacherous; because fds are
205		// reused, a double close can cause serious havoc
206		if f1 > 0 {
207			syscall.Close(f1)
208		}
209	}()
210
211	f2, err := syscall.Open(tc.mntDir+"/dst", syscall.O_RDWR, 0)
212	if err != nil {
213		t.Fatalf("Open dst: %v", err)
214	}
215	defer func() {
216		if f2 > 0 {
217			defer syscall.Close(f2)
218		}
219	}()
220
221	srcOff := int64(5)
222	dstOff := int64(7)
223	if sz, err := unix.CopyFileRange(f1, &srcOff, f2, &dstOff, 3, 0); err != nil || sz != 3 {
224		t.Fatalf("CopyFileRange: %d,%v", sz, err)
225	}
226
227	err = syscall.Close(f1)
228	f1 = 0
229	if err != nil {
230		t.Fatalf("Close src: %v", err)
231	}
232
233	err = syscall.Close(f2)
234	f2 = 0
235	if err != nil {
236		t.Fatalf("Close dst: %v", err)
237	}
238	c, err := ioutil.ReadFile(tc.mntDir + "/dst")
239	if err != nil {
240		t.Fatalf("ReadFile: %v", err)
241	}
242
243	want := "abcdefg567abcdefghij"
244	got := string(c)
245	if got != want {
246		t.Errorf("got %q want %q", got, want)
247	}
248
249}
250
251// Wait for a change in /proc/self/mounts. Efficient through the use of
252// unix.Poll().
253func waitProcMountsChange() error {
254	fd, err := syscall.Open("/proc/self/mounts", syscall.O_RDONLY, 0)
255	defer syscall.Close(fd)
256	if err != nil {
257		return err
258	}
259	pollFds := []unix.PollFd{
260		{
261			Fd:     int32(fd),
262			Events: unix.POLLPRI,
263		},
264	}
265	_, err = unix.Poll(pollFds, 1000)
266	return err
267}
268
269// Wait until mountpoint "mnt" shows up /proc/self/mounts
270func waitMount(mnt string) error {
271	for {
272		err := waitProcMountsChange()
273		if err != nil {
274			return err
275		}
276		content, err := ioutil.ReadFile("/proc/self/mounts")
277		if err != nil {
278			return err
279		}
280		if bytes.Contains(content, []byte(mnt)) {
281			return nil
282		}
283	}
284}
285
286// There is a hang that appears when enabling CAP_PARALLEL_DIROPS on Linux
287// 4.15.0: https://github.com/hanwen/go-fuse/issues/281
288// The hang was originally triggered by gvfs-udisks2-volume-monitor. This
289// test emulates what gvfs-udisks2-volume-monitor does.
290func TestParallelDiropsHang(t *testing.T) {
291	// We do NOT want to use newTestCase() here because we need to know the
292	// mnt path before the filesystem is mounted
293	dir := testutil.TempDir()
294	orig := dir + "/orig"
295	mnt := dir + "/mnt"
296	if err := os.Mkdir(orig, 0755); err != nil {
297		t.Fatal(err)
298	}
299	if err := os.Mkdir(mnt, 0755); err != nil {
300		t.Fatal(err)
301	}
302	defer os.RemoveAll(dir)
303
304	// Unblock the goroutines onces the mount shows up in /proc/self/mounts
305	wait := make(chan struct{})
306	go func() {
307		err := waitMount(mnt)
308		if err != nil {
309			t.Error(err)
310		}
311		// Unblock the goroutines regardless of an error. We don't want to hang
312		// the test.
313		close(wait)
314	}()
315
316	// gvfs-udisks2-volume-monitor hits the mount with three threads - we try to
317	// emulate exactly what it does acc. to an strace log.
318	var wg sync.WaitGroup
319	wg.Add(3)
320	// [pid  2117] lstat(".../mnt/autorun.inf",  <unfinished ...>
321	go func() {
322		defer wg.Done()
323		<-wait
324		var st unix.Stat_t
325		unix.Lstat(mnt+"/autorun.inf", &st)
326	}()
327	// [pid  2116] open(".../mnt/.xdg-volume-info", O_RDONLY <unfinished ...>
328	go func() {
329		defer wg.Done()
330		<-wait
331		syscall.Open(mnt+"/.xdg-volume-info", syscall.O_RDONLY, 0)
332	}()
333	// 25 times this:
334	// [pid  1874] open(".../mnt", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC <unfinished ...>
335	// [pid  1874] fstat(11, {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
336	// [pid  1874] getdents(11, /* 2 entries */, 32768) = 48
337	// [pid  1874] close(11)                   = 0
338	go func() {
339		defer wg.Done()
340		<-wait
341		for i := 1; i <= 25; i++ {
342			f, err := os.Open(mnt)
343			if err != nil {
344				t.Error(err)
345				return
346			}
347			_, err = f.Stat()
348			if err != nil {
349				t.Error(err)
350				f.Close()
351				return
352			}
353			_, err = f.Readdirnames(-1)
354			if err != nil {
355				t.Errorf("iteration %d: fd %d: %v", i, f.Fd(), err)
356				return
357			}
358			f.Close()
359		}
360	}()
361
362	loopbackRoot, err := NewLoopbackRoot(orig)
363	if err != nil {
364		t.Fatalf("NewLoopbackRoot(%s): %v\n", orig, err)
365	}
366	sec := time.Second
367	opts := &Options{
368		AttrTimeout:  &sec,
369		EntryTimeout: &sec,
370	}
371	opts.Debug = testutil.VerboseTest()
372
373	rawFS := NewNodeFS(loopbackRoot, opts)
374	server, err := fuse.NewServer(rawFS, mnt, &opts.MountOptions)
375	if err != nil {
376		t.Fatal(err)
377	}
378	go server.Serve()
379
380	wg.Wait()
381	server.Unmount()
382}
383
384func TestRoMount(t *testing.T) {
385	tc := newTestCase(t, &testOptions{ro: true})
386	defer tc.Clean()
387}
388