Commit 88b431df authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Fixes for weekly.2011-12-02

* Use time.Duration/time.Time where possible

* Use new FileInfo mode encoding.
parent bde72938
...@@ -15,9 +15,9 @@ import ( ...@@ -15,9 +15,9 @@ import (
// Used for benchmarking. Returns milliseconds. // Used for benchmarking. Returns milliseconds.
func BulkStat(parallelism int, files []string) float64 { func BulkStat(parallelism int, files []string) float64 {
todo := make(chan string, len(files)) todo := make(chan string, len(files))
dts := make(chan int64, parallelism) dts := make(chan time.Duration, parallelism)
allStart := time.Nanoseconds() allStart := time.Now()
fmt.Printf("Statting %d files with %d threads\n", len(files), parallelism) fmt.Printf("Statting %d files with %d threads\n", len(files), parallelism)
for i := 0; i < parallelism; i++ { for i := 0; i < parallelism; i++ {
...@@ -28,12 +28,12 @@ func BulkStat(parallelism int, files []string) float64 { ...@@ -28,12 +28,12 @@ func BulkStat(parallelism int, files []string) float64 {
break break
} }
t := time.Nanoseconds() t := time.Now()
_, err := os.Lstat(fn) _, err := os.Lstat(fn)
if err != nil { if err != nil {
log.Fatal("All stats should succeed:", err) log.Fatal("All stats should succeed:", err)
} }
dts <- time.Nanoseconds() - t dts <- time.Now().Sub(t)
} }
}() }()
} }
...@@ -44,14 +44,14 @@ func BulkStat(parallelism int, files []string) float64 { ...@@ -44,14 +44,14 @@ func BulkStat(parallelism int, files []string) float64 {
total := 0.0 total := 0.0
for i := 0; i < len(files); i++ { for i := 0; i < len(files); i++ {
total += float64(<-dts) * 1e-6 total += (<-dts).Seconds() * 1e-3
} }
allEnd := time.Nanoseconds() allEnd := time.Now()
avg := total / float64(len(files)) avg := total / float64(len(files))
fmt.Printf("Elapsed: %f sec. Average stat %f ms\n", fmt.Printf("Elapsed: %f sec. Average stat %f ms\n",
float64(allEnd-allStart)*1e-9, avg) allEnd.Sub(allStart).Seconds(), avg)
return avg return avg
} }
...@@ -87,7 +87,7 @@ func AnalyzeBenchmarkRuns(times []float64) { ...@@ -87,7 +87,7 @@ func AnalyzeBenchmarkRuns(times []float64) {
len(times), avg, 2*stddev, median, perc10, perc90) len(times), avg, 2*stddev, median, perc10, perc90)
} }
func RunBulkStat(runs int, threads int, sleepTime float64, files []string) (results []float64) { func RunBulkStat(runs int, threads int, sleepTime time.Duration, files []string) (results []float64) {
runs++ runs++
for j := 0; j < runs; j++ { for j := 0; j < runs; j++ {
result := BulkStat(threads, files) result := BulkStat(threads, files)
...@@ -99,7 +99,7 @@ func RunBulkStat(runs int, threads int, sleepTime float64, files []string) (resu ...@@ -99,7 +99,7 @@ func RunBulkStat(runs int, threads int, sleepTime float64, files []string) (resu
if j < runs-1 { if j < runs-1 {
fmt.Printf("Sleeping %.2f seconds\n", sleepTime) fmt.Printf("Sleeping %.2f seconds\n", sleepTime)
time.Sleep(int64(sleepTime * 1e9)) time.Sleep(sleepTime)
} }
} }
return results return results
......
...@@ -24,15 +24,13 @@ type StatFs struct { ...@@ -24,15 +24,13 @@ type StatFs struct {
dirs map[string][]fuse.DirEntry dirs map[string][]fuse.DirEntry
} }
func (me *StatFs) add(name string, fi os.FileInfo) { func (me *StatFs) add(name string, a *fuse.Attr) {
name = strings.TrimRight(name, "/") name = strings.TrimRight(name, "/")
_, ok := me.entries[name] _, ok := me.entries[name]
if ok { if ok {
return return
} }
a := &fuse.Attr{}
a.FromFileInfo(&fi)
me.entries[name] = a me.entries[name] = a
if name == "/" || name == "" { if name == "/" || name == "" {
return return
...@@ -40,8 +38,8 @@ func (me *StatFs) add(name string, fi os.FileInfo) { ...@@ -40,8 +38,8 @@ func (me *StatFs) add(name string, fi os.FileInfo) {
dir, base := filepath.Split(name) dir, base := filepath.Split(name)
dir = strings.TrimRight(dir, "/") dir = strings.TrimRight(dir, "/")
me.dirs[dir] = append(me.dirs[dir], fuse.DirEntry{Name: base, Mode: fi.Mode}) me.dirs[dir] = append(me.dirs[dir], fuse.DirEntry{Name: base, Mode: a.Mode})
me.add(dir, os.FileInfo{Mode: fuse.S_IFDIR | 0755}) me.add(dir, &fuse.Attr{Mode: fuse.S_IFDIR | 0755})
} }
func (me *StatFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) { func (me *StatFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
...@@ -97,7 +95,7 @@ func TestNewStatFs(t *testing.T) { ...@@ -97,7 +95,7 @@ func TestNewStatFs(t *testing.T) {
for _, n := range []string{ for _, n := range []string{
"file.txt", "sub/dir/foo.txt", "file.txt", "sub/dir/foo.txt",
"sub/dir/bar.txt", "sub/marine.txt"} { "sub/dir/bar.txt", "sub/marine.txt"} {
fs.add(n, os.FileInfo{Mode: fuse.S_IFREG | 0644}) fs.add(n, &fuse.Attr{Mode: fuse.S_IFREG | 0644})
} }
wd, clean := setupFs(fs, nil) wd, clean := setupFs(fs, nil)
...@@ -111,7 +109,7 @@ func TestNewStatFs(t *testing.T) { ...@@ -111,7 +109,7 @@ func TestNewStatFs(t *testing.T) {
fi, err := os.Lstat(wd + "/sub") fi, err := os.Lstat(wd + "/sub")
CheckSuccess(err) CheckSuccess(err)
if !fi.IsDirectory() { if !fi.IsDir() {
t.Error("mode", fi) t.Error("mode", fi)
} }
names, err = ioutil.ReadDir(wd + "/sub") names, err = ioutil.ReadDir(wd + "/sub")
...@@ -127,7 +125,7 @@ func TestNewStatFs(t *testing.T) { ...@@ -127,7 +125,7 @@ func TestNewStatFs(t *testing.T) {
fi, err = os.Lstat(wd + "/sub/marine.txt") fi, err = os.Lstat(wd + "/sub/marine.txt")
CheckSuccess(err) CheckSuccess(err)
if !fi.IsRegular() { if fi.Mode()&os.ModeType != 0 {
t.Error("mode", fi) t.Error("mode", fi)
} }
} }
...@@ -161,7 +159,7 @@ func BenchmarkGoFuseThreadedStat(b *testing.B) { ...@@ -161,7 +159,7 @@ func BenchmarkGoFuseThreadedStat(b *testing.B) {
fs := NewStatFs() fs := NewStatFs()
files := GetTestLines() files := GetTestLines()
for _, fn := range files { for _, fn := range files {
fs.add(fn, os.FileInfo{Mode: fuse.S_IFREG | 0644}) fs.add(fn, &fuse.Attr{Mode: fuse.S_IFREG | 0644})
} }
if len(files) == 0 { if len(files) == 0 {
log.Fatal("no files added") log.Fatal("no files added")
...@@ -169,10 +167,10 @@ func BenchmarkGoFuseThreadedStat(b *testing.B) { ...@@ -169,10 +167,10 @@ func BenchmarkGoFuseThreadedStat(b *testing.B) {
log.Printf("Read %d file names", len(files)) log.Printf("Read %d file names", len(files))
ttl := 0.1 ttl := 100 * time.Millisecond
opts := fuse.FileSystemOptions{ opts := fuse.FileSystemOptions{
EntryTimeout: ttl, EntryTimeout: ttl.Seconds(),
AttrTimeout: ttl, AttrTimeout: ttl.Seconds(),
NegativeTimeout: 0.0, NegativeTimeout: 0.0,
} }
wd, clean := setupFs(fs, &opts) wd, clean := setupFs(fs, &opts)
...@@ -184,11 +182,11 @@ func BenchmarkGoFuseThreadedStat(b *testing.B) { ...@@ -184,11 +182,11 @@ func BenchmarkGoFuseThreadedStat(b *testing.B) {
log.Println("N = ", b.N) log.Println("N = ", b.N)
threads := runtime.GOMAXPROCS(0) threads := runtime.GOMAXPROCS(0)
results := TestingBOnePass(b, threads, ttl*1.2, files) results := TestingBOnePass(b, threads, time.Duration((ttl*120)/100), files)
AnalyzeBenchmarkRuns(results) AnalyzeBenchmarkRuns(results)
} }
func TestingBOnePass(b *testing.B, threads int, sleepTime float64, files []string) (results []float64) { func TestingBOnePass(b *testing.B, threads int, sleepTime time.Duration, files []string) (results []float64) {
runs := b.N + 1 runs := b.N + 1
for j := 0; j < runs; j++ { for j := 0; j < runs; j++ {
if j > 0 { if j > 0 {
...@@ -203,8 +201,8 @@ func TestingBOnePass(b *testing.B, threads int, sleepTime float64, files []strin ...@@ -203,8 +201,8 @@ func TestingBOnePass(b *testing.B, threads int, sleepTime float64, files []strin
} }
if j < runs-1 { if j < runs-1 {
fmt.Printf("Sleeping %.2f seconds\n", sleepTime) fmt.Printf("Sleeping %.2f seconds\n", sleepTime.Seconds())
time.Sleep(int64(sleepTime * 1e9)) time.Sleep(sleepTime)
} }
} }
return results return results
...@@ -260,6 +258,6 @@ func BenchmarkCFuseThreadedStat(b *testing.B) { ...@@ -260,6 +258,6 @@ func BenchmarkCFuseThreadedStat(b *testing.B) {
ttl := 1.0 ttl := 1.0
log.Println("N = ", b.N) log.Println("N = ", b.N)
threads := runtime.GOMAXPROCS(0) threads := runtime.GOMAXPROCS(0)
results := TestingBOnePass(b, threads, ttl*1.2, lines) results := TestingBOnePass(b, threads, time.Duration((ttl*12)/10), lines)
AnalyzeBenchmarkRuns(results) AnalyzeBenchmarkRuns(results)
} }
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/unionfs" "github.com/hanwen/go-fuse/unionfs"
"os" "os"
"time"
) )
func main() { func main() {
...@@ -28,16 +29,16 @@ func main() { ...@@ -28,16 +29,16 @@ func main() {
os.Exit(2) os.Exit(2)
} }
ufsOptions := unionfs.UnionFsOptions{ ufsOptions := unionfs.UnionFsOptions{
DeletionCacheTTLSecs: *delcache_ttl, DeletionCacheTTL: time.Duration(*delcache_ttl * float64(time.Second)),
BranchCacheTTLSecs: *branchcache_ttl, BranchCacheTTL: time.Duration(*branchcache_ttl * float64(time.Second)),
DeletionDirName: *deldirname, DeletionDirName: *deldirname,
} }
options := unionfs.AutoUnionFsOptions{ options := unionfs.AutoUnionFsOptions{
UnionFsOptions: ufsOptions, UnionFsOptions: ufsOptions,
FileSystemOptions: fuse.FileSystemOptions{ FileSystemOptions: fuse.FileSystemOptions{
EntryTimeout: 1.0, EntryTimeout: time.Second,
AttrTimeout: 1.0, AttrTimeout: time.Second,
NegativeTimeout: 1.0, NegativeTimeout: time.Second,
Owner: fuse.CurrentOwner(), Owner: fuse.CurrentOwner(),
}, },
UpdateOnMount: true, UpdateOnMount: true,
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"log" "log"
"os" "os"
"runtime" "runtime"
"time"
) )
func main() { func main() {
...@@ -39,6 +40,8 @@ func main() { ...@@ -39,6 +40,8 @@ func main() {
files = append(files, string(l)) files = append(files, string(l))
} }
results := fuse.RunBulkStat(*runs, *threads, *sleepTime, files) d := time.Duration(*sleepTime * float64(time.Second))
results := fuse.RunBulkStat(*runs, *threads, d, files)
fuse.AnalyzeBenchmarkRuns(results) fuse.AnalyzeBenchmarkRuns(results)
} }
...@@ -33,9 +33,9 @@ func main() { ...@@ -33,9 +33,9 @@ func main() {
opts := &fuse.FileSystemOptions{ opts := &fuse.FileSystemOptions{
// These options are to be compatible with libfuse defaults, // These options are to be compatible with libfuse defaults,
// making benchmarking easier. // making benchmarking easier.
NegativeTimeout: 1.0, NegativeTimeout: time.Second,
AttrTimeout: 1.0, AttrTimeout: time.Second,
EntryTimeout: 1.0, EntryTimeout: time.Second,
} }
pathFs := fuse.NewPathNodeFs(finalFs, nil) pathFs := fuse.NewPathNodeFs(finalFs, nil)
conn := fuse.NewFileSystemConnector(pathFs, opts) conn := fuse.NewFileSystemConnector(pathFs, opts)
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"github.com/hanwen/go-fuse/unionfs" "github.com/hanwen/go-fuse/unionfs"
"log" "log"
"os" "os"
"time"
) )
func main() { func main() {
...@@ -27,9 +28,9 @@ func main() { ...@@ -27,9 +28,9 @@ func main() {
} }
ufsOptions := unionfs.UnionFsOptions{ ufsOptions := unionfs.UnionFsOptions{
DeletionCacheTTLSecs: *delcache_ttl, DeletionCacheTTL: time.Duration(*delcache_ttl * float64(time.Second)),
BranchCacheTTLSecs: *branchcache_ttl, BranchCacheTTL: time.Duration(*branchcache_ttl * float64(time.Second)),
DeletionDirName: *deldirname, DeletionDirName: *deldirname,
} }
ufs, err := unionfs.NewUnionFsFromRoots(flag.Args()[1:], &ufsOptions, true) ufs, err := unionfs.NewUnionFsFromRoots(flag.Args()[1:], &ufsOptions, true)
...@@ -39,9 +40,9 @@ func main() { ...@@ -39,9 +40,9 @@ func main() {
} }
nodeFs := fuse.NewPathNodeFs(ufs, &fuse.PathNodeFsOptions{ClientInodes: true}) nodeFs := fuse.NewPathNodeFs(ufs, &fuse.PathNodeFsOptions{ClientInodes: true})
mOpts := fuse.FileSystemOptions{ mOpts := fuse.FileSystemOptions{
EntryTimeout: *entry_ttl, EntryTimeout: time.Duration(*entry_ttl * float64(time.Second)),
AttrTimeout: *entry_ttl, AttrTimeout: time.Duration(*entry_ttl * float64(time.Second)),
NegativeTimeout: *negative_ttl, NegativeTimeout: time.Duration(*negative_ttl * float64(time.Second)),
PortableInodes: *portable, PortableInodes: *portable,
} }
mountState, _, err := fuse.MountNodeFileSystem(flag.Arg(0), nodeFs, &mOpts) mountState, _, err := fuse.MountNodeFileSystem(flag.Arg(0), nodeFs, &mOpts)
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
package fuse package fuse
import () import (
"time"
)
// Types for users to implement. // Types for users to implement.
...@@ -191,9 +193,9 @@ type WithFlags struct { ...@@ -191,9 +193,9 @@ type WithFlags struct {
// default copied from libfuse and set in NewMountOptions() is // default copied from libfuse and set in NewMountOptions() is
// (1s,1s,0s). // (1s,1s,0s).
type FileSystemOptions struct { type FileSystemOptions struct {
EntryTimeout float64 EntryTimeout time.Duration
AttrTimeout float64 AttrTimeout time.Duration
NegativeTimeout float64 NegativeTimeout time.Duration
// If set, replace all uids with given UID. // If set, replace all uids with given UID.
// NewFileSystemOptions() will set this to the daemon's // NewFileSystemOptions() will set this to the daemon's
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"log" "log"
"os" "os"
"syscall" "syscall"
"time"
) )
type FileMode uint32 type FileMode uint32
...@@ -35,8 +36,8 @@ func (me FileMode) IsFifo() bool { return (uint32(me) & syscall.S_IFMT) == sysca ...@@ -35,8 +36,8 @@ func (me FileMode) IsFifo() bool { return (uint32(me) & syscall.S_IFMT) == sysca
// IsChar reports whether the FileInfo describes a character special file. // IsChar reports whether the FileInfo describes a character special file.
func (me FileMode) IsChar() bool { return (uint32(me) & syscall.S_IFMT) == syscall.S_IFCHR } func (me FileMode) IsChar() bool { return (uint32(me) & syscall.S_IFMT) == syscall.S_IFCHR }
// IsDirectory reports whether the FileInfo describes a directory. // IsDir reports whether the FileInfo describes a directory.
func (me FileMode) IsDirectory() bool { return (uint32(me) & syscall.S_IFMT) == syscall.S_IFDIR } func (me FileMode) IsDir() bool { return (uint32(me) & syscall.S_IFMT) == syscall.S_IFDIR }
// IsBlock reports whether the FileInfo describes a block special file. // IsBlock reports whether the FileInfo describes a block special file.
func (me FileMode) IsBlock() bool { return (uint32(me) & syscall.S_IFMT) == syscall.S_IFBLK } func (me FileMode) IsBlock() bool { return (uint32(me) & syscall.S_IFMT) == syscall.S_IFBLK }
...@@ -55,8 +56,8 @@ func (me *Attr) IsFifo() bool { return (uint32(me.Mode) & syscall.S_IFMT) == sys ...@@ -55,8 +56,8 @@ func (me *Attr) IsFifo() bool { return (uint32(me.Mode) & syscall.S_IFMT) == sys
// IsChar reports whether the FileInfo describes a character special file. // IsChar reports whether the FileInfo describes a character special file.
func (me *Attr) IsChar() bool { return (uint32(me.Mode) & syscall.S_IFMT) == syscall.S_IFCHR } func (me *Attr) IsChar() bool { return (uint32(me.Mode) & syscall.S_IFMT) == syscall.S_IFCHR }
// IsDirectory reports whether the FileInfo describes a directory. // IsDir reports whether the FileInfo describes a directory.
func (me *Attr) IsDirectory() bool { return (uint32(me.Mode) & syscall.S_IFMT) == syscall.S_IFDIR } func (me *Attr) IsDir() bool { return (uint32(me.Mode) & syscall.S_IFMT) == syscall.S_IFDIR }
// IsBlock reports whether the FileInfo describes a block special file. // IsBlock reports whether the FileInfo describes a block special file.
func (me *Attr) IsBlock() bool { return (uint32(me.Mode) & syscall.S_IFMT) == syscall.S_IFBLK } func (me *Attr) IsBlock() bool { return (uint32(me.Mode) & syscall.S_IFMT) == syscall.S_IFBLK }
...@@ -82,7 +83,7 @@ func (a *Attr) Ctimens() int64 { ...@@ -82,7 +83,7 @@ func (a *Attr) Ctimens() int64 {
return int64(1e9*a.Ctime) + int64(a.Ctimensec) return int64(1e9*a.Ctime) + int64(a.Ctimensec)
} }
func (a *Attr) SetTimes(atimens int64, mtimens int64, ctimens int64) { func (a *Attr) SetNs(atimens int64, mtimens int64, ctimens int64) {
if atimens >= 0 { if atimens >= 0 {
a.Atime = uint64(atimens / 1e9) a.Atime = uint64(atimens / 1e9)
a.Atimensec = uint32(atimens % 1e9) a.Atimensec = uint32(atimens % 1e9)
...@@ -97,31 +98,69 @@ func (a *Attr) SetTimes(atimens int64, mtimens int64, ctimens int64) { ...@@ -97,31 +98,69 @@ func (a *Attr) SetTimes(atimens int64, mtimens int64, ctimens int64) {
} }
} }
func (attr *Attr) FromFileInfo(fi *os.FileInfo) { func (a *Attr) SetTimes(access *time.Time, mod *time.Time, chstatus *time.Time) {
attr.Ino = uint64(fi.Ino) if access != nil {
attr.Size = uint64(fi.Size) atimens := access.UnixNano()
attr.Blocks = uint64(fi.Blocks) a.Atime = uint64(atimens / 1e9)
attr.SetTimes(fi.Atime_ns, fi.Mtime_ns, fi.Ctime_ns) a.Atimensec = uint32(atimens % 1e9)
attr.Mode = fi.Mode }
attr.Nlink = uint32(fi.Nlink) if mod != nil {
attr.Uid = uint32(fi.Uid) mtimens := mod.UnixNano()
attr.Gid = uint32(fi.Gid) a.Mtime = uint64(mtimens / 1e9)
attr.Rdev = uint32(fi.Rdev) a.Mtimensec = uint32(mtimens % 1e9)
attr.Blksize = uint32(fi.Blksize) }
if chstatus != nil {
ctimens := chstatus.UnixNano()
a.Ctime = uint64(ctimens / 1e9)
a.Ctimensec = uint32(ctimens % 1e9)
}
}
func (a *Attr) FromStat(s *syscall.Stat_t) {
a.Ino = uint64(s.Ino)
a.Size = uint64(s.Size)
a.Blocks = uint64(s.Blocks)
a.Atime = uint64(s.Atim.Sec)
a.Atimensec = uint32(s.Atim.Nsec)
a.Mtime = uint64(s.Mtim.Sec)
a.Mtimensec = uint32(s.Mtim.Nsec)
a.Ctime = uint64(s.Ctim.Sec)
a.Ctimensec = uint32(s.Ctim.Nsec)
a.Mode = s.Mode
a.Nlink = uint32(s.Nlink)
a.Uid = uint32(s.Uid)
a.Gid = uint32(s.Gid)
a.Rdev = uint32(s.Rdev)
a.Blksize = uint32(s.Blksize)
} }
func (a *Attr) ToFileInfo() (fi *os.FileInfo) {
return &os.FileInfo{ func (a *Attr) FromFileInfo(fi os.FileInfo) {
Ino: a.Ino, stat := fi.(*os.FileStat)
Size: int64(a.Size), sys := stat.Sys.(*syscall.Stat_t)
Atime_ns: a.Atimens(), a.FromStat(sys)
Mtime_ns: a.Mtimens(), }
Ctime_ns: a.Ctimens(),
Blocks: int64(a.Blocks), func (a *Attr) ChangeTime() time.Time {
Mode: a.Mode, return time.Unix(int64(a.Ctime), int64(a.Ctimensec))
Nlink: uint64(a.Nlink), }
Uid: int(a.Uid),
Gid: int(a.Gid), func (a *Attr) AccessTime() time.Time {
Rdev: uint64(a.Rdev), return time.Unix(int64(a.Atime), int64(a.Atimensec))
Blksize: int64(a.Blksize), }
func (a *Attr) ModTime() time.Time {
return time.Unix(int64(a.Mtime), int64(a.Mtimensec))
}
func ToStatT(f os.FileInfo) *syscall.Stat_t {
return f.(*os.FileStat).Sys.(*syscall.Stat_t)
}
func ToAttr(f os.FileInfo) *Attr {
if f == nil {
return nil
} }
a := &Attr{}
a.FromStat(ToStatT(f))
return a
} }
...@@ -74,7 +74,6 @@ func TestCacheFs(t *testing.T) { ...@@ -74,7 +74,6 @@ func TestCacheFs(t *testing.T) {
c, err = ioutil.ReadFile(wd + "/mnt/file.txt") c, err = ioutil.ReadFile(wd + "/mnt/file.txt")
CheckSuccess(err) CheckSuccess(err)
// x
if string(c) != "hello" { if string(c) != "hello" {
t.Fatalf("Page cache skipped: expect 'hello' %q", string(c)) t.Fatalf("Page cache skipped: expect 'hello' %q", string(c))
} }
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"log" "log"
"path/filepath" "path/filepath"
"strings" "strings"
"time"
"unsafe" "unsafe"
) )
...@@ -43,9 +44,9 @@ type FileSystemConnector struct { ...@@ -43,9 +44,9 @@ type FileSystemConnector struct {
func NewFileSystemOptions() *FileSystemOptions { func NewFileSystemOptions() *FileSystemOptions {
return &FileSystemOptions{ return &FileSystemOptions{
NegativeTimeout: 0.0, NegativeTimeout: 0,
AttrTimeout: 1.0, AttrTimeout: time.Second,
EntryTimeout: 1.0, EntryTimeout: time.Second,
Owner: CurrentOwner(), Owner: CurrentOwner(),
} }
} }
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"os" "os"
"syscall" "syscall"
"testing" "testing"
"time"
) )
type MutableDataFile struct { type MutableDataFile struct {
...@@ -58,7 +59,7 @@ func (me *MutableDataFile) Fsync(*FsyncIn) (code Status) { ...@@ -58,7 +59,7 @@ func (me *MutableDataFile) Fsync(*FsyncIn) (code Status) {
} }
func (me *MutableDataFile) Utimens(atimeNs int64, mtimeNs int64) Status { func (me *MutableDataFile) Utimens(atimeNs int64, mtimeNs int64) Status {
me.Attr.SetTimes(atimeNs, mtimeNs, -1) me.Attr.SetNs(atimeNs, mtimeNs, -1)
return OK return OK
} }
...@@ -175,7 +176,7 @@ func TestFSetAttr(t *testing.T) { ...@@ -175,7 +176,7 @@ func TestFSetAttr(t *testing.T) {
t.Error("chmod") t.Error("chmod")
} }
err = os.Chtimes(fn, 100e3, 101e3) err = os.Chtimes(fn, time.Unix(0, 100e3), time.Unix(0, 101e3))
CheckSuccess(err) CheckSuccess(err)
if fs.file.Attr.Atimensec != 100e3 || fs.file.Attr.Mtimensec != 101e3 { if fs.file.Attr.Atimensec != 100e3 || fs.file.Attr.Mtimensec != 101e3 {
t.Errorf("Utimens: atime %d != 100e3 mtime %d != 101e3", t.Errorf("Utimens: atime %d != 100e3 mtime %d != 101e3",
...@@ -184,8 +185,10 @@ func TestFSetAttr(t *testing.T) { ...@@ -184,8 +185,10 @@ func TestFSetAttr(t *testing.T) {
newFi, err := f.Stat() newFi, err := f.Stat()
CheckSuccess(err) CheckSuccess(err)
if fi.Ino != newFi.Ino { i1 := ToStatT(fi).Ino
t.Errorf("f.Lstat().Ino = %d. Returned %d before.", newFi.Ino, fi.Ino) i2 := ToStatT(newFi).Ino
if i1 != i2 {
t.Errorf("f.Lstat().Ino = %d. Returned %d before.", i2, i1)
} }
// TODO - test chown if run as root. // TODO - test chown if run as root.
} }
...@@ -63,10 +63,10 @@ func (me *fileSystemMount) fileInfoToEntry(attr *Attr) (out *EntryOut) { ...@@ -63,10 +63,10 @@ func (me *fileSystemMount) fileInfoToEntry(attr *Attr) (out *EntryOut) {
out = &EntryOut{} out = &EntryOut{}
out.Attr = *attr out.Attr = *attr
splitNs(me.options.EntryTimeout, &out.EntryValid, &out.EntryValidNsec) splitDuration(me.options.EntryTimeout, &out.EntryValid, &out.EntryValidNsec)
splitNs(me.options.AttrTimeout, &out.AttrValid, &out.AttrValidNsec) splitDuration(me.options.AttrTimeout, &out.AttrValid, &out.AttrValidNsec)
me.setOwner(&out.Attr) me.setOwner(&out.Attr)
if !attr.IsDirectory() && attr.Nlink == 0 { if !attr.IsDir() && attr.Nlink == 0 {
out.Nlink = 1 out.Nlink = 1
} }
return out return out
...@@ -75,7 +75,7 @@ func (me *fileSystemMount) fileInfoToEntry(attr *Attr) (out *EntryOut) { ...@@ -75,7 +75,7 @@ func (me *fileSystemMount) fileInfoToEntry(attr *Attr) (out *EntryOut) {
func (me *fileSystemMount) fillAttr(a *Attr, nodeId uint64) (out *AttrOut) { func (me *fileSystemMount) fillAttr(a *Attr, nodeId uint64) (out *AttrOut) {
out = &AttrOut{} out = &AttrOut{}
out.Attr = *a out.Attr = *a
splitNs(me.options.AttrTimeout, &out.AttrValid, &out.AttrValidNsec) splitDuration(me.options.AttrTimeout, &out.AttrValid, &out.AttrValidNsec)
me.setOwner(&out.Attr) me.setOwner(&out.Attr)
out.Ino = nodeId out.Ino = nodeId
return out return out
...@@ -146,7 +146,7 @@ func (me *fileSystemMount) negativeEntry() (*EntryOut, Status) { ...@@ -146,7 +146,7 @@ func (me *fileSystemMount) negativeEntry() (*EntryOut, Status) {
if me.options.NegativeTimeout > 0.0 { if me.options.NegativeTimeout > 0.0 {
out := new(EntryOut) out := new(EntryOut)
out.NodeId = 0 out.NodeId = 0
splitNs(me.options.NegativeTimeout, &out.EntryValid, &out.EntryValidNsec) splitDuration(me.options.NegativeTimeout, &out.EntryValid, &out.EntryValidNsec)
return out, OK return out, OK
} }
return nil, ENOENT return nil, ENOENT
......
...@@ -152,7 +152,7 @@ func (me *FileSystemConnector) SetAttr(header *InHeader, input *SetAttrIn) (out ...@@ -152,7 +152,7 @@ func (me *FileSystemConnector) SetAttr(header *InHeader, input *SetAttrIn) (out
if code.Ok() && (input.Valid&(FATTR_ATIME|FATTR_MTIME|FATTR_ATIME_NOW|FATTR_MTIME_NOW) != 0) { if code.Ok() && (input.Valid&(FATTR_ATIME|FATTR_MTIME|FATTR_ATIME_NOW|FATTR_MTIME_NOW) != 0) {
now := int64(0) now := int64(0)
if input.Valid&FATTR_ATIME_NOW != 0 || input.Valid&FATTR_MTIME_NOW != 0 { if input.Valid&FATTR_ATIME_NOW != 0 || input.Valid&FATTR_MTIME_NOW != 0 {
now = time.Nanoseconds() now = time.Now().UnixNano()
} }
atime := int64(input.Atime*1e9) + int64(input.Atimensec) atime := int64(input.Atime*1e9) + int64(input.Atimensec)
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"syscall" "syscall"
"time"
) )
var _ = fmt.Println var _ = fmt.Println
...@@ -36,7 +37,7 @@ func (me *LoopbackFileSystem) GetPath(relPath string) string { ...@@ -36,7 +37,7 @@ func (me *LoopbackFileSystem) GetPath(relPath string) string {
func (me *LoopbackFileSystem) GetAttr(name string, context *Context) (a *Attr, code Status) { func (me *LoopbackFileSystem) GetAttr(name string, context *Context) (a *Attr, code Status) {
fullPath := me.GetPath(name) fullPath := me.GetPath(name)
var err error = nil var err error = nil
var fi *os.FileInfo var fi os.FileInfo
if name == "" { if name == "" {
// When GetAttr is called for the toplevel directory, we always want // When GetAttr is called for the toplevel directory, we always want
// to look through symlinks. // to look through symlinks.
...@@ -65,9 +66,10 @@ func (me *LoopbackFileSystem) OpenDir(name string, context *Context) (stream cha ...@@ -65,9 +66,10 @@ func (me *LoopbackFileSystem) OpenDir(name string, context *Context) (stream cha
for { for {
infos, err := f.Readdir(want) infos, err := f.Readdir(want)
for i, _ := range infos { for i, _ := range infos {
s := ToStatT(infos[i])
output <- DirEntry{ output <- DirEntry{
Name: infos[i].Name, Name: infos[i].Name(),
Mode: infos[i].Mode, Mode: s.Mode,
} }
} }
if len(infos) < want || err == io.EOF { if len(infos) < want || err == io.EOF {
...@@ -107,7 +109,7 @@ func (me *LoopbackFileSystem) Truncate(path string, offset uint64, context *Cont ...@@ -107,7 +109,7 @@ func (me *LoopbackFileSystem) Truncate(path string, offset uint64, context *Cont
} }
func (me *LoopbackFileSystem) Utimens(path string, AtimeNs int64, MtimeNs int64, context *Context) (code Status) { func (me *LoopbackFileSystem) Utimens(path string, AtimeNs int64, MtimeNs int64, context *Context) (code Status) {
return ToStatus(os.Chtimes(me.GetPath(path), int64(AtimeNs), int64(MtimeNs))) return ToStatus(os.Chtimes(me.GetPath(path), time.Unix(0, AtimeNs), time.Unix(0, MtimeNs)))
} }
func (me *LoopbackFileSystem) Readlink(name string, context *Context) (out string, code Status) { func (me *LoopbackFileSystem) Readlink(name string, context *Context) (out string, code Status) {
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
"strings" "strings"
"syscall" "syscall"
"testing" "testing"
"time"
) )
var _ = strings.Join var _ = strings.Join
...@@ -39,7 +40,7 @@ type testCase struct { ...@@ -39,7 +40,7 @@ type testCase struct {
connector *FileSystemConnector connector *FileSystemConnector
} }
const testTtl = 0.1 const testTtl = 100 * time.Millisecond
// Create and mount filesystem. // Create and mount filesystem.
func NewTestCase(t *testing.T) *testCase { func NewTestCase(t *testing.T) *testCase {
...@@ -123,11 +124,12 @@ func TestTouch(t *testing.T) { ...@@ -123,11 +124,12 @@ func TestTouch(t *testing.T) {
err := ioutil.WriteFile(ts.origFile, []byte(contents), 0700) err := ioutil.WriteFile(ts.origFile, []byte(contents), 0700)
CheckSuccess(err) CheckSuccess(err)
err = os.Chtimes(ts.mountFile, 42e9, 43e9) err = os.Chtimes(ts.mountFile, time.Unix(42, 0), time.Unix(43, 0))
CheckSuccess(err) CheckSuccess(err)
fi, err := os.Lstat(ts.mountFile) fi, err := os.Lstat(ts.mountFile)
CheckSuccess(err) CheckSuccess(err)
if fi.Atime_ns != 42e9 || fi.Mtime_ns != 43e9 { stat := fi.(*os.FileStat).Sys.(*syscall.Stat_t)
if stat.Atim.Sec != 42 || stat.Mtim.Sec != 43 {
t.Errorf("Got wrong timestamps %v", fi) t.Errorf("Got wrong timestamps %v", fi)
} }
} }
...@@ -144,8 +146,8 @@ func (me *testCase) TestReadThrough(t *testing.T) { ...@@ -144,8 +146,8 @@ func (me *testCase) TestReadThrough(t *testing.T) {
fi, err := os.Lstat(ts.mountFile) fi, err := os.Lstat(ts.mountFile)
CheckSuccess(err) CheckSuccess(err)
if (fi.Mode & 0777) != mode { if uint32(fi.Mode().Perm()) != mode {
t.Errorf("Wrong mode %o != %o", fi.Mode, mode) t.Errorf("Wrong mode %o != %o", int(fi.Mode().Perm()), mode)
} }
// Open (for read), read. // Open (for read), read.
...@@ -193,8 +195,8 @@ func TestWriteThrough(t *testing.T) { ...@@ -193,8 +195,8 @@ func TestWriteThrough(t *testing.T) {
} }
fi, err := os.Lstat(me.origFile) fi, err := os.Lstat(me.origFile)
if fi.Mode&0777 != 0644 { if fi.Mode().Perm() != 0644 {
t.Errorf("create mode error %o", fi.Mode&0777) t.Errorf("create mode error %o", fi.Mode()&0777)
} }
f, err = os.Open(me.origFile) f, err = os.Open(me.origFile)
...@@ -218,8 +220,8 @@ func TestMkdirRmdir(t *testing.T) { ...@@ -218,8 +220,8 @@ func TestMkdirRmdir(t *testing.T) {
err := os.Mkdir(me.mountSubdir, 0777) err := os.Mkdir(me.mountSubdir, 0777)
CheckSuccess(err) CheckSuccess(err)
fi, err := os.Lstat(me.origSubdir) fi, err := os.Lstat(me.origSubdir)
if !fi.IsDirectory() { if !fi.IsDir() {
t.Errorf("Not a directory: %o", fi.Mode) t.Errorf("Not a directory: %v", fi)
} }
err = os.Remove(me.mountSubdir) err = os.Remove(me.mountSubdir)
...@@ -240,11 +242,13 @@ func TestLinkCreate(t *testing.T) { ...@@ -240,11 +242,13 @@ func TestLinkCreate(t *testing.T) {
err = os.Link(me.mountFile, mountSubfile) err = os.Link(me.mountFile, mountSubfile)
CheckSuccess(err) CheckSuccess(err)
subfi, err := os.Lstat(mountSubfile) subStat, err := os.Lstat(mountSubfile)
CheckSuccess(err) CheckSuccess(err)
fi, err := os.Lstat(me.mountFile) stat, err := os.Lstat(me.mountFile)
CheckSuccess(err) CheckSuccess(err)
subfi := ToStatT(subStat)
fi := ToStatT(stat)
if fi.Nlink != 2 { if fi.Nlink != 2 {
t.Errorf("Expect 2 links: %v", fi) t.Errorf("Expect 2 links: %v", fi)
} }
...@@ -282,8 +286,11 @@ func TestLinkExisting(t *testing.T) { ...@@ -282,8 +286,11 @@ func TestLinkExisting(t *testing.T) {
CheckSuccess(err) CheckSuccess(err)
f2, err := os.Lstat(me.mnt + "/file2") f2, err := os.Lstat(me.mnt + "/file2")
CheckSuccess(err) CheckSuccess(err)
if f1.Ino != f2.Ino {
t.Errorf("linked files should have identical inodes %v %v", f1.Ino, f2.Ino) s1 := ToStatT(f1)
s2 := ToStatT(f2)
if s1.Ino != s2.Ino {
t.Errorf("linked files should have identical inodes %v %v", s1.Ino, s2.Ino)
} }
c1, err := ioutil.ReadFile(me.mnt + "/file1") c1, err := ioutil.ReadFile(me.mnt + "/file1")
...@@ -313,7 +320,10 @@ func TestLinkForget(t *testing.T) { ...@@ -313,7 +320,10 @@ func TestLinkForget(t *testing.T) {
f2, err := os.Lstat(me.mnt + "/file2") f2, err := os.Lstat(me.mnt + "/file2")
CheckSuccess(err) CheckSuccess(err)
if f1.Ino == f2.Ino {
s1 := ToStatT(f1)
s2 := ToStatT(f2)
if s1.Ino == s2.Ino {
t.Error("After forget, we should not export links") t.Error("After forget, we should not export links")
} }
} }
...@@ -336,8 +346,8 @@ func TestSymlink(t *testing.T) { ...@@ -336,8 +346,8 @@ func TestSymlink(t *testing.T) {
fi, err := os.Lstat(origLink) fi, err := os.Lstat(origLink)
CheckSuccess(err) CheckSuccess(err)
if !fi.IsSymlink() { if fi.Mode()&os.ModeSymlink == 0 {
t.Errorf("not a symlink: %o", fi.Mode) t.Errorf("not a symlink: %v", fi)
return return
} }
...@@ -461,7 +471,7 @@ func TestMknod(t *testing.T) { ...@@ -461,7 +471,7 @@ func TestMknod(t *testing.T) {
t.Errorf("Mknod %v", errNo) t.Errorf("Mknod %v", errNo)
} }
fi, _ := os.Lstat(me.origFile) fi, _ := os.Lstat(me.origFile)
if fi == nil || !fi.IsFifo() { if fi == nil || fi.Mode()&os.ModeNamedPipe == 0 {
t.Errorf("Expected FIFO filetype.") t.Errorf("Expected FIFO filetype.")
} }
} }
...@@ -489,9 +499,9 @@ func TestReaddir(t *testing.T) { ...@@ -489,9 +499,9 @@ func TestReaddir(t *testing.T) {
t.Errorf("Length mismatch %v", infos) t.Errorf("Length mismatch %v", infos)
} else { } else {
for _, v := range infos { for _, v := range infos {
_, ok := wanted[v.Name] _, ok := wanted[v.Name()]
if !ok { if !ok {
t.Errorf("Unexpected name %v", v.Name) t.Errorf("Unexpected name %v", v.Name())
} }
} }
} }
...@@ -799,7 +809,7 @@ func TestUmask(t *testing.T) { ...@@ -799,7 +809,7 @@ func TestUmask(t *testing.T) {
CheckSuccess(err) CheckSuccess(err)
expect := mask ^ 0777 expect := mask ^ 0777
got := int(fi.Mode & 0777) got := int(fi.Mode().Perm())
if got != expect { if got != expect {
t.Errorf("got %o, expect mode %o for file %s", got, expect, fn) t.Errorf("got %o, expect mode %o for file %s", got, expect, fn)
} }
......
...@@ -34,8 +34,8 @@ func (me *MemNodeFs) newNode() *memNode { ...@@ -34,8 +34,8 @@ func (me *MemNodeFs) newNode() *memNode {
fs: me, fs: me,
id: me.nextFree, id: me.nextFree,
} }
now := time.Nanoseconds() now := time.Now().UnixNano()
n.info.SetTimes(now, now, now) n.info.SetNs(now, now, now)
n.info.Mode = S_IFDIR | 0777 n.info.Mode = S_IFDIR | 0777
me.nextFree++ me.nextFree++
return n return n
...@@ -182,7 +182,7 @@ func (me *memNode) Truncate(file File, size uint64, context *Context) (code Stat ...@@ -182,7 +182,7 @@ func (me *memNode) Truncate(file File, size uint64, context *Context) (code Stat
code = ToStatus(err) code = ToStatus(err)
} }
if code.Ok() { if code.Ok() {
me.info.SetTimes(-1, -1, time.Nanoseconds()) me.info.SetNs(-1, -1, time.Now().UnixNano())
// TODO - should update mtime too? // TODO - should update mtime too?
me.info.Size = size me.info.Size = size
} }
...@@ -190,19 +190,19 @@ func (me *memNode) Truncate(file File, size uint64, context *Context) (code Stat ...@@ -190,19 +190,19 @@ func (me *memNode) Truncate(file File, size uint64, context *Context) (code Stat
} }
func (me *memNode) Utimens(file File, atime int64, mtime int64, context *Context) (code Status) { func (me *memNode) Utimens(file File, atime int64, mtime int64, context *Context) (code Status) {
me.info.SetTimes(int64(atime), int64(mtime), time.Nanoseconds()) me.info.SetNs(int64(atime), int64(mtime), time.Now().UnixNano())
return OK return OK
} }
func (me *memNode) Chmod(file File, perms uint32, context *Context) (code Status) { func (me *memNode) Chmod(file File, perms uint32, context *Context) (code Status) {
me.info.Mode = (me.info.Mode ^ 07777) | perms me.info.Mode = (me.info.Mode ^ 07777) | perms
me.info.SetTimes(-1, -1, time.Nanoseconds()) me.info.SetNs(-1, -1, time.Now().UnixNano())
return OK return OK
} }
func (me *memNode) Chown(file File, uid uint32, gid uint32, context *Context) (code Status) { func (me *memNode) Chown(file File, uid uint32, gid uint32, context *Context) (code Status) {
me.info.Uid = uid me.info.Uid = uid
me.info.Gid = gid me.info.Gid = gid
me.info.SetTimes(-1, -1, time.Nanoseconds()) me.info.SetNs(-1, -1, time.Now().UnixNano())
return OK return OK
} }
...@@ -49,12 +49,12 @@ func TestMemNodeFs(t *testing.T) { ...@@ -49,12 +49,12 @@ func TestMemNodeFs(t *testing.T) {
fi, err := os.Lstat(wd + "/test") fi, err := os.Lstat(wd + "/test")
CheckSuccess(err) CheckSuccess(err)
if fi.Size != 1 { if fi.Size() != 1 {
t.Errorf("Size after write incorrect: got %d want 1", fi.Size) t.Errorf("Size after write incorrect: got %d want 1", fi.Size())
} }
entries, err := ioutil.ReadDir(wd) entries, err := ioutil.ReadDir(wd)
if len(entries) != 1 || entries[0].Name != "test" { if len(entries) != 1 || entries[0].Name() != "test" {
t.Fatalf("Readdir got %v, expected 1 file named 'test'", entries) t.Fatalf("Readdir got %v, expected 1 file named 'test'", entries)
} }
} }
...@@ -72,7 +72,7 @@ func TestMemNodeSetattr(t *testing.T) { ...@@ -72,7 +72,7 @@ func TestMemNodeSetattr(t *testing.T) {
fi, err := f.Stat() fi, err := f.Stat()
CheckSuccess(err) CheckSuccess(err)
if fi.Size != 4096 { if fi.Size() != 4096 {
t.Errorf("Size should be 4096 after Truncate: %d", fi.Size) t.Errorf("Size should be 4096 after Truncate: %d", fi.Size())
} }
} }
...@@ -6,11 +6,11 @@ import ( ...@@ -6,11 +6,11 @@ import (
"flag" "flag"
"fmt" "fmt"
"log" "log"
"math"
"os" "os"
"reflect" "reflect"
"strings" "strings"
"syscall" "syscall"
"time"
"unsafe" "unsafe"
) )
...@@ -50,9 +50,10 @@ func ToStatus(err error) Status { ...@@ -50,9 +50,10 @@ func ToStatus(err error) Status {
return OK return OK
} }
func splitNs(time float64, secs *uint64, nsecs *uint32) { func splitDuration(dt time.Duration, secs *uint64, nsecs *uint32) {
*nsecs = uint32(1e9 * (time - math.Trunc(time))) ns := int64(dt)
*secs = uint64(math.Trunc(time)) *nsecs = uint32(ns % 1e9)
*secs = uint64(ns / 1e9)
} }
// TODO - expose in Go's syscall package. // TODO - expose in Go's syscall package.
......
...@@ -44,7 +44,9 @@ func TestLinkAt(t *testing.T) { ...@@ -44,7 +44,9 @@ func TestLinkAt(t *testing.T) {
t.Fatalf("Lstat b: %v", err) t.Fatalf("Lstat b: %v", err)
} }
if f1.Ino != f2.Ino { s1 := ToStatT(f1)
t.Fatal("Ino mismatch", f1, f2) s2 := ToStatT(f2)
if s1.Ino != s2.Ino {
t.Fatal("Ino mismatch", s1, s2)
} }
} }
...@@ -58,7 +58,7 @@ func TestMountReaddir(t *testing.T) { ...@@ -58,7 +58,7 @@ func TestMountReaddir(t *testing.T) {
entries, err := ioutil.ReadDir(ts.mnt) entries, err := ioutil.ReadDir(ts.mnt)
CheckSuccess(err) CheckSuccess(err)
if len(entries) != 1 || entries[0].Name != "mnt" { if len(entries) != 1 || entries[0].Name() != "mnt" {
t.Error("wrong readdir result", entries) t.Error("wrong readdir result", entries)
} }
ts.pathFs.Unmount("mnt") ts.pathFs.Unmount("mnt")
...@@ -92,9 +92,8 @@ func TestRecursiveMount(t *testing.T) { ...@@ -92,9 +92,8 @@ func TestRecursiveMount(t *testing.T) {
} }
f.Close() f.Close()
t.Log("Waiting for kernel to flush file-close to fuse...") t.Log("Waiting for kernel to flush file-close to fuse...")
time.Sleep(1.5e9 * testTtl) time.Sleep(testTtl)
t.Log("Attempting unmount, should succeed") t.Log("Attempting unmount, should succeed")
code = ts.pathFs.Unmount("mnt") code = ts.pathFs.Unmount("mnt")
...@@ -130,7 +129,7 @@ func TestDeletedUnmount(t *testing.T) { ...@@ -130,7 +129,7 @@ func TestDeletedUnmount(t *testing.T) {
} }
f.Close() f.Close()
time.Sleep(1.5e9 * testTtl) time.Sleep((3 * testTtl) / 2)
code = ts.pathFs.Unmount("mnt") code = ts.pathFs.Unmount("mnt")
if !code.Ok() { if !code.Ok() {
t.Error("should succeed", code) t.Error("should succeed", code)
......
...@@ -98,7 +98,7 @@ func (me *MountState) Unmount() (err error) { ...@@ -98,7 +98,7 @@ func (me *MountState) Unmount() (err error) {
if me.mountPoint == "" { if me.mountPoint == "" {
return nil return nil
} }
delay := int64(0) delay := time.Duration(0)
for try := 0; try < 5; try++ { for try := 0; try < 5; try++ {
err = unmount(me.mountPoint) err = unmount(me.mountPoint)
if err == nil { if err == nil {
...@@ -108,7 +108,7 @@ func (me *MountState) Unmount() (err error) { ...@@ -108,7 +108,7 @@ func (me *MountState) Unmount() (err error) {
// Sleep for a bit. This is not pretty, but there is // Sleep for a bit. This is not pretty, but there is
// no way we can be certain that the kernel thinks all // no way we can be certain that the kernel thinks all
// open files have already been closed. // open files have already been closed.
delay = 2*delay + 5e6 delay = 2*delay + 5*time.Millisecond
time.Sleep(delay) time.Sleep(delay)
} }
if err == nil { if err == nil {
...@@ -151,7 +151,7 @@ func (me *MountState) readRequest(req *request) Status { ...@@ -151,7 +151,7 @@ func (me *MountState) readRequest(req *request) Status {
// If we start timing before the read, we may take into // If we start timing before the read, we may take into
// account waiting for input into the timing. // account waiting for input into the timing.
if me.latencies != nil { if me.latencies != nil {
req.startNs = time.Nanoseconds() req.startNs = time.Now().UnixNano()
} }
req.inputBuf = req.inputBuf[0:n] req.inputBuf = req.inputBuf[0:n]
return ToStatus(err) return ToStatus(err)
...@@ -159,7 +159,7 @@ func (me *MountState) readRequest(req *request) Status { ...@@ -159,7 +159,7 @@ func (me *MountState) readRequest(req *request) Status {
func (me *MountState) recordStats(req *request) { func (me *MountState) recordStats(req *request) {
if me.latencies != nil { if me.latencies != nil {
endNs := time.Nanoseconds() endNs := time.Now().UnixNano()
dt := endNs - req.startNs dt := endNs - req.startNs
opname := operationName(req.inHeader.opcode) opname := operationName(req.inHeader.opcode)
...@@ -258,7 +258,7 @@ func (me *MountState) write(req *request) Status { ...@@ -258,7 +258,7 @@ func (me *MountState) write(req *request) Status {
} }
if me.latencies != nil { if me.latencies != nil {
req.preWriteNs = time.Nanoseconds() req.preWriteNs = time.Now().UnixNano()
} }
if req.outHeaderBytes == nil { if req.outHeaderBytes == nil {
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"log" "log"
"os" "os"
"testing" "testing"
"time"
) )
var _ = log.Println var _ = log.Println
...@@ -46,7 +47,7 @@ func NewNotifyTest() *NotifyTest { ...@@ -46,7 +47,7 @@ func NewNotifyTest() *NotifyTest {
var err error var err error
me.dir, err = ioutil.TempDir("", "go-fuse") me.dir, err = ioutil.TempDir("", "go-fuse")
CheckSuccess(err) CheckSuccess(err)
entryTtl := 0.1 entryTtl := 100 * time.Millisecond
opts := &FileSystemOptions{ opts := &FileSystemOptions{
EntryTimeout: entryTtl, EntryTimeout: entryTtl,
AttrTimeout: entryTtl, AttrTimeout: entryTtl,
...@@ -79,14 +80,14 @@ func TestInodeNotify(t *testing.T) { ...@@ -79,14 +80,14 @@ func TestInodeNotify(t *testing.T) {
fs.size = 42 fs.size = 42
fi, err := os.Lstat(dir + "/file") fi, err := os.Lstat(dir + "/file")
CheckSuccess(err) CheckSuccess(err)
if !fi.IsRegular() || fi.Size != 42 { if fi.Mode()&os.ModeType != 0 || fi.Size() != 42 {
t.Error(fi) t.Error(fi)
} }
fs.size = 666 fs.size = 666
fi, err = os.Lstat(dir + "/file") fi, err = os.Lstat(dir + "/file")
CheckSuccess(err) CheckSuccess(err)
if !fi.IsRegular() || fi.Size == 666 { if fi.Mode()&os.ModeType != 0 || fi.Size() == 666 {
t.Error(fi) t.Error(fi)
} }
...@@ -97,7 +98,7 @@ func TestInodeNotify(t *testing.T) { ...@@ -97,7 +98,7 @@ func TestInodeNotify(t *testing.T) {
fi, err = os.Lstat(dir + "/file") fi, err = os.Lstat(dir + "/file")
CheckSuccess(err) CheckSuccess(err)
if !fi.IsRegular() || fi.Size != 666 { if fi.Mode()&os.ModeType != 0 || fi.Size() != 666 {
t.Error(fi) t.Error(fi)
} }
} }
......
...@@ -46,8 +46,8 @@ func TestOwnerDefault(t *testing.T) { ...@@ -46,8 +46,8 @@ func TestOwnerDefault(t *testing.T) {
fi, err := os.Lstat(wd + "/foo") fi, err := os.Lstat(wd + "/foo")
CheckSuccess(err) CheckSuccess(err)
if fi.Uid != os.Getuid() || fi.Gid != os.Getgid() { if int(ToStatT(fi).Uid) != os.Getuid() || int(ToStatT(fi).Gid) != os.Getgid() {
t.Fatal("Should use current uid for mount", fi.Uid, fi.Gid) t.Fatal("Should use current uid for mount")
} }
} }
...@@ -57,8 +57,8 @@ func TestOwnerRoot(t *testing.T) { ...@@ -57,8 +57,8 @@ func TestOwnerRoot(t *testing.T) {
fi, err := os.Lstat(wd + "/foo") fi, err := os.Lstat(wd + "/foo")
CheckSuccess(err) CheckSuccess(err)
if fi.Uid != _RANDOM_OWNER || fi.Gid != _RANDOM_OWNER { if ToStatT(fi).Uid != _RANDOM_OWNER || ToStatT(fi).Gid != _RANDOM_OWNER {
t.Fatal("Should use FS owner uid", fi.Uid, fi.Gid) t.Fatal("Should use FS owner uid")
} }
} }
...@@ -68,7 +68,7 @@ func TestOwnerOverride(t *testing.T) { ...@@ -68,7 +68,7 @@ func TestOwnerOverride(t *testing.T) {
fi, err := os.Lstat(wd + "/foo") fi, err := os.Lstat(wd + "/foo")
CheckSuccess(err) CheckSuccess(err)
if fi.Uid != 42 || fi.Gid != 43 { if ToStatT(fi).Uid != 42 || ToStatT(fi).Gid != 43 {
t.Fatal("Should use current uid for mount", fi.Uid, fi.Gid) t.Fatal("Should use current uid for mount")
} }
} }
...@@ -525,7 +525,7 @@ func (me *pathInode) findChild(fi *Attr, name string, fullPath string) (out *pat ...@@ -525,7 +525,7 @@ func (me *pathInode) findChild(fi *Attr, name string, fullPath string) (out *pat
} }
if out == nil { if out == nil {
out = me.createChild(fi.IsDirectory()) out = me.createChild(fi.IsDir())
out.clientInode = fi.Ino out.clientInode = fi.Ino
me.addChild(name, out) me.addChild(name, out)
} }
...@@ -551,7 +551,7 @@ func (me *pathInode) GetAttr(file File, context *Context) (fi *Attr, code Status ...@@ -551,7 +551,7 @@ func (me *pathInode) GetAttr(file File, context *Context) (fi *Attr, code Status
me.setClientInode(fi.Ino) me.setClientInode(fi.Ino)
} }
if fi != nil && !fi.IsDirectory() && fi.Nlink == 0 { if fi != nil && !fi.IsDir() && fi.Nlink == 0 {
fi.Nlink = 1 fi.Nlink = 1
} }
return fi, code return fi, code
......
...@@ -3,7 +3,7 @@ package fuse ...@@ -3,7 +3,7 @@ package fuse
import ( import (
"fmt" "fmt"
"os" "os"
"sort" // "sort"
"strings" "strings"
"syscall" "syscall"
) )
...@@ -91,30 +91,8 @@ func (me *OpenIn) String() string { ...@@ -91,30 +91,8 @@ func (me *OpenIn) String() string {
return fmt.Sprintf("{%s}", flagString(openFlagNames, int(me.Flags), "O_RDONLY")) return fmt.Sprintf("{%s}", flagString(openFlagNames, int(me.Flags), "O_RDONLY"))
} }
type OsFileInfo os.FileInfo /*
type OsFileInfos []*s.FileInfo
func (me OsFileInfo) String() string {
return fmt.Sprintf(
"{%s M0%o S=%d L=%d "+
"%d:%d "+
"%d*%d %d:%d "+
"C %d.%09d "+
"M %d.%09d "+
"A %d.%09d}",
me.Name,
me.Mode, me.Size, me.Nlink,
me.Uid, me.Gid,
me.Blocks, me.Blksize,
me.Rdev, me.Ino,
me.Ctime_ns/1e9,
me.Ctime_ns%1e9,
me.Mtime_ns/1e9,
me.Mtime_ns%1e9,
me.Atime_ns/1e9,
me.Atime_ns%1e9)
}
type OsFileInfos []*os.FileInfo
func (me OsFileInfos) String() string { func (me OsFileInfos) String() string {
out := []string{} out := []string{}
...@@ -124,7 +102,7 @@ func (me OsFileInfos) String() string { ...@@ -124,7 +102,7 @@ func (me OsFileInfos) String() string {
sort.Strings(out) sort.Strings(out)
return fmt.Sprintf("[%v]", out) return fmt.Sprintf("[%v]", out)
} }
*/
func (me *SetAttrIn) String() string { func (me *SetAttrIn) String() string {
s := []string{} s := []string{}
if me.Valid&FATTR_MODE != 0 { if me.Valid&FATTR_MODE != 0 {
......
...@@ -155,7 +155,11 @@ func (me *AutoUnionFs) getRoots(path string) []string { ...@@ -155,7 +155,11 @@ func (me *AutoUnionFs) getRoots(path string) []string {
ro := filepath.Join(path, _READONLY) ro := filepath.Join(path, _READONLY)
fi, err := os.Lstat(ro) fi, err := os.Lstat(ro)
fiDir, errDir := os.Stat(ro) fiDir, errDir := os.Stat(ro)
if err == nil && errDir == nil && fi.IsSymlink() && fiDir.IsDirectory() { if err != nil || errDir != nil {
return nil
}
if fi.Mode()&os.ModeSymlink != 0 && fiDir.IsDir() {
// TODO - should recurse and chain all READONLYs // TODO - should recurse and chain all READONLYs
// together. // together.
return []string{path, ro} return []string{path, ro}
...@@ -163,8 +167,8 @@ func (me *AutoUnionFs) getRoots(path string) []string { ...@@ -163,8 +167,8 @@ func (me *AutoUnionFs) getRoots(path string) []string {
return nil return nil
} }
func (me *AutoUnionFs) visit(path string, fi *os.FileInfo, err error) error { func (me *AutoUnionFs) visit(path string, fi os.FileInfo, err error) error {
if fi.IsDirectory() { if fi.IsDir() {
roots := me.getRoots(path) roots := me.getRoots(path)
if roots != nil { if roots != nil {
me.addAutomaticFs(roots) me.addAutomaticFs(roots)
...@@ -180,12 +184,12 @@ func (me *AutoUnionFs) updateKnownFses() { ...@@ -180,12 +184,12 @@ func (me *AutoUnionFs) updateKnownFses() {
directoryEntries, err := ioutil.ReadDir(me.root) directoryEntries, err := ioutil.ReadDir(me.root)
if err == nil { if err == nil {
for _, dir := range directoryEntries { for _, dir := range directoryEntries {
if dir.IsDirectory() || dir.IsSymlink() { if dir.IsDir() || dir.Mode()&os.ModeSymlink != 0 {
path := filepath.Join(me.root, dir.Name) path := filepath.Join(me.root, dir.Name())
dir, _ = os.Stat(path) dir, _ = os.Stat(path)
me.visit(path, dir, nil) me.visit(path, dir, nil)
filepath.Walk(path, filepath.Walk(path,
func(path string, fi *os.FileInfo, err error) error { func(path string, fi os.FileInfo, err error) error {
return me.visit(path, fi, err) return me.visit(path, fi, err)
}) })
} }
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"log" "log"
"os" "os"
"testing" "testing"
"time"
) )
var _ = fmt.Print var _ = fmt.Print
...@@ -14,7 +15,7 @@ var _ = log.Print ...@@ -14,7 +15,7 @@ var _ = log.Print
var CheckSuccess = fuse.CheckSuccess var CheckSuccess = fuse.CheckSuccess
const entryTtl = 0.1 const entryTtl = 100 * time.Millisecond
var testAOpts = AutoUnionFsOptions{ var testAOpts = AutoUnionFsOptions{
UnionFsOptions: testOpts, UnionFsOptions: testOpts,
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"log" "log"
"strings" "strings"
"time"
) )
var _ = fmt.Println var _ = fmt.Println
...@@ -84,25 +85,25 @@ func readLink(fs fuse.FileSystem, name string) *linkResponse { ...@@ -84,25 +85,25 @@ func readLink(fs fuse.FileSystem, name string) *linkResponse {
} }
} }
func NewCachingFileSystem(fs fuse.FileSystem, ttlNs int64) *CachingFileSystem { func NewCachingFileSystem(fs fuse.FileSystem, ttl time.Duration) *CachingFileSystem {
c := new(CachingFileSystem) c := new(CachingFileSystem)
c.FileSystem = fs c.FileSystem = fs
c.attributes = NewTimedCache(func(n string) (interface{}, bool) { c.attributes = NewTimedCache(func(n string) (interface{}, bool) {
a := getAttr(fs, n) a := getAttr(fs, n)
return a, a.Ok() return a, a.Ok()
}, ttlNs) }, ttl)
c.dirs = NewTimedCache(func(n string) (interface{}, bool) { c.dirs = NewTimedCache(func(n string) (interface{}, bool) {
d := readDir(fs, n) d := readDir(fs, n)
return d, d.Ok() return d, d.Ok()
}, ttlNs) }, ttl)
c.links = NewTimedCache(func(n string) (interface{}, bool) { c.links = NewTimedCache(func(n string) (interface{}, bool) {
l := readLink(fs, n) l := readLink(fs, n)
return l, l.Ok() return l, l.Ok()
}, ttlNs) }, ttl)
c.xattr = NewTimedCache(func(n string) (interface{}, bool) { c.xattr = NewTimedCache(func(n string) (interface{}, bool) {
l := getXAttr(fs, n) l := getXAttr(fs, n)
return l, l.Ok() return l, l.Ok()
}, ttlNs) }, ttl)
return c return c
} }
......
...@@ -39,7 +39,7 @@ func TestCachingFs(t *testing.T) { ...@@ -39,7 +39,7 @@ func TestCachingFs(t *testing.T) {
if !code.Ok() { if !code.Ok() {
t.Fatal("GetAttr failure", code) t.Fatal("GetAttr failure", code)
} }
if !fi.IsDirectory() { if !fi.IsDir() {
t.Error("unexpected attr", fi) t.Error("unexpected attr", fi)
} }
......
...@@ -13,7 +13,7 @@ func NewUnionFsFromRoots(roots []string, opts *UnionFsOptions, roCaching bool) ( ...@@ -13,7 +13,7 @@ func NewUnionFsFromRoots(roots []string, opts *UnionFsOptions, roCaching bool) (
if err != nil { if err != nil {
return nil, err return nil, err
} }
if fi.IsDirectory() { if fi.IsDir() {
fs = fuse.NewLoopbackFileSystem(r) fs = fuse.NewLoopbackFileSystem(r)
} }
if fs == nil { if fs == nil {
......
...@@ -31,9 +31,9 @@ func newDirnameMap(fs fuse.FileSystem, dir string) map[string]bool { ...@@ -31,9 +31,9 @@ func newDirnameMap(fs fuse.FileSystem, dir string) map[string]bool {
// If called when the cache is expired, the filenames are read afresh in // If called when the cache is expired, the filenames are read afresh in
// the background. // the background.
type DirCache struct { type DirCache struct {
dir string dir string
ttlNs int64 ttl time.Duration
fs fuse.FileSystem fs fuse.FileSystem
// Protects data below. // Protects data below.
lock sync.RWMutex lock sync.RWMutex
...@@ -48,7 +48,7 @@ func (me *DirCache) setMap(newMap map[string]bool) { ...@@ -48,7 +48,7 @@ func (me *DirCache) setMap(newMap map[string]bool) {
me.names = newMap me.names = newMap
me.updateRunning = false me.updateRunning = false
_ = time.AfterFunc(me.ttlNs, _ = time.AfterFunc(int64(me.ttl),
func() { me.DropCache() }) func() { me.DropCache() })
} }
...@@ -95,11 +95,11 @@ func (me *DirCache) AddEntry(name string) { ...@@ -95,11 +95,11 @@ func (me *DirCache) AddEntry(name string) {
me.names[name] = true me.names[name] = true
} }
func NewDirCache(fs fuse.FileSystem, dir string, ttlNs int64) *DirCache { func NewDirCache(fs fuse.FileSystem, dir string, ttl time.Duration) *DirCache {
dc := new(DirCache) dc := new(DirCache)
dc.dir = dir dc.dir = dir
dc.fs = fs dc.fs = fs
dc.ttlNs = ttlNs dc.ttl = ttl
return dc return dc
} }
......
...@@ -11,8 +11,8 @@ var _ = log.Println ...@@ -11,8 +11,8 @@ var _ = log.Println
type cacheEntry struct { type cacheEntry struct {
data interface{} data interface{}
// expiryNs is the absolute timestamp of the expiry. // expiry is the absolute timestamp of the expiry.
expiryNs int64 expiry time.Time
} }
// TimedIntCache caches the result of fetch() for some time. It is // TimedIntCache caches the result of fetch() for some time. It is
...@@ -23,8 +23,8 @@ type TimedCacheFetcher func(name string) (value interface{}, cacheable bool) ...@@ -23,8 +23,8 @@ type TimedCacheFetcher func(name string) (value interface{}, cacheable bool)
type TimedCache struct { type TimedCache struct {
fetch TimedCacheFetcher fetch TimedCacheFetcher
// ttlNs is a duration of the cache. // ttl is the duration of the cache.
ttlNs int64 ttl time.Duration
cacheMapMutex sync.RWMutex cacheMapMutex sync.RWMutex
cacheMap map[string]*cacheEntry cacheMap map[string]*cacheEntry
...@@ -32,13 +32,11 @@ type TimedCache struct { ...@@ -32,13 +32,11 @@ type TimedCache struct {
PurgeTimer *time.Timer PurgeTimer *time.Timer
} }
const layerCacheTimeoutNs = 1e9
// Creates a new cache with the given TTL. If TTL <= 0, the caching is // Creates a new cache with the given TTL. If TTL <= 0, the caching is
// indefinite. // indefinite.
func NewTimedCache(fetcher TimedCacheFetcher, ttlNs int64) *TimedCache { func NewTimedCache(fetcher TimedCacheFetcher, ttl time.Duration) *TimedCache {
l := new(TimedCache) l := new(TimedCache)
l.ttlNs = ttlNs l.ttl = ttl
l.fetch = fetcher l.fetch = fetcher
l.cacheMap = make(map[string]*cacheEntry) l.cacheMap = make(map[string]*cacheEntry)
return l return l
...@@ -49,7 +47,7 @@ func (me *TimedCache) Get(name string) interface{} { ...@@ -49,7 +47,7 @@ func (me *TimedCache) Get(name string) interface{} {
info, ok := me.cacheMap[name] info, ok := me.cacheMap[name]
me.cacheMapMutex.RUnlock() me.cacheMapMutex.RUnlock()
valid := ok && (me.ttlNs <= 0 || info.expiryNs > time.Nanoseconds()) valid := ok && (me.ttl <= 0 || info.expiry.After(time.Now()))
if valid { if valid {
return info.data return info.data
} }
...@@ -61,8 +59,8 @@ func (me *TimedCache) Set(name string, val interface{}) { ...@@ -61,8 +59,8 @@ func (me *TimedCache) Set(name string, val interface{}) {
defer me.cacheMapMutex.Unlock() defer me.cacheMapMutex.Unlock()
me.cacheMap[name] = &cacheEntry{ me.cacheMap[name] = &cacheEntry{
data: val, data: val,
expiryNs: time.Nanoseconds() + me.ttlNs, expiry: time.Now().Add(me.ttl),
} }
} }
...@@ -84,12 +82,12 @@ func (me *TimedCache) GetFresh(name string) interface{} { ...@@ -84,12 +82,12 @@ func (me *TimedCache) GetFresh(name string) interface{} {
// Drop all expired entries. // Drop all expired entries.
func (me *TimedCache) Purge() { func (me *TimedCache) Purge() {
keys := make([]string, 0, len(me.cacheMap)) keys := make([]string, 0, len(me.cacheMap))
now := time.Nanoseconds() now := time.Now()
me.cacheMapMutex.Lock() me.cacheMapMutex.Lock()
defer me.cacheMapMutex.Unlock() defer me.cacheMapMutex.Unlock()
for k, v := range me.cacheMap { for k, v := range me.cacheMap {
if v.expiryNs < now { if now.After(v.expiry) {
keys = append(keys, k) keys = append(keys, k)
} }
} }
...@@ -99,12 +97,12 @@ func (me *TimedCache) Purge() { ...@@ -99,12 +97,12 @@ func (me *TimedCache) Purge() {
} }
func (me *TimedCache) RecurringPurge() { func (me *TimedCache) RecurringPurge() {
if me.ttlNs <= 0 { if me.ttl <= 0 {
return return
} }
me.Purge() me.Purge()
me.PurgeTimer = time.AfterFunc(me.ttlNs*5, me.PurgeTimer = time.AfterFunc(int64(me.ttl*5),
func() { me.RecurringPurge() }) func() { me.RecurringPurge() })
} }
......
...@@ -38,10 +38,8 @@ func TestTimedCache(t *testing.T) { ...@@ -38,10 +38,8 @@ func TestTimedCache(t *testing.T) {
return &i, true return &i, true
} }
var ttl int64
// This fails with 1e6 on some Opteron CPUs. // This fails with 1e6 on some Opteron CPUs.
ttl = 1e8 ttl := 100 * time.Millisecond
cache := NewTimedCache(fetch, ttl) cache := NewTimedCache(fetch, ttl)
v := cache.Get("n").(*int) v := cache.Get("n").(*int)
...@@ -53,7 +51,7 @@ func TestTimedCache(t *testing.T) { ...@@ -53,7 +51,7 @@ func TestTimedCache(t *testing.T) {
} }
// The cache update is async. // The cache update is async.
time.Sleep(ttl / 10) time.Sleep(time.Duration(ttl / 10))
w := cache.Get("n") w := cache.Get("n")
if v != w { if v != w {
...@@ -64,7 +62,7 @@ func TestTimedCache(t *testing.T) { ...@@ -64,7 +62,7 @@ func TestTimedCache(t *testing.T) {
t.Errorf("fetch count fail: %d > 1", fetchCount) t.Errorf("fetch count fail: %d > 1", fetchCount)
} }
time.Sleep(ttl * 2) time.Sleep(time.Duration(ttl * 2))
cache.Purge() cache.Purge()
w = cache.Get("n") w = cache.Get("n")
......
...@@ -19,7 +19,7 @@ func filePathHash(path string) string { ...@@ -19,7 +19,7 @@ func filePathHash(path string) string {
h := md5.New() h := md5.New()
h.Write([]byte(dir)) h.Write([]byte(dir))
return fmt.Sprintf("%x-%s", h.Sum()[:8], base) return fmt.Sprintf("%x-%s", h.Sum(nil)[:8], base)
} }
/* /*
...@@ -70,9 +70,9 @@ type UnionFs struct { ...@@ -70,9 +70,9 @@ type UnionFs struct {
} }
type UnionFsOptions struct { type UnionFsOptions struct {
BranchCacheTTLSecs float64 BranchCacheTTL time.Duration
DeletionCacheTTLSecs float64 DeletionCacheTTL time.Duration
DeletionDirName string DeletionDirName string
} }
const ( const (
...@@ -91,10 +91,10 @@ func NewUnionFs(fileSystems []fuse.FileSystem, options UnionFsOptions) *UnionFs ...@@ -91,10 +91,10 @@ func NewUnionFs(fileSystems []fuse.FileSystem, options UnionFsOptions) *UnionFs
return nil return nil
} }
g.deletionCache = NewDirCache(writable, options.DeletionDirName, int64(options.DeletionCacheTTLSecs*1e9)) g.deletionCache = NewDirCache(writable, options.DeletionDirName, options.DeletionCacheTTL)
g.branchCache = NewTimedCache( g.branchCache = NewTimedCache(
func(n string) (interface{}, bool) { return g.getBranchAttrNoCache(n), true }, func(n string) (interface{}, bool) { return g.getBranchAttrNoCache(n), true },
int64(options.BranchCacheTTLSecs*1e9)) options.BranchCacheTTL)
g.branchCache.RecurringPurge() g.branchCache.RecurringPurge()
return g return g
} }
...@@ -138,7 +138,7 @@ func (me *UnionFs) createDeletionStore() (code fuse.Status) { ...@@ -138,7 +138,7 @@ func (me *UnionFs) createDeletionStore() (code fuse.Status) {
} }
} }
if !code.Ok() || !fi.IsDirectory() { if !code.Ok() || !fi.IsDir() {
code = fuse.Status(syscall.EROFS) code = fuse.Status(syscall.EROFS)
} }
...@@ -310,7 +310,7 @@ func (me *UnionFs) Promote(name string, srcResult branchResult, context *fuse.Co ...@@ -310,7 +310,7 @@ func (me *UnionFs) Promote(name string, srcResult branchResult, context *fuse.Co
} else { } else {
code = writable.Symlink(link, name, context) code = writable.Symlink(link, name, context)
} }
} else if srcResult.attr.IsDirectory() { } else if srcResult.attr.IsDir() {
code = writable.Mkdir(name, srcResult.attr.Mode&07777|0200, context) code = writable.Mkdir(name, srcResult.attr.Mode&07777|0200, context)
} else { } else {
log.Println("Unknown file type:", srcResult.attr) log.Println("Unknown file type:", srcResult.attr)
...@@ -366,7 +366,7 @@ func (me *UnionFs) Rmdir(path string, context *fuse.Context) (code fuse.Status) ...@@ -366,7 +366,7 @@ func (me *UnionFs) Rmdir(path string, context *fuse.Context) (code fuse.Status)
if r.code != fuse.OK { if r.code != fuse.OK {
return r.code return r.code
} }
if !r.attr.IsDirectory() { if !r.attr.IsDir() {
return fuse.Status(syscall.ENOTDIR) return fuse.Status(syscall.ENOTDIR)
} }
...@@ -460,8 +460,8 @@ func (me *UnionFs) Truncate(path string, size uint64, context *fuse.Context) (co ...@@ -460,8 +460,8 @@ func (me *UnionFs) Truncate(path string, size uint64, context *fuse.Context) (co
} }
if code.Ok() { if code.Ok() {
r.attr.Size = size r.attr.Size = size
now := time.Nanoseconds() now := time.Now()
r.attr.SetTimes(-1, now, now) r.attr.SetTimes(nil, &now, &now)
me.branchCache.Set(path, r) me.branchCache.Set(path, r)
} }
return code return code
...@@ -480,7 +480,7 @@ func (me *UnionFs) Utimens(name string, atime int64, mtime int64, context *fuse. ...@@ -480,7 +480,7 @@ func (me *UnionFs) Utimens(name string, atime int64, mtime int64, context *fuse.
code = me.fileSystems[0].Utimens(name, atime, mtime, context) code = me.fileSystems[0].Utimens(name, atime, mtime, context)
} }
if code.Ok() { if code.Ok() {
r.attr.SetTimes(atime, mtime, time.Nanoseconds()) r.attr.SetNs(atime, mtime, time.Now().UnixNano())
me.branchCache.Set(name, r) me.branchCache.Set(name, r)
} }
return code return code
...@@ -509,7 +509,8 @@ func (me *UnionFs) Chown(name string, uid uint32, gid uint32, context *fuse.Cont ...@@ -509,7 +509,8 @@ func (me *UnionFs) Chown(name string, uid uint32, gid uint32, context *fuse.Cont
} }
r.attr.Uid = uid r.attr.Uid = uid
r.attr.Gid = gid r.attr.Gid = gid
r.attr.SetTimes(-1, -1, time.Nanoseconds()) now := time.Now()
r.attr.SetTimes(nil, nil, &now)
me.branchCache.Set(name, r) me.branchCache.Set(name, r)
return fuse.OK return fuse.OK
} }
...@@ -540,7 +541,8 @@ func (me *UnionFs) Chmod(name string, mode uint32, context *fuse.Context) (code ...@@ -540,7 +541,8 @@ func (me *UnionFs) Chmod(name string, mode uint32, context *fuse.Context) (code
me.fileSystems[0].Chmod(name, mode, context) me.fileSystems[0].Chmod(name, mode, context)
} }
r.attr.Mode = (r.attr.Mode &^ permMask) | mode r.attr.Mode = (r.attr.Mode &^ permMask) | mode
r.attr.SetTimes(-1, -1, time.Nanoseconds()) now := time.Now()
r.attr.SetTimes(nil, nil, &now)
me.branchCache.Set(name, r) me.branchCache.Set(name, r)
return fuse.OK return fuse.OK
} }
...@@ -585,7 +587,7 @@ func (me *UnionFs) Readlink(name string, context *fuse.Context) (out string, cod ...@@ -585,7 +587,7 @@ func (me *UnionFs) Readlink(name string, context *fuse.Context) (out string, cod
func IsDir(fs fuse.FileSystem, name string) bool { func IsDir(fs fuse.FileSystem, name string) bool {
a, code := fs.GetAttr(name, nil) a, code := fs.GetAttr(name, nil)
return code.Ok() && a.IsDirectory() return code.Ok() && a.IsDir()
} }
func stripSlash(fn string) string { func stripSlash(fn string) string {
...@@ -604,7 +606,7 @@ func (me *UnionFs) promoteDirsTo(filename string) fuse.Status { ...@@ -604,7 +606,7 @@ func (me *UnionFs) promoteDirsTo(filename string) fuse.Status {
if !r.code.Ok() { if !r.code.Ok() {
log.Println("path component does not exist", filename, dirName) log.Println("path component does not exist", filename, dirName)
} }
if !r.attr.IsDirectory() { if !r.attr.IsDir() {
log.Println("path component is not a directory.", dirName, r) log.Println("path component is not a directory.", dirName, r)
return fuse.EPERM return fuse.EPERM
} }
...@@ -646,11 +648,11 @@ func (me *UnionFs) Create(name string, flags uint32, mode uint32, context *fuse. ...@@ -646,11 +648,11 @@ func (me *UnionFs) Create(name string, flags uint32, mode uint32, context *fuse.
fuseFile = me.newUnionFsFile(fuseFile, 0) fuseFile = me.newUnionFsFile(fuseFile, 0)
me.removeDeletion(name) me.removeDeletion(name)
now := time.Nanoseconds() now := time.Now()
a := fuse.Attr{ a := fuse.Attr{
Mode: fuse.S_IFREG | mode, Mode: fuse.S_IFREG | mode,
} }
a.SetTimes(-1, now, now) a.SetTimes(nil, &now, &now)
me.branchCache.Set(name, branchResult{&a, fuse.OK, 0}) me.branchCache.Set(name, branchResult{&a, fuse.OK, 0})
} }
return fuseFile, code return fuseFile, code
...@@ -804,7 +806,7 @@ func (me *UnionFs) recursivePromote(path string, pathResult branchResult, contex ...@@ -804,7 +806,7 @@ func (me *UnionFs) recursivePromote(path string, pathResult branchResult, contex
names = append(names, path) names = append(names, path)
} }
if code.Ok() && pathResult.attr != nil && pathResult.attr.IsDirectory() { if code.Ok() && pathResult.attr != nil && pathResult.attr.IsDir() {
var stream chan fuse.DirEntry var stream chan fuse.DirEntry
stream, code = me.OpenDir(path, context) stream, code = me.OpenDir(path, context)
for e := range stream { for e := range stream {
...@@ -865,7 +867,7 @@ func (me *UnionFs) Rename(src string, dst string, context *fuse.Context) (code f ...@@ -865,7 +867,7 @@ func (me *UnionFs) Rename(src string, dst string, context *fuse.Context) (code f
code = srcResult.code code = srcResult.code
} }
if srcResult.attr.IsDirectory() { if srcResult.attr.IsDir() {
return me.renameDirectory(srcResult, src, dst, context) return me.renameDirectory(srcResult, src, dst, context)
} }
...@@ -937,7 +939,8 @@ func (me *UnionFs) Open(name string, flags uint32, context *fuse.Context) (fuseF ...@@ -937,7 +939,8 @@ func (me *UnionFs) Open(name string, flags uint32, context *fuse.Context) (fuseF
return nil, code return nil, code
} }
r.branch = 0 r.branch = 0
r.attr.SetTimes(-1, time.Nanoseconds(), -1) now := time.Now()
r.attr.SetTimes(nil, &now, nil)
me.branchCache.Set(name, r) me.branchCache.Set(name, r)
} }
fuseFile, status = me.fileSystems[r.branch].Open(name, uint32(flags), context) fuseFile, status = me.fileSystems[r.branch].Open(name, uint32(flags), context)
......
...@@ -22,16 +22,17 @@ func TestFilePathHash(t *testing.T) { ...@@ -22,16 +22,17 @@ func TestFilePathHash(t *testing.T) {
} }
var testOpts = UnionFsOptions{ var testOpts = UnionFsOptions{
DeletionCacheTTLSecs: entryTtl, DeletionCacheTTL: entryTtl,
DeletionDirName: "DELETIONS", DeletionDirName: "DELETIONS",
BranchCacheTTLSecs: entryTtl, BranchCacheTTL: entryTtl,
} }
func freezeRo(dir string) { func freezeRo(dir string) {
err := filepath.Walk( err := filepath.Walk(
dir, dir,
func(path string, fi *os.FileInfo, err error) error { func(path string, fi os.FileInfo, err error) error {
return os.Chmod(path, (fi.Mode&0777)&^0222) newMode := uint32(fi.Mode().Perm()) &^ 0222
return os.Chmod(path, newMode)
}) })
CheckSuccess(err) CheckSuccess(err)
} }
...@@ -59,9 +60,9 @@ func setupUfs(t *testing.T) (workdir string, cleanup func()) { ...@@ -59,9 +60,9 @@ func setupUfs(t *testing.T) (workdir string, cleanup func()) {
// We configure timeouts are smaller, so we can check for // We configure timeouts are smaller, so we can check for
// UnionFs's cache consistency. // UnionFs's cache consistency.
opts := &fuse.FileSystemOptions{ opts := &fuse.FileSystemOptions{
EntryTimeout: .5 * entryTtl, EntryTimeout: entryTtl / 2,
AttrTimeout: .5 * entryTtl, AttrTimeout: entryTtl / 2,
NegativeTimeout: .5 * entryTtl, NegativeTimeout: entryTtl / 2,
} }
pathfs := fuse.NewPathNodeFs(ufs, pathfs := fuse.NewPathNodeFs(ufs,
...@@ -181,14 +182,15 @@ func TestUnionFsChtimes(t *testing.T) { ...@@ -181,14 +182,15 @@ func TestUnionFsChtimes(t *testing.T) {
defer clean() defer clean()
writeToFile(wd+"/ro/file", "a") writeToFile(wd+"/ro/file", "a")
err := os.Chtimes(wd+"/ro/file", 42e9, 43e9) err := os.Chtimes(wd+"/ro/file", time.Unix(42, 0), time.Unix(43, 0))
CheckSuccess(err) CheckSuccess(err)
err = os.Chtimes(wd+"/mnt/file", 82e9, 83e9) err = os.Chtimes(wd+"/mnt/file", time.Unix(82, 0), time.Unix(83, 0))
CheckSuccess(err) CheckSuccess(err)
fi, err := os.Lstat(wd + "/mnt/file") fi, err := os.Lstat(wd + "/mnt/file")
if fi.Atime_ns != 82e9 || fi.Mtime_ns != 83e9 { stat := fuse.ToStatT(fi)
if stat.Atim.Sec != 82 || stat.Mtim.Sec != 83 {
t.Error("Incorrect timestamp", fi) t.Error("Incorrect timestamp", fi)
} }
} }
...@@ -200,13 +202,13 @@ func TestUnionFsChmod(t *testing.T) { ...@@ -200,13 +202,13 @@ func TestUnionFsChmod(t *testing.T) {
ro_fn := wd + "/ro/file" ro_fn := wd + "/ro/file"
m_fn := wd + "/mnt/file" m_fn := wd + "/mnt/file"
writeToFile(ro_fn, "a") writeToFile(ro_fn, "a")
err := os.Chmod(m_fn, 07070) err := os.Chmod(m_fn, 00070)
CheckSuccess(err) CheckSuccess(err)
fi, err := os.Lstat(m_fn) fi, err := os.Lstat(m_fn)
CheckSuccess(err) CheckSuccess(err)
if fi.Mode&07777 != 07270 { if fi.Mode()&07777 != 00270 {
t.Errorf("Unexpected mode found: %o", fi.Mode) t.Errorf("Unexpected mode found: %o", uint32(fi.Mode().Perm()))
} }
_, err = os.Lstat(wd + "/rw/file") _, err = os.Lstat(wd + "/rw/file")
if err != nil { if err != nil {
...@@ -377,7 +379,7 @@ func TestUnionFsMkdirPromote(t *testing.T) { ...@@ -377,7 +379,7 @@ func TestUnionFsMkdirPromote(t *testing.T) {
CheckSuccess(err) CheckSuccess(err)
fi, _ := os.Lstat(wd + "/rw/subdir/subdir2/dir3") fi, _ := os.Lstat(wd + "/rw/subdir/subdir2/dir3")
CheckSuccess(err) CheckSuccess(err)
if fi == nil || !fi.IsDirectory() { if fi == nil || !fi.IsDir() {
t.Error("is not a directory: ", fi) t.Error("is not a directory: ", fi)
} }
} }
...@@ -469,12 +471,12 @@ func TestUnionFsRenameDirBasic(t *testing.T) { ...@@ -469,12 +471,12 @@ func TestUnionFsRenameDirBasic(t *testing.T) {
t.Fatalf("%s/mnt/dir should have disappeared: %v", wd, fi) t.Fatalf("%s/mnt/dir should have disappeared: %v", wd, fi)
} }
if fi, _ := os.Lstat(wd + "/mnt/renamed"); fi == nil || !fi.IsDirectory() { if fi, _ := os.Lstat(wd + "/mnt/renamed"); fi == nil || !fi.IsDir() {
t.Fatalf("%s/mnt/renamed should be directory: %v", wd, fi) t.Fatalf("%s/mnt/renamed should be directory: %v", wd, fi)
} }
entries, err := ioutil.ReadDir(wd + "/mnt/renamed") entries, err := ioutil.ReadDir(wd + "/mnt/renamed")
if err != nil || len(entries) != 1 || entries[0].Name != "subdir" { if err != nil || len(entries) != 1 || entries[0].Name() != "subdir" {
t.Errorf("readdir(%s/mnt/renamed) should have one entry: %v, err %v", wd, entries, err) t.Errorf("readdir(%s/mnt/renamed) should have one entry: %v, err %v", wd, entries, err)
} }
...@@ -517,7 +519,7 @@ func TestUnionFsRenameDirWithDeletions(t *testing.T) { ...@@ -517,7 +519,7 @@ func TestUnionFsRenameDirWithDeletions(t *testing.T) {
CheckSuccess(err) CheckSuccess(err)
freezeRo(wd + "/ro") freezeRo(wd + "/ro")
if fi, _ := os.Lstat(wd + "/mnt/dir/subdir/file.txt"); fi == nil || !fi.IsRegular() { if fi, _ := os.Lstat(wd + "/mnt/dir/subdir/file.txt"); fi == nil || fi.Mode()&os.ModeType != 0 {
t.Fatalf("%s/mnt/dir/subdir/file.txt should be file: %v", wd, fi) t.Fatalf("%s/mnt/dir/subdir/file.txt should be file: %v", wd, fi)
} }
...@@ -535,7 +537,7 @@ func TestUnionFsRenameDirWithDeletions(t *testing.T) { ...@@ -535,7 +537,7 @@ func TestUnionFsRenameDirWithDeletions(t *testing.T) {
t.Fatalf("%s/mnt/dir should have disappeared: %v", wd, fi) t.Fatalf("%s/mnt/dir should have disappeared: %v", wd, fi)
} }
if fi, _ := os.Lstat(wd + "/mnt/renamed"); fi == nil || !fi.IsDirectory() { if fi, _ := os.Lstat(wd + "/mnt/renamed"); fi == nil || !fi.IsDir() {
t.Fatalf("%s/mnt/renamed should be directory: %v", wd, fi) t.Fatalf("%s/mnt/renamed should be directory: %v", wd, fi)
} }
...@@ -566,7 +568,7 @@ func TestUnionFsRenameSymlink(t *testing.T) { ...@@ -566,7 +568,7 @@ func TestUnionFsRenameSymlink(t *testing.T) {
t.Fatalf("%s/mnt/link should have disappeared: %v", wd, fi) t.Fatalf("%s/mnt/link should have disappeared: %v", wd, fi)
} }
if fi, _ := os.Lstat(wd + "/mnt/renamed"); fi == nil || !fi.IsSymlink() { if fi, _ := os.Lstat(wd + "/mnt/renamed"); fi == nil || fi.Mode()&os.ModeSymlink == 0 {
t.Fatalf("%s/mnt/renamed should be link: %v", wd, fi) t.Fatalf("%s/mnt/renamed should be link: %v", wd, fi)
} }
...@@ -586,8 +588,8 @@ func TestUnionFsWritableDir(t *testing.T) { ...@@ -586,8 +588,8 @@ func TestUnionFsWritableDir(t *testing.T) {
fi, err := os.Lstat(wd + "/mnt/subdir") fi, err := os.Lstat(wd + "/mnt/subdir")
CheckSuccess(err) CheckSuccess(err)
if fi.Permission()&0222 == 0 { if fi.Mode().Perm()&0222 == 0 {
t.Errorf("unexpected permission %o", fi.Permission()) t.Errorf("unexpected permission %o", fi.Mode().Perm())
} }
} }
...@@ -625,8 +627,11 @@ func TestUnionFsLink(t *testing.T) { ...@@ -625,8 +627,11 @@ func TestUnionFsLink(t *testing.T) {
fi1, err := os.Lstat(wd + "/mnt/file") fi1, err := os.Lstat(wd + "/mnt/file")
CheckSuccess(err) CheckSuccess(err)
if fi1.Ino != fi2.Ino {
t.Errorf("inode numbers should be equal for linked files %v, %v", fi1.Ino, fi2.Ino) s1 := fuse.ToStatT(fi1)
s2 := fuse.ToStatT(fi2)
if s1.Ino != s2.Ino {
t.Errorf("inode numbers should be equal for linked files %v, %v", s1.Ino, s2.Ino)
} }
c, err := ioutil.ReadFile(wd + "/mnt/linked") c, err := ioutil.ReadFile(wd + "/mnt/linked")
if string(c) != content { if string(c) != content {
...@@ -666,14 +671,14 @@ func TestUnionFsCopyChmod(t *testing.T) { ...@@ -666,14 +671,14 @@ func TestUnionFsCopyChmod(t *testing.T) {
fi, err := os.Lstat(fn) fi, err := os.Lstat(fn)
CheckSuccess(err) CheckSuccess(err)
if fi.Mode&0111 == 0 { if fi.Mode()&0111 == 0 {
t.Errorf("1st attr error %o", fi.Mode) t.Errorf("1st attr error %o", fi.Mode())
} }
time.Sleep(entryTtl * 1.1e9) time.Sleep(entryTtl)
fi, err = os.Lstat(fn) fi, err = os.Lstat(fn)
CheckSuccess(err) CheckSuccess(err)
if fi.Mode&0111 == 0 { if fi.Mode()&0111 == 0 {
t.Errorf("uncached attr error %o", fi.Mode) t.Errorf("uncached attr error %o", fi.Mode())
} }
} }
...@@ -694,15 +699,15 @@ func TestUnionFsTruncateTimestamp(t *testing.T) { ...@@ -694,15 +699,15 @@ func TestUnionFsTruncateTimestamp(t *testing.T) {
CheckSuccess(err) CheckSuccess(err)
time.Sleep(0.2e9) time.Sleep(0.2e9)
truncTs := time.Nanoseconds() truncTs := time.Now()
err = os.Truncate(fn, 3) err = os.Truncate(fn, 3)
CheckSuccess(err) CheckSuccess(err)
fi, err := os.Lstat(fn) fi, err := os.Lstat(fn)
CheckSuccess(err) CheckSuccess(err)
if abs(truncTs-fi.Mtime_ns) > 0.1e9 { if truncTs.Sub(fi.ModTime()) > 100*time.Millisecond {
t.Error("timestamp drift", truncTs, fi.Mtime_ns) t.Error("timestamp drift", truncTs, fi.ModTime())
} }
} }
...@@ -810,7 +815,7 @@ func TestUnionFsDropDeletionCache(t *testing.T) { ...@@ -810,7 +815,7 @@ func TestUnionFsDropDeletionCache(t *testing.T) {
} }
// Expire kernel entry. // Expire kernel entry.
time.Sleep(0.6e9 * entryTtl) time.Sleep((6 * entryTtl) / 10)
err = ioutil.WriteFile(wd+"/mnt/.drop_cache", []byte(""), 0644) err = ioutil.WriteFile(wd+"/mnt/.drop_cache", []byte(""), 0644)
CheckSuccess(err) CheckSuccess(err)
_, err = os.Lstat(wd + "/mnt/file") _, err = os.Lstat(wd + "/mnt/file")
...@@ -894,7 +899,7 @@ func TestUnionFsDisappearing(t *testing.T) { ...@@ -894,7 +899,7 @@ func TestUnionFsDisappearing(t *testing.T) {
oldRoot := wrFs.Root oldRoot := wrFs.Root
wrFs.Root = "/dev/null" wrFs.Root = "/dev/null"
time.Sleep(1.5 * entryTtl * 1e9) time.Sleep((3 * entryTtl) / 2)
_, err = ioutil.ReadDir(wd + "/mnt") _, err = ioutil.ReadDir(wd + "/mnt")
if err == nil { if err == nil {
...@@ -910,7 +915,7 @@ func TestUnionFsDisappearing(t *testing.T) { ...@@ -910,7 +915,7 @@ func TestUnionFsDisappearing(t *testing.T) {
// Restore, and wait for caches to catch up. // Restore, and wait for caches to catch up.
wrFs.Root = oldRoot wrFs.Root = oldRoot
time.Sleep(1.5 * entryTtl * 1e9) time.Sleep((3 * entryTtl) / 2)
_, err = ioutil.ReadDir(wd + "/mnt") _, err = ioutil.ReadDir(wd + "/mnt")
if err != nil { if err != nil {
...@@ -937,7 +942,7 @@ func TestUnionFsDeletedGetAttr(t *testing.T) { ...@@ -937,7 +942,7 @@ func TestUnionFsDeletedGetAttr(t *testing.T) {
err = os.Remove(wd + "/mnt/file") err = os.Remove(wd + "/mnt/file")
CheckSuccess(err) CheckSuccess(err)
if fi, err := f.Stat(); err != nil || !fi.IsRegular() { if fi, err := f.Stat(); err != nil || fi.Mode()&os.ModeType != 0 {
t.Fatalf("stat returned error or non-file: %v %v", err, fi) t.Fatalf("stat returned error or non-file: %v %v", err, fi)
} }
} }
...@@ -1033,8 +1038,8 @@ func TestUnionFsFlushSize(t *testing.T) { ...@@ -1033,8 +1038,8 @@ func TestUnionFsFlushSize(t *testing.T) {
f.Close() f.Close()
fi, err = os.Lstat(fn) fi, err = os.Lstat(fn)
CheckSuccess(err) CheckSuccess(err)
if fi.Size != int64(n) { if fi.Size() != int64(n) {
t.Errorf("got %d from Stat().Size, want %d", fi.Size, n) t.Errorf("got %d from Stat().Size, want %d", fi.Size(), n)
} }
} }
...@@ -1060,8 +1065,8 @@ func TestUnionFsFlushRename(t *testing.T) { ...@@ -1060,8 +1065,8 @@ func TestUnionFsFlushRename(t *testing.T) {
fi, err = os.Lstat(dst) fi, err = os.Lstat(dst)
CheckSuccess(err) CheckSuccess(err)
if fi.Size != int64(n) { if fi.Size() != int64(n) {
t.Errorf("got %d from Stat().Size, want %d", fi.Size, n) t.Errorf("got %d from Stat().Size, want %d", fi.Size(), n)
} }
} }
...@@ -1078,8 +1083,8 @@ func TestUnionFsTruncGetAttr(t *testing.T) { ...@@ -1078,8 +1083,8 @@ func TestUnionFsTruncGetAttr(t *testing.T) {
CheckSuccess(err) CheckSuccess(err)
fi, err := os.Lstat(wd + "/mnt/file") fi, err := os.Lstat(wd + "/mnt/file")
if fi.Size != int64(len(c)) { if fi.Size() != int64(len(c)) {
t.Fatalf("Length mismatch got %d want %d", fi.Size, len(c)) t.Fatalf("Length mismatch got %d want %d", fi.Size(), len(c))
} }
} }
...@@ -1103,11 +1108,11 @@ func TestUnionFsPromoteDirTimeStamp(t *testing.T) { ...@@ -1103,11 +1108,11 @@ func TestUnionFsPromoteDirTimeStamp(t *testing.T) {
// TODO - need to update timestamps after promoteDirsTo calls, // TODO - need to update timestamps after promoteDirsTo calls,
// not during. // not during.
if false && fRo.Mtime_ns != fRw.Mtime_ns { if false && fRo.ModTime().Equal(fRw.ModTime()) {
t.Errorf("Changed timestamps on promoted subdir: ro %d rw %d", fRo.Mtime_ns, fRw.Mtime_ns) t.Errorf("Changed timestamps on promoted subdir: ro %d rw %d", fRo.ModTime(), fRw.ModTime())
} }
if fRo.Mode|0200 != fRw.Mode { if fRo.Mode().Perm()|0200 != fRw.Mode().Perm() {
t.Errorf("Changed mode ro: %o, rw: %o", fRo.Mode, fRw.Mode) t.Errorf("Changed mode ro: %v, rw: %v", fRo.Mode(), fRw.Mode())
} }
} }
...@@ -55,7 +55,7 @@ func TestMultiZipFs(t *testing.T) { ...@@ -55,7 +55,7 @@ func TestMultiZipFs(t *testing.T) {
entries, err := ioutil.ReadDir(mountPoint) entries, err := ioutil.ReadDir(mountPoint)
CheckSuccess(err) CheckSuccess(err)
if len(entries) != 1 || string(entries[0].Name) != "config" { if len(entries) != 1 || string(entries[0].Name()) != "config" {
t.Errorf("wrong names return. %v", entries) t.Errorf("wrong names return. %v", entries)
} }
...@@ -63,7 +63,7 @@ func TestMultiZipFs(t *testing.T) { ...@@ -63,7 +63,7 @@ func TestMultiZipFs(t *testing.T) {
CheckSuccess(err) CheckSuccess(err)
fi, err := os.Lstat(mountPoint + "/zipmount") fi, err := os.Lstat(mountPoint + "/zipmount")
if !fi.IsDirectory() { if !fi.IsDir() {
t.Errorf("Expect directory at /zipmount") t.Errorf("Expect directory at /zipmount")
} }
...@@ -81,14 +81,14 @@ func TestMultiZipFs(t *testing.T) { ...@@ -81,14 +81,14 @@ func TestMultiZipFs(t *testing.T) {
fi, err = os.Lstat(mountPoint + "/zipmount") fi, err = os.Lstat(mountPoint + "/zipmount")
CheckSuccess(err) CheckSuccess(err)
if !fi.IsDirectory() { if !fi.IsDir() {
t.Fatal("expect directory for /zipmount, got %v", fi) t.Fatal("expect directory for /zipmount, got %v", fi)
} }
// Check that zipfs itself works. // Check that zipfs itself works.
fi, err = os.Stat(mountPoint + "/zipmount/subdir") fi, err = os.Stat(mountPoint + "/zipmount/subdir")
CheckSuccess(err) CheckSuccess(err)
if !fi.IsDirectory() { if !fi.IsDir() {
t.Error("directory type", fi) t.Error("directory type", fi)
} }
......
...@@ -24,7 +24,7 @@ func HeaderToFileInfo(h *tar.Header) (*fuse.Attr, string) { ...@@ -24,7 +24,7 @@ func HeaderToFileInfo(h *tar.Header) (*fuse.Attr, string) {
} }
a.Uid = uint32(h.Uid) a.Uid = uint32(h.Uid)
a.Gid = uint32(h.Gid) a.Gid = uint32(h.Gid)
a.SetTimes(h.Atime, h.Mtime, h.Ctime) a.SetTimes(&h.AccessTime, &h.ModTime, &h.ChangeTime)
return a, h.Name return a, h.Name
} }
......
...@@ -45,14 +45,14 @@ func TestZipFs(t *testing.T) { ...@@ -45,14 +45,14 @@ func TestZipFs(t *testing.T) {
} }
fi, err := os.Stat(mountPoint + "/subdir") fi, err := os.Stat(mountPoint + "/subdir")
CheckSuccess(err) CheckSuccess(err)
if !fi.IsDirectory() { if !fi.IsDir() {
t.Error("directory type", fi) t.Error("directory type", fi)
} }
fi, err = os.Stat(mountPoint + "/file.txt") fi, err = os.Stat(mountPoint + "/file.txt")
CheckSuccess(err) CheckSuccess(err)
if !fi.IsRegular() { if fi.IsDir() {
t.Error("file type", fi) t.Error("file type", fi)
} }
...@@ -75,7 +75,7 @@ func TestLinkCount(t *testing.T) { ...@@ -75,7 +75,7 @@ func TestLinkCount(t *testing.T) {
fi, err := os.Stat(mp + "/file.txt") fi, err := os.Stat(mp + "/file.txt")
CheckSuccess(err) CheckSuccess(err)
if fi.Nlink != 1 { if fuse.ToStatT(fi).Nlink != 1 {
t.Fatal("wrong link count", fi.Nlink) t.Fatal("wrong link count", fuse.ToStatT(fi).Nlink)
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment