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