diff --git a/internal/dir.go b/internal/dir.go index 98de75f1..5a349495 100644 --- a/internal/dir.go +++ b/internal/dir.go @@ -725,7 +725,7 @@ func (dh *DirHandle) ReadDir(internalOffset int, offset fuseops.DirOffset) (en * } } - if len(notifications) > 0 { + if len(notifications) > 0 && fs.NotifyCallback != nil { fs.NotifyCallback(notifications) } diff --git a/internal/goofys.go b/internal/goofys.go index b9223cd5..c5cbe8f5 100644 --- a/internal/goofys.go +++ b/internal/goofys.go @@ -867,7 +867,9 @@ func (fs *Goofys) RefreshInodeCache(inode *Inode) error { } dh.CloseDir() dh.mu.Unlock() - fs.NotifyCallback(notifications) + if fs.NotifyCallback != nil { + fs.NotifyCallback(notifications) + } return mappedErr } inode, err := parent.recheckInode(inode, name) @@ -884,7 +886,9 @@ func (fs *Goofys) RefreshInodeCache(inode *Inode) error { Name: name, }) } - fs.NotifyCallback(notifications) + if fs.NotifyCallback != nil { + fs.NotifyCallback(notifications) + } if mappedErr == syscall.ENOENT { // We don't mind if the file disappeared return nil diff --git a/internal/goofys_fs_test.go b/internal/goofys_fs_test.go index 98971778..fb28d0ee 100644 --- a/internal/goofys_fs_test.go +++ b/internal/goofys_fs_test.go @@ -23,8 +23,8 @@ import ( "io/fs" "io/ioutil" "os" - "os/exec" "path/filepath" + "runtime" "syscall" "time" @@ -90,7 +90,7 @@ func (s *GoofysTest) TestWriteAnonymousFuse(t *C) { t.Assert(err, NotNil) pathErr, ok := err.(*os.PathError) t.Assert(ok, Equals, true) - t.Assert(pathErr.Err, Equals, syscall.EACCES) + t.Assert(IsAccessDenied(pathErr.Err), Equals, true) err = file.Close() t.Assert(err, IsNil) @@ -281,61 +281,53 @@ func (s *GoofysTest) TestRmdirWithDiropen(t *C) { err = os.MkdirAll(mountPoint+"/dir2/dir5", 0700) t.Assert(err, IsNil) - //1, open dir5 + // 1, open dir5 dir := mountPoint + "/dir2/dir5" fh, err := os.Open(dir) t.Assert(err, IsNil) defer fh.Close() - cmd1 := exec.Command("ls", mountPoint+"/dir2") - //out, err := cmd.Output() - out1, err1 := cmd1.Output() - if err1 != nil { - if ee, ok := err.(*exec.ExitError); ok { - panic(ee.Stderr) - } - } - t.Assert(string(out1), DeepEquals, ""+"dir3\n"+"dir4\n"+"dir5\n") - - //2, rm -rf dir5 - cmd := exec.Command("rm", "-rf", dir) - _, err = cmd.Output() - if err != nil { - if ee, ok := err.(*exec.ExitError); ok { - panic(ee.Stderr) - } + fh2, err := os.Open(mountPoint + "/dir2") + t.Assert(err, IsNil) + defer fh2.Close() + names, err := fh2.Readdirnames(0) + t.Assert(err, IsNil) + t.Assert(names, DeepEquals, []string{"dir3", "dir4", "dir5"}) + fh2.Close() + + // 2, remove dir5 + if runtime.GOOS == "windows" { + // Can't remove opened files under Windows + err = fh.Close() + t.Assert(err, IsNil) } + err = os.RemoveAll(dir) + t.Assert(err, IsNil) - //3, readdir dir2 + // 3, readdir dir2 fh1, err := os.Open(mountPoint + "/dir2") t.Assert(err, IsNil) - defer func() { - // close the file if the test failed so we can unmount - if fh1 != nil { - fh1.Close() - } - }() - - names, err := fh1.Readdirnames(0) + defer fh1.Close() + names, err = fh1.Readdirnames(0) t.Assert(err, IsNil) t.Assert(names, DeepEquals, []string{"dir3", "dir4"}) - cmd = exec.Command("ls", mountPoint+"/dir2") - out, err := cmd.Output() - if err != nil { - if ee, ok := err.(*exec.ExitError); ok { - panic(ee.Stderr) - } - } - - t.Assert(string(out), DeepEquals, ""+"dir3\n"+"dir4\n") + fh2, err = os.Open(mountPoint + "/dir2") + t.Assert(err, IsNil) + names, err = fh2.Readdirnames(0) + t.Assert(err, IsNil) + t.Assert(names, DeepEquals, []string{"dir3", "dir4"}) + err = fh2.Close() + t.Assert(err, IsNil) err = fh1.Close() t.Assert(err, IsNil) - // 4,reset env - err = fh.Close() - t.Assert(err, IsNil) + // 4, reset env + if runtime.GOOS != "windows" { + err = fh.Close() + t.Assert(err, IsNil) + } err = os.RemoveAll(mountPoint + "/dir2/dir4") t.Assert(err, IsNil) @@ -353,12 +345,14 @@ func (s *GoofysTest) TestRmImplicitDir(t *C) { t.Assert(err, IsNil) defer os.Chdir(oldCwd) - dir, err := os.Open(mountPoint + "/test_rm_implicit_dir/dir2") - t.Assert(err, IsNil) - defer dir.Close() + if runtime.GOOS != "windows" { + dir, err := os.Open(mountPoint + "/test_rm_implicit_dir/dir2") + t.Assert(err, IsNil) + defer dir.Close() - err = dir.Chdir() - t.Assert(err, IsNil) + err = os.Chdir(mountPoint + "/test_rm_implicit_dir/dir2") + t.Assert(err, IsNil) + } err = os.RemoveAll(mountPoint + "/test_rm_implicit_dir/dir2") t.Assert(err, IsNil) diff --git a/internal/goofys_test.go b/internal/goofys_test.go index 2de1be1b..83d69571 100644 --- a/internal/goofys_test.go +++ b/internal/goofys_test.go @@ -1328,7 +1328,7 @@ func (s *GoofysTest) TestWriteAnonymous(t *C) { t.Assert(err, IsNil) err = in.SyncFile() - t.Assert(err, Equals, syscall.EACCES) + t.Assert(mapAwsError(err), Equals, syscall.EACCES) fh.Release() } @@ -1832,84 +1832,6 @@ func (s *GoofysTest) TestRead403(t *C) { t.Assert(mapAwsError(err), Equals, syscall.EACCES) } -func (s *GoofysTest) TestDirMTime(t *C) { - s.fs.flags.StatCacheTTL = 1 * time.Minute - // enable cheap to ensure GET dir/ will come back before LIST dir/ - s.fs.flags.Cheap = true - - root := s.getRoot(t) - t.Assert(time.Time{}.Before(root.Attributes.Mtime), Equals, true) - - dir1, err := s.fs.LookupPath("dir1") - t.Assert(err, IsNil) - - attr1 := dir1.GetAttributes() - m1 := attr1.Mtime - - time.Sleep(2 * time.Second) - - dir2, err := dir1.MkDir("dir2") - t.Assert(err, IsNil) - - attr2 := dir2.GetAttributes() - m2 := attr2.Mtime - t.Assert(m1.Add(2*time.Second).Before(m2), Equals, true) - - // dir1 didn't have an explicit mtime, so it should update now - // that we did a mkdir inside it - attr1 = dir1.GetAttributes() - m1 = attr1.Mtime - t.Assert(m1, Equals, m2) - - time.Sleep(2 * time.Second) - - // different dir2 - dir2, err = s.fs.LookupPath("dir2") - t.Assert(err, IsNil) - - attr2 = dir2.GetAttributes() - m2 = attr2.Mtime - - // this fails because we are listing dir/, which means we - // don't actually see the dir blob dir2/dir3/ (it's returned - // as common prefix), so we can't get dir3's mtime - if false { - // dir2/dir3/ exists and has mtime - s.readDirIntoCache(t, dir2.Id) - dir3, err := s.fs.LookupPath("dir2/dir3") - t.Assert(err, IsNil) - - attr3 := dir3.GetAttributes() - // setupDefaultEnv is before mounting - t.Assert(attr3.Mtime.Before(m2), Equals, true) - } - - time.Sleep(time.Second) - - params := &PutBlobInput{ - Key: "dir2/newfile", - Body: bytes.NewReader([]byte("foo")), - Size: PUInt64(3), - } - _, err = s.cloud.PutBlob(params) - t.Assert(err, IsNil) - - // dir2 could be already preloaded due to optimisations, it may have older mtime - // FIXME: (maybe) update parent directory modification times when flushing files inside them - s.fs.flags.StatCacheTTL = 1 * time.Second - s.readDirIntoCache(t, dir2.Id) - s.fs.flags.StatCacheTTL = 1 * time.Minute - - newfile, err := dir2.LookUp("newfile", false) - t.Assert(err, IsNil) - - attr2New := dir2.GetAttributes() - // mtime should reflect that of the latest object - // GCS can return nano second resolution so truncate to second for compare - t.Assert(attr2New.Mtime.Unix(), Equals, newfile.Attributes.Mtime.Unix()) - t.Assert(m2.Before(attr2New.Mtime), Equals, true) -} - func (s *GoofysTest) TestDirMTimeNoTTL(t *C) { if s.cloud.Capabilities().DirBlob { t.Skip("Tests for behavior without dir blob") diff --git a/internal/goofys_unix_test.go b/internal/goofys_unix_test.go index 103f5807..8c0a2758 100644 --- a/internal/goofys_unix_test.go +++ b/internal/goofys_unix_test.go @@ -104,6 +104,10 @@ func FsyncDir(dir string) error { return fh.Close() } +func IsAccessDenied(err error) bool { + return err == syscall.EACCES +} + func (s *GoofysTest) SetUpSuite(t *C) { s.tmp = os.Getenv("TMPDIR") if s.tmp == "" { @@ -714,3 +718,81 @@ func (s *GoofysTest) TestConcurrentRefDeref(t *C) { }) } } + +func (s *GoofysTest) TestDirMTime(t *C) { + s.fs.flags.StatCacheTTL = 1 * time.Minute + // enable cheap to ensure GET dir/ will come back before LIST dir/ + s.fs.flags.Cheap = true + + root := s.getRoot(t) + t.Assert(time.Time{}.Before(root.Attributes.Mtime), Equals, true) + + dir1, err := s.fs.LookupPath("dir1") + t.Assert(err, IsNil) + + attr1 := dir1.GetAttributes() + m1 := attr1.Mtime + + time.Sleep(2 * time.Second) + + dir2, err := dir1.MkDir("dir2") + t.Assert(err, IsNil) + + attr2 := dir2.GetAttributes() + m2 := attr2.Mtime + t.Assert(m1.Add(2*time.Second).Before(m2), Equals, true) + + // dir1 didn't have an explicit mtime, so it should update now + // that we did a mkdir inside it + attr1 = dir1.GetAttributes() + m1 = attr1.Mtime + t.Assert(m1, Equals, m2) + + time.Sleep(2 * time.Second) + + // different dir2 + dir2, err = s.fs.LookupPath("dir2") + t.Assert(err, IsNil) + + attr2 = dir2.GetAttributes() + m2 = attr2.Mtime + + // this fails because we are listing dir/, which means we + // don't actually see the dir blob dir2/dir3/ (it's returned + // as common prefix), so we can't get dir3's mtime + if false { + // dir2/dir3/ exists and has mtime + s.readDirIntoCache(t, dir2.Id) + dir3, err := s.fs.LookupPath("dir2/dir3") + t.Assert(err, IsNil) + + attr3 := dir3.GetAttributes() + // setupDefaultEnv is before mounting + t.Assert(attr3.Mtime.Before(m2), Equals, true) + } + + time.Sleep(time.Second) + + params := &PutBlobInput{ + Key: "dir2/newfile", + Body: bytes.NewReader([]byte("foo")), + Size: PUInt64(3), + } + _, err = s.cloud.PutBlob(params) + t.Assert(err, IsNil) + + // dir2 could be already preloaded due to optimisations, it may have older mtime + // FIXME: (maybe) update parent directory modification times when flushing files inside them + s.fs.flags.StatCacheTTL = 1 * time.Second + s.readDirIntoCache(t, dir2.Id) + s.fs.flags.StatCacheTTL = 1 * time.Minute + + newfile, err := dir2.LookUp("newfile", false) + t.Assert(err, IsNil) + + attr2New := dir2.GetAttributes() + // mtime should reflect that of the latest object + // GCS can return nano second resolution so truncate to second for compare + t.Assert(attr2New.Mtime.Unix(), Equals, newfile.Attributes.Mtime.Unix()) + t.Assert(m2.Before(attr2New.Mtime), Equals, true) +} diff --git a/internal/goofys_windows.go b/internal/goofys_windows.go index 29243ef4..9ec42ab9 100644 --- a/internal/goofys_windows.go +++ b/internal/goofys_windows.go @@ -72,7 +72,7 @@ func NewGoofysWin(fs *Goofys) *GoofysWin { fsint := &GoofysWin{ Goofys: fs, } - fsint.initCh = make(chan int) + fsint.initCh = make(chan int, 3) fs.NotifyCallback = func(notifications []interface{}) { fsint.Notify(notifications) } diff --git a/internal/goofys_windows_test.go b/internal/goofys_windows_test.go index 2816aed7..12c2b043 100644 --- a/internal/goofys_windows_test.go +++ b/internal/goofys_windows_test.go @@ -67,3 +67,7 @@ func FsyncDir(dir string) error { } return err } + +func IsAccessDenied(err error) bool { + return err == syscall.EACCES || err == syscall.ERROR_ACCESS_DENIED +}