func Mkdirs(t Fataler, dirs ...string) { if len(dirs) == 0 { t.Fatalf("not enough dirs") } for _, d := range dirs { for _, dp := range MkdirPaths { printf("mkdir %s\n", dp) p := zx.Path(d, dp) if err := os.Mkdir(p, 0750); err != nil { t.Fatalf("mkdir %s: %s", p, err) } fi, err := os.Stat(p) if err != nil { t.Fatalf("didn't stat: %s", err) } if !fi.IsDir() { t.Fatalf("didn't create a dir") } if fi.Mode().String() != "drwxr-x---" { t.Fatalf("bad dir mode") } } for _, dp := range BadMkdirPaths { printf("mkdir %s\n", dp) p := zx.Path(d, dp) if err := os.Mkdir(p, 0750); err == nil { t.Fatalf("mkdir %s: didn't fail") } } } }
func diff(rc chan<- string, fn string, mtime bool, dirs ...string) { d0 := dirs[0] dirs = dirs[1:] for _, di := range dirs { p0 := zx.Path(d0, fn) pi := zx.Path(di, fn) s := diff1(p0, pi, fn != "/", mtime) if s != "" { rc <- fmt.Sprintf("%s: chg %s %s", di, fn, s) } c0 := Children(p0) c1 := Children(pi) if len(c0) ==0 && len(c1) == 0 { continue } for _, c := range c0 { cf := zx.Path(fn, c) if !member(c, c1) { rc <- fmt.Sprintf("%s: del %s", di, cf) continue } diff(rc, cf, mtime, d0, di) } for _, c := range c1 { if !member(c, c0) { cf := zx.Path(fn, c) rc <- fmt.Sprintf("%s: add %s", di, cf) } } } }
// Walk to the given rid checking out perms when t.ai is not nil. // Returns also the gid and mode even when no perms are checked. func (t *Lfs) canWalkTo(rid string, forwhat int) (string, uint64, error) { if !t.readattrs || rid == "/Ctl" { return dbg.Usr, 0777, nil } noperm := t.NoPermCheck || t.ai == nil elems := zx.Elems(rid) fpath := t.path pgid := "" var pmode uint64 if len(elems) == 0 { elems = append(elems, "/") } for len(elems) > 0 { if noperm { // skip to just the final step if len(elems) > 1 { fpath = zx.Path(t.path, path.Dir(rid)) elems = elems[len(elems)-1:] } } pd := zx.Dir{ "name": elems[0], } fpath = zx.Path(fpath, elems[0]) elems = elems[1:] st, err := os.Stat(fpath) if err != nil { return "", 0, err } mode := st.Mode() m := int64(mode & 0777) pd["mode"] = "0" + strconv.FormatInt(m, 8) t.fileAttrs(fpath, pd) if !noperm && len(elems) > 0 && !pd.CanWalk(t.ai) { return "", 0, dbg.ErrPerm } if len(elems) == 0 { pgid = pd["Gid"] pmode = uint64(pd.Int("mode")) } if !noperm && len(elems) == 0 && forwhat != 0 { if !pd.Can(t.ai, forwhat) { return "", 0, dbg.ErrPerm } } } if pgid == "" { pgid = dbg.Usr } if pmode == 0 { pmode = 0775 } return pgid, pmode, nil }
// make sure paths are ok in children after a move func (f *mFile) moved() { f.mlk.Lock() fpath := f.d["path"] fspath := f.d["spath"] f.mlk.Unlock() f.clk.Lock() defer f.clk.Unlock() for _, cs := range f.child { cs.mlk.Lock() cs.d["path"] = zx.Path(fpath, cs.name) cs.d["spath"] = zx.Path(fspath, cs.name) cs.mlk.Unlock() cs.moved() } }
func All(t Fataler, dirs ...string) { diffs, err := Diff(WithoutMtime, dirs...) if err != nil { t.Fatalf("diff errors") } if len(diffs) != 0 { t.Fatalf("initial diffs") } Stats(t, dirs...) Gets(t, dirs...) AsAFile(t, dirs...) Puts(t, dirs...) Mkdirs(t, dirs...) Wstats(t, dirs...) Removes(t, dirs...) for _, d := range dirs { os.Remove(zx.Path(d, "/foo")) } diffs, err = Diff(WithoutMtime, dirs...) if err != nil { t.Fatalf("diff errors") } for _, d := range diffs { printf("diff %s\n", d) } if len(diffs) != 0 { t.Fatalf("final diffs") } }
func Wstats(t Fataler, dirs ...string) { if len(dirs) == 0 { t.Fatalf("not enough dirs") } for _, d := range dirs { for _, dp := range WstatTests { p := zx.Path(d, dp.Path) printf("wstat %s %o %d\n", dp.Path, os.FileMode(dp.Mode)&0777, dp.Mtime) if err := os.Chmod(p, dp.Mode); err != nil { if dp.Fails { continue } t.Fatalf("chmod: %s", err) } if dp.Fails { t.Fatalf("wstat didn't fail") } mt := time.Unix(dp.Mtime, 0) if err := os.Chtimes(p, mt, mt); err != nil { t.Fatalf("chtime: %s", err) } fi, err := os.Stat(p) if err != nil { t.Fatalf("stat: %s", err) } if uint(fi.Mode())&0777 != uint(dp.Mode) { t.Fatalf("wrong mode") } if fi.ModTime().Unix() != dp.Mtime { t.Fatalf("wrong mtime") } } } }
func Puts(t Fataler, dirs ...string) { if len(dirs) == 0 { t.Fatalf("not enough dirs") } for _, d := range dirs { nn := 0 for _, pt := range PutTests { printf("put %s\n", pt.Path) p := zx.Path(d, pt.Path) // 1. use create fd, err := os.Create(p) if err != nil && !pt.Fails { t.Fatalf("create: %s", err) } if pt.Fails && err == nil { t.Fatalf("create did not fail") } if pt.Fails { continue } nn++ var buf bytes.Buffer for i := 0; i < 1000*nn; i++ { fmt.Fprintf(fd, "hi %s %d\n", pt.Path, i) fmt.Fprintf(&buf, "hi %s %d\n", pt.Path, i) } fd.Close() printf("%s: %d bytes\n", pt.Path, buf.Len()) dat, err := ioutil.ReadFile(p) if err != nil { t.Fatalf("read: %s", err) } if bytes.Compare(buf.Bytes(), dat) != 0 { t.Fatalf("didn't put the bytes") } // 2. use openfile with truncate fd, err = os.OpenFile(p, os.O_TRUNC|os.O_RDWR, 0644) if err != nil { t.Fatalf("read: %s", err) } for i := 0; i < 1000*nn; i++ { fmt.Fprintf(fd, "hi %s %d\n", pt.Path, i) } fd.Close() dat, err = ioutil.ReadFile(p) if err != nil { t.Fatalf("read: %s", err) } if bytes.Compare(buf.Bytes(), dat) != 0 { t.Fatalf("didn't put the bytes") } } } }
// Walk to rid, and return it r/wlocked (unless there's an error) with at least its meta updated. func (fs *Cfs) walk(rid string, isrlock bool) (f *cFile, left []string, e error) { fs.dprintf("walk %s...\n", rid) els := zx.Elems(rid) left = els defer func() { if e != nil { fs.dprintf("walkerr %s left %v\n", rid, left) } else { fs.dprintf("walked %s left %v\n", rid, left) } if e == nil && len(left) > 0 { panic("cfs walk: left and no error") } }() path := "/" for { isr := len(els) > 0 || isrlock nf, err := fs.cwalk(path, isr) if err != nil { return f, left, err } left = els f = nf if len(els) > 0 { path = zx.Path(path, els[0]) els = els[1:] } if len(left) == 0 { if err := f.needMeta(isr); err != nil { // lock has been released if dbg.IsNotExist(err) { // discovered in needMeta that the file is gone // we raced, so try again f, left, err = fs.walk(rid, isrlock) } return f, left, err } return f, left, nil } if err := f.needData(isr, nil); err != nil { // lock has been released if dbg.IsNotExist(err) { // discovered in needMeta that the file is gone // we raced, so try again f, left, err = fs.walk(rid, isrlock) } return f, left, err } if !fs.NoPermCheck && !f.d.CanWalk(fs.ai) { f.unlockc(isr) return f, left, fmt.Errorf("%s: %s", f, dbg.ErrPerm) } f.unlockc(isr) } }
func main() { defer dbg.Exits("") os.Args[0] = "zxdump" dfltdump := zx.Path(dbg.Home, "dump") opts.NewFlag("s", "don't dump right now, wait until next at 5am", &Skip) opts.NewFlag("1", "dump once and exit", &Once) opts.NewFlag("v", "verbose", &Verbose) opts.NewFlag("D", "debug", &Debug) opts.NewFlag("x", "expr: files excluded (.*, tmp.* if none given); tmp always excluded.", &Xcludes) Dump = dfltdump opts.NewFlag("d", "dir: where to keep the dump, or empty if none", &Dump) args, err := opts.Parse(os.Args) if err != nil { dbg.Warn("%s", err) opts.Usage() dbg.Exits(err) } if len(Xcludes) == 0 { Xcludes = []string{".*", "tmp.*", "*.tmp"} } Xcludes = append(Xcludes, "tmp") if len(args) == 0 { dbg.Warn("arguments missing") opts.Usage() dbg.Exits("usage") } if Skip && Once { dbg.Fatal("can't skip the current dump and dump once now") } nt := 0 ec := make(chan bool) for i := 0; i < len(args); i++ { al := strings.SplitN(args[i], "!", 2) if len(al) == 1 { al = append(al, al[0]) al[0] = path.Base(al[0]) } t, err := lfs.New(al[0], al[1], lfs.RO) if err != nil { dbg.Warn("%s: %s", al[0], err) continue } t.ReadAttrs(true) nt++ go dump(Dump, t, ec) } if nt == 0 { dbg.Fatal("no trees to dump") } for nt > 0 { <-ec nt-- } }
func Removes(t Fataler, dirs ...string) { if len(dirs) == 0 { t.Fatalf("not enough dirs") } for _, d := range dirs { for _, dp := range RemovePaths { printf("remove %s\n", dp) p := zx.Path(d, dp) if err := os.Remove(p); err != nil { t.Fatalf("rm %s: %s", p, err) } } for _, dp := range BadRemovePaths { printf("remove %s\n", dp) p := zx.Path(d, dp) if err := os.Remove(p); err == nil { t.Fatalf("rm %s: did not fail", p) } } } }
func (t *Fs) getdir(f File, d zx.Dir, off, count int64, c chan<- []byte) error { gf, ok := f.(Walker) if !ok { return nil } ns, err := gf.Getdir() if err != nil { return err } sort.Sort(sort.StringSlice(ns)) if d["name"] == "/" { ns = append([]string{"Ctl"}, ns...) } Dloop: for _, n := range ns { if n == "" { err = fmt.Errorf("%s: empty name in getdir", d["path"], n) app.Warn("fs bug: %s", err) return err } if off > 0 { off-- continue } switch count { case zx.All: break case 0: break Dloop default: count-- } if d["name"]=="/" && n == "Ctl" { cd := ctldir.Dup() cd["tpath"] = t.path cd.Send(c) continue } cf, err := gf.Walk(n) if err != nil { return err } cp := zx.Path(d["path"], n) cd, err := t.statf(cf, cp) if err != nil { return err } t.Dprintf("getdir %s: %s\n", gf, cf) if _, err := cd.Send(c); err != nil { return err } } return nil }
func newDumpDir(dir, dfpath string, rf zx.File, ds []zx.Dir, dhash []string) error { if len(dhash) != 2*len(ds) { panic("newDumpDir: dhash length bug") } dprintf("create %s\t%s\n", dfpath, rf.D["path"]) if err := os.MkdirAll(dfpath, 0750); err != nil { return err } var err error for i := 0; i < len(ds); i++ { cname := dhash[2*i] if cname == "" { continue // entry had errors } if cname != ds[i]["name"] { panic("newDumpDir: bad entry") } cdfpath := zx.Path(dir, dhash[2*i+1]) cpath := zx.Path(dfpath, cname) dprintf("link %s\t<- %s\n", cdfpath, cpath) var e error e = os.Symlink(cdfpath, cpath) // TODO: save user attributes from ds[i] for cname at dfpath/.#zx if err == nil && e != nil { err = e } if e == nil { saveAttrs(dfpath, ds[i]) } } mt := rf.D.Time("mtime") os.Chtimes(dfpath, mt, mt) mode := rf.D.Int("mode") if mode != 0 { // uncomment to set dir modes -w // mode = mode &^ 0222 os.Chmod(dfpath, os.FileMode(mode)) } return err }
func Gets(t Fataler, dirs ...string) { if len(dirs) == 0 { t.Fatalf("not enough dirs") } for _, d := range dirs { for _, fp := range GetFPaths { p := zx.Path(d, fp) dat, err := ioutil.ReadFile(p) if err != nil { t.Fatalf("get %s: %s", p, err) } printf("got %d bytes \n\n", len(dat)) if string(dat) != string(FileData[fp]) { printf("got <%s>\nexpected<%s>\n", string(dat), string(FileData[fp])) t.Fatalf("%s: bad data", p) } } for _, dp := range GetDPaths { printf("getall %s\n", dp) p := zx.Path(d, dp) cs := Children(p) printf("children `%s`\n", strings.Join(cs, " ")) if strings.Join(cs, " ") != GetDOuts[dp] { t.Logf("got %s", strings.Join(cs, " ")) t.Fatalf("bad dir data for %s", dp) } } for _, fp := range BadPaths { p := zx.Path(d, fp) _, err := ioutil.ReadFile(p) if err == nil { t.Fatalf("%s did not fail", fp) } } } }
func (t *Lfs) stat(rid string) (zx.Dir, error) { rid, err := zx.AbsPath(rid) if err != nil { return nil, err } if rid == "/Ctl" { d := ctldir.Dup() d["tpath"] = t.path return d, nil } path := zx.Path(t.path, rid) st, err := os.Stat(path) if err != nil { return nil, err } nd := 0 sum := "" if st.IsDir() { nd, sum = dirsz(path) if rid == "/" { nd++ } } d := zx.NewDir(st, nd) if t.readattrs { t.fileAttrs(path, d) } if sum != "" { d["Sum"] = sum } d["tpath"] = t.path d["path"] = rid d["spath"] = rid d["proto"] = "lfs" if rid == "/" { d["name"] = "/" } if d["Uid"] == "" { d["Uid"] = dbg.Usr } if d["Gid"] == "" { d["Gid"] = dbg.Usr } if d["Wuid"] == "" { d["Wuid"] = dbg.Usr } return d, nil }
func (t *Fs) walk(rid string) (File, error) { rid, err := zx.AbsPath(rid) if err != nil { return nil, err } if rid == "/" { return t.root, nil } if rid == "/Ctl" { return nil, nil } els := zx.Elems(rid) f := t.root p := "/" var d zx.Dir for _, e := range els { t.Dprintf("walk %s %s...\n", f, e) d, err = f.Stat() if err != nil { t.Dprintf("\tstat: %s\n", f, err) return nil, err } if d["type"] != "d" { t.Dprintf("\tnot dir\n") return nil, fmt.Errorf("%s: %s", p, dbg.ErrNotDir) } if !t.NoPermCheck && !d.CanWalk(t.ai) { t.Dprintf("\tno perm\n") return nil, fmt.Errorf("%s: %s", p, dbg.ErrPerm) } wf, ok := f.(Walker) if !ok { t.Dprintf("\tnot walker\n") return nil, fmt.Errorf("%s: %s: %s", p, e, dbg.ErrNotExist) } f, err = wf.Walk(e) if err != nil { t.Dprintf("\twalk: %s\n", err) return nil, err } p = zx.Path(p, e) t.Dprintf("walked %s\n", f) } return f, nil }
func Stats(t Fataler, dirs ...string) { if len(dirs) == 0 { t.Fatalf("not enough dirs") } for _, d := range dirs { for _, st := range StatTests { p := zx.Path(d, st.Path) fi, err := os.Stat(p) if err == nil && st.Fails { t.Fatalf("%s did not fail", st.Path) } if err != nil && !st.Fails { t.Fatalf("%s did fail", st.Path) } printf("\t`%s`,\n", stat(fi)) if !st.Fails && stat(fi) != st.Res { t.Fatalf("wrong stat <%s> vs <%s>", stat(fi), st.Res) } } } }
func dumpFile(dir, name string, f zx.File) (string, error) { dc := f.T.Get(f.D["path"], 0, zx.All, "") h := sha1.New() for dat := range dc { h.Write(dat) } err := cerror(dc) if err != nil { dprintf("dump file %s: get: %s\n", f.D["path"], err) return "", err } sum := h.Sum(nil) s := fmt.Sprintf("%02x/%02x/%036x", sum[0], sum[1], sum[2:]) dfpath := zx.Path(dir, s) fi, err := os.Stat(dfpath) dprintf("dump file %s\t%s\n", name, s) if fi != nil { return s, nil } vprintf("%s: new %s\n", os.Args[0], name) return s, newDumpFile(dfpath, f) }
// Resolve a name and return the prefix path, the array of mount points for it and the server paths // for each mount point. // The path must be absolute. func (ns *Tree) Resolve(name string) (pref string, mnts []zx.Dir, spaths []string, err error) { path, err := zx.AbsPath(name) if err != nil { return "", nil, nil, err } ns.dprintf("resolve %s\n", path) ns.lk.RLock() defer ns.lk.RUnlock() var p *prefix for _, np := range ns.pref { if zx.HasPrefix(path, np.name) { ns.dprintf("\thasprefix %s %s\n", path, np.name) p = np } } if p == nil { ns.dprintf("\tno prefixes\n") return "", nil, nil, dbg.ErrNotExist } suff := zx.Suffix(path, p.name) mnts = make([]zx.Dir, 0, len(p.mnt)) spaths = []string{} for _, d := range p.mnt { if isfinder(d) || suff=="" || suff=="/" { mnts = append(mnts, d.Dup()) spath := zx.Path(suff, d["spath"]) spaths = append(spaths, spath) ns.dprintf("\ts='%s' d=%s\n", spath, d) } else { ns.dprintf("\tskip %s\n", d) } } if len(mnts) == 0 { ns.dprintf("\tno prefixes left\n") return "", nil, nil, dbg.ErrNotExist } return p.name, mnts, spaths, nil }
// Make sure /foo behaves as a file issuing calls from 3 fds. func AsAFile(t Fataler, dirs ...string) { if len(dirs) == 0 { t.Fatalf("not enough dirs") } fn := "/foo" for _, d := range dirs { p := zx.Path(d, fn) fd, err := os.Create(p) if err != nil { t.Fatalf("create: %s: %s", d, err) } fd1, err := os.Create(p) if err != nil { t.Fatalf("create: %s: %s", d, err) } fd2, err := os.Create(p) if err != nil { t.Fatalf("create: %s: %s", d, err) } fds := []rwtest.Object{fd, fd1, fd2} rwtest.AsAConcFile(t, fds, 1000, 128 * 1024, 3803) } }
func dump(dir string, t zx.Tree, ec chan bool) { defer func() { ec <- true }() name := t.Name() data := zx.Path(dir, "data") doskip := Skip for { if doskip { waitDumpTime(name) doskip = false continue } dbg.Warn("snap %s...", name) if err := os.MkdirAll(data, 0750); err != nil { dbg.Warn("%s: %s", data, err) return } rd, err := zx.Stat(t, "/") if err != nil { dbg.Warn("%s: %s", name, err) continue } // make sure it's not empty ds, err := zx.GetDir(t, "/") if err != nil { dbg.Warn("%s: %s", name, err) continue } if len(ds) == 0 { dbg.Warn("%s: file system is empty. ignored.", name) continue } s, err := dumpDir(data, name, zx.File{t, rd}) if err != nil { dbg.Warn("%s: %s", name, err) } ts := time.Now().Format("2006/0102") tree := strings.Replace(name, "/", ".", -1) tspath0 := zx.Path(dir, tree, ts) os.MkdirAll(path.Dir(tspath0), 0755) spath := zx.Path(data, s) tspath := tspath0 for i := 1; ; i++ { fi, _ := os.Stat(tspath) if fi == nil { break } tspath = fmt.Sprintf("%s.%d", tspath0, i) } os.MkdirAll(path.Dir(tspath), 0755) if err := os.Symlink(spath, tspath); err != nil { dbg.Warn("%s: %s", name, err) } dbg.Warn("snap %s %s", tspath, s) if Once { break } waitDumpTime(name) } }
// d is a dup and can be changed. func (t *Fs) find(f File, d zx.Dir, p *pred.Pred, spref, dpref string, lvl int, c chan<- zx.Dir, ai *auth.Info) { match, pruned, err := p.EvalAt(d, lvl) t.dprintf("find at %v\n\t%v\n\t%v %v %v\n\n", d, p, match, pruned, err) if pruned { if !match { d["err"] = "pruned" } c <- d return } if d["type"] == "d" && err == nil { if !t.NoPermCheck && !d.CanWalk(ai) { err = dbg.ErrPerm } } if err != nil { d["err"] = err.Error() c <-d return } if d["rm"] != "" { return } if match { if ok := c <- d; !ok { return } } if d["type"] != "d" || f == nil { return } wf, ok := f.(Walker) if !ok { return } ns, err := wf.Getdir() if err != nil { return } sort.Sort(sort.StringSlice(ns)) if d["name"] == "/" { cd := ctldir.Dup() if spref != dpref { cpath := cd["path"] suff := zx.Suffix(cpath, spref) cd["path"] = zx.Path(dpref, suff) } t.find(nil, cd, p, spref, dpref, lvl+1, c, ai) } for _, cnm := range ns { cf, err := wf.Walk(cnm) if err != nil { continue } cp := zx.Path(d["path"], cnm) cd, err := t.statf(cf, cp) if err != nil || cd["rm"] != "" { continue } cd = cd.Dup() if spref != dpref { cpath := cd["path"] suff := zx.Suffix(cpath, spref) cd["path"] = zx.Path(dpref, suff) } t.find(cf, cd, p, spref, dpref, lvl+1, c, ai) } } func (t *Fs) Find(rid, fpred, spref, dpref string, depth int) <-chan zx.Dir { t.dprintf("find %s '%s' '%s' '%s' %d\n", rid, fpred, spref, dpref, depth) cs := t.IOstats.NewCall(zx.Sfind) dc := make(chan zx.Dir) go func() { rid, err := zx.AbsPath(rid) if err != nil { cs.End(err != nil) t.dprintf("find %s: %s\n", rid, err) close(dc, err) return } f, err := t.walk(rid) if err != nil { cs.End(err != nil) t.dprintf("find %s: %s\n", rid, err) close(dc, err) return } p, err := pred.New(fpred) if err != nil { cs.End(err != nil) t.dprintf("find %s: %s\n", rid, err) close(dc, err) return } d, err := t.statf(f, rid) if err != nil { cs.End(err != nil) t.dprintf("find %s: %s\n", rid, err) close(dc, err) return } d = d.Dup() if spref != dpref { suff := zx.Suffix(rid, spref) d["path"] = zx.Path(dpref, suff) } t.find(f, d, p, spref, dpref, depth, dc, t.ai) cs.End(err != nil) t.dprintf("find %s: ok\n", rid) close(dc) }() return dc } // used only by findget func (t *Fs) get(rid string, datac chan<- []byte) error { f, err := t.walk(rid) if err != nil { return err } gf, ok := f.(Getter) if !ok { return nil } return gf.Get(0, -1, datac) } func (t *Fs) FindGet(rid, fpred, spref, dpref string, depth int) <-chan zx.DirData { t.dprintf("findget %s '%s' '%s' '%s' %d\n", rid, fpred, spref, dpref, depth) gc := make(chan zx.DirData) cs := t.IOstats.NewCall(zx.Sfindget) go func() { dc := t.Find(rid, fpred, spref, dpref, depth) // BUG: will stat a Sfind for d := range dc { g := zx.DirData{Dir: d} var datac chan []byte if d["err"] == "" && !t.NoPermCheck && d["type"] != "d" && !d.CanRead(t.ai) { d["err"] = dbg.ErrPerm.Error() } if d["err"]=="" && d["type"]!="d" { datac = make(chan []byte) g.Datac = datac } if ok := gc <- g; !ok { close(dc, cerror(gc)) break } if datac != nil { err := t.get(d["spath"], datac) close(datac, err) } } err := cerror(dc) cs.End(err != nil) if err != nil { t.dprintf("find %s: %s\n", rid, err) } else { t.dprintf("find %s: ok\n", rid) } close(gc, err) }() return gc }
// d is a dup and can be changed. func (f *mFile) find(d zx.Dir, p *pred.Pred, spref, dpref string, lvl int, c chan<- zx.Dir, ai *auth.Info) { match, pruned, err := p.EvalAt(d, lvl) f.dprintf("find at %d %s\n\t%v\n\t%v %v %v\n\n", lvl, d.Long(), p, match, pruned, err) if pruned { if !match { d["err"] = "pruned" } c <- d return } if d["type"] == "d" && err == nil { f.mlk.Lock() if !f.t.NoPermCheck && !f.d.CanWalk(ai) { err = dbg.ErrPerm } f.mlk.Unlock() } if err != nil { d["err"] = err.Error() c <-d return } if d["rm"] != "" { return } if match { if ok := c <- d; !ok { return } } if d["type"] != "d" { return } f.clk.Lock() child := make([]*mFile, len(f.child)) copy(child, f.child) f.clk.Unlock() if f.name == "/" { nc := []*mFile{ &mFile{name: "Ctl", d: ctldir.Dup(), t: f.t}, } nc[0].d["tpath"] = f.t.path child = append(nc, child...) } for _, cf := range child { cf.mlk.Lock() cd := cf.d.Dup() cf.mlk.Unlock() // fmt.Printf("child %s\n", cd) if cd["rm"] != "" { continue } if spref != dpref { cpath := cd["path"] suff := zx.Suffix(cpath, spref) cd["path"] = zx.Path(dpref, suff) } cf.find(cd, p, spref, dpref, lvl+1, c, ai) } } func (t *Fs) Find(rid, fpred, spref, dpref string, depth int) <-chan zx.Dir { t.dprintf("find %s '%s' '%s' '%s' %d\n", rid, fpred, spref, dpref, depth) cs := t.IOstats.NewCall(zx.Sfind) dc := make(chan zx.Dir) go func() { rid, err := zx.AbsPath(rid) if err != nil { cs.End(err != nil) t.dprintf("find %s: %s\n", rid, err) close(dc, err) return } f, _, err := t.walk(rid, nil) if err != nil { cs.End(err != nil) t.dprintf("find %s: %s\n", rid, err) close(dc, err) return } p, err := pred.New(fpred) if err != nil { cs.End(err != nil) t.dprintf("find %s: %s\n", rid, err) close(dc, err) return } cs.Sending() f.mlk.Lock() d := f.d.Dup() f.mlk.Unlock() if spref != dpref { suff := zx.Suffix(rid, spref) d["path"] = zx.Path(dpref, suff) } f.find(d, p, spref, dpref, depth, dc, t.ai) cs.End(err != nil) t.dprintf("find %s: ok\n", rid) close(dc) }() return dc } func (t *Fs) FindGet(rid, fpred, spref, dpref string, depth int) <-chan zx.DirData { t.dprintf("findget %s '%s' '%s' '%s' %d\n", rid, fpred, spref, dpref, depth) gc := make(chan zx.DirData) cs := t.IOstats.NewCall(zx.Sfindget) go func() { dc := t.Find(rid, fpred, spref, dpref, depth) // BUG: will stat a Sfind for d := range dc { g := zx.DirData{Dir: d} var datac chan []byte if d["err"]=="" && d["type"]=="-" { datac = make(chan []byte) g.Datac = datac } if ok := gc <- g; !ok { close(dc, cerror(gc)) break } if datac != nil { err := t.get(d["spath"], 0, zx.All, datac, nil) close(datac, err) } } err := cerror(dc) cs.End(err != nil) if err != nil { t.dprintf("find %s: %s\n", rid, err) } else { t.dprintf("find %s: ok\n", rid) } close(gc, err) }() return gc } func (t *Fs) Dump(w io.Writer) { if t == nil { fmt.Fprintf(w, "<nil tree>\n") return } fmt.Fprintf(w, "tree [%s] path %s\n", t.name, t.path) t.root.Dump(w, 0) fmt.Fprintf(w, "\n") } func (f *mFile) Dump(w io.Writer, lvl int) { tabs := strings.Repeat(" ", lvl) if f == nil { fmt.Fprintf(w, "%s<nil file>\n", tabs) return } d := f.d.Dup() if d["path"] == "/" { d["size"] = "0" } fmt.Fprintf(w, "%s%s\n", tabs, d.TestFmt()) if d["type"] != "d" { fmt.Fprintf(w, "%s %d bytes\n", tabs, f.data.Len()) return } for _, c := range f.child { c.Dump(w, lvl+1) } }
func (t *Fs) Move(from, to string) chan error { t.dprintf("move %s %s \n", from, to) cs := t.IOstats.NewCall(zx.Smove) c := make(chan error, 1) from, ferr := zx.AbsPath(from) to, terr := zx.AbsPath(to) var pfrom, pto *mFile var leftto []string var err error var fd, pd *mFile if ferr != nil { err = ferr } else if terr != nil { err = terr } else if from == to { c <- nil close(c) cs.End(false) return c } if err == nil { fd, _, err = t.walk(path.Dir(from), nil) } if err == nil && !t.noPerms() { fd.mlk.Lock() if !fd.d.CanWrite(t.ai) { err = fmt.Errorf("%s: %s", fd, dbg.ErrPerm) } fd.mlk.Unlock() } if err == nil { pd, _, err = t.walk(path.Dir(to), nil) } var pdpath, pdspath string if err == nil { pd.mlk.Lock() if !t.noPerms() && !pd.d.CanWrite(t.ai) { err = fmt.Errorf("%s: %s", pd, dbg.ErrPerm) } pdpath = pd.d["path"] pdspath = pd.d["spath"] pd.mlk.Unlock() } if err != nil { goto Fail } if from=="/Ctl" || to=="/Ctl" || from == "/" || to == "/" { err = dbg.ErrPerm } else if pfrom, _, ferr = t.walk(from, nil); ferr != nil { err = ferr } else if inconsistent(from, to) { err = errors.New("inconsistent move") } else if pto, leftto, terr = t.walk(to, nil); terr != nil && !dbg.IsNotExist(terr) { err = terr } else if len(leftto) > 1 { err = terr } else if len(leftto) == 0 && pto.d["type"] == "d" { err = fmt.Errorf("%s: %s", pto, dbg.ErrExists) } else if len(leftto) == 0 && pto.d["type"] != pfrom.d["type"] { // race: no lock in types err = fmt.Errorf("%s: incosistent move", pfrom) } Fail: if err != nil { c <- err t.dprintf("move %s: %s\n", from, err) close(c, err) cs.End(true) return c } t.detach(pfrom) pfrom.mlk.Lock() pfrom.name= path.Base(to) pfrom.d["name"] = pfrom.name pfrom.d["path"] = zx.Path(pdpath, pfrom.name) pfrom.d["spath"] = zx.Path(pdspath, pfrom.name) pfrom.mlk.Unlock() pfrom.moved() pd.mlk.Lock() pd.clk.Lock() pd.attach(pfrom) pd.clk.Unlock() pd.mlk.Unlock() t.dprintf("move %s: ok\n", from) close(c) cs.End(false) return c }
// Perform black box testing of dir1 // by performing random FS ops in it and a read OS dir and comparing the trees // and the results from the operations. // There can be at most one invocation of this function at a time. func AsAFs(t Fataler, dirs ...string) { dir1 := dirs[0] os.RemoveAll(osdir) defer os.RemoveAll(osdir) if err := os.Mkdir(osdir, 0775); err != nil { t.Fatalf("mkdir: %s", err) } dir1 = dir1 + "/fst" os.RemoveAll(dir1) if err := os.Mkdir(dir1, 0775); err != nil { t.Fatalf("mkdir: %s", err) } defer os.RemoveAll(dir1) opc := make(chan fsOp) nops := 0 for o := oFirst; o < oRead; o++ { n := nOps if o == oCreate || o == oMkdir { n += nOps } nops += n go func(o fsOp, n int) { for i := 0; i < n; i++ { if ok := opc <- o; !ok { break } } }(o, n) } for o := oRead; o < oMax; o++ { go func(o fsOp) { nops += nOps for i := 0; i < nOps; i++ { if ok := opc <- o; !ok { break } } }(o) } t.Logf("%d ops", nops) counts = map[fsOp]int{} for i := 0; i < nops; i++ { // Make an update operation op := <- opc if op == oRemove && i %2 == 0 { continue } fp := paths[i%len(paths)] p1 := zx.Path(dir1, fp) p2 := zx.Path(osdir, fp) ok := calls[op](t, p1, p2) if ok { counts[op] = counts[op] + 1 } } for o := oFirst; o < oMax; o++ { t.Logf("op %v: %d ok", o, counts[o]) } close(opc) }
func (f *File) files(rc chan<- *File) error { if ok := rc <- f; !ok { return cerror(rc) } for _, c := range f.Child { if err := c.files(rc); err != nil { return err } } return nil } // Enumerate all files in db func (db *DB) Files() <-chan *File { rc := make(chan *File) if db == nil || db.Root == nil { close(rc) } else { go func() { close(rc, db.Root.files(rc)) }() } return rc } // Debug dump func (db *DB) DumpTo(w io.Writer) { fmt.Fprintf(w, "%s\n", db) if db == nil { return } fc := db.Files() for f := range fc { fmt.Fprintf(w, "%s\n", f) } } // Walk to the given path func (db *DB) Walk(elems ...string) (*File, error) { f := db.Root for _, e := range elems { cf, err := f.Walk1(e) if err != nil { return nil, err } f = cf } return f, nil } type byName []*File func (b byName) Len() int { return len(b) } func (b byName) Less(i, j int) bool { return b[i].D["name"] < b[j].D["name"] } func (b byName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } // add or update the entry for a dir into db. func (db *DB) Add(d zx.Dir) error { f := &File{ D: d, } elems := zx.Elems(d["path"]) if len(elems) == 0 { db.Root = f return nil } parent := elems[0 : len(elems)-1] name := elems[len(elems)-1] pdir := zx.Path(parent...) if pdir != db.lastpdir || db.lastpf == nil { pf, err := db.Walk(parent...) if err != nil { dprintf("add: can't walk: %s\n", err) return err } db.lastpdir = pdir db.lastpf = pf } vprintf("add %s to %s\n", name, db.lastpf) child := db.lastpf.Child for _, cf := range child { if cf.D["name"] == name { cf.D = d if cf.D["type"] != "d" { cf.Child = nil } return nil } } child = append(child, f) if n := len(child); n > 1 && child[n-1].D["name"] < child[n-2].D["name"] { sort.Sort(byName(child)) } db.lastpf.Child = child return nil } // part of zx.Finder type Finder interface { Find(path, pred string, spref, dpref string, depth0 int) <-chan zx.Dir } // Scan the underlying tree and re-build the metadata db. // Dials the tree if necessary. // Only the first error is reported. func (db *DB) scan(fs Finder) error { dprintf("scan /,%s\n", db.Pred) dc := fs.Find("/", db.Pred, "", "", 0) db.lastpdir = "" db.lastpf = nil db.Root = nil var err error for d := range dc { if d["path"] == "/Ctl" || d["path"] == "/Chg" { continue } vprintf("add %s\n", d) if e := db.Add(d); err == nil && e != nil { err = nil } } if err != nil { return err } return cerror(dc) } // Send the db through c. // The channel must preserve message boundaries and is not closed by this function. // The db name is first sent and then one packed dir per file recorded in the db. // An empty msg is sent to signal the end of the stream of dir entries func (db *DB) SendTo(c chan<- []byte) error { if ok := c <- []byte(db.Name); !ok { return cerror(c) } if ok := c <- []byte(db.Pred); !ok { return cerror(c) } fc := db.Files() var err error for f := range fc { if f == nil || f.D == nil { err = errors.New("nil file sent") break } if _, err = f.D.Send(c); err != nil { break } } if err != nil { close(fc, err) } else { err = cerror(fc) } c <- []byte{} return err } // Receive a db from c assuming it was sent in the same format used by SendTo. func RecvDBFrom(c <-chan []byte) (*DB, error) { nm, ok1 := <-c pred, ok2 := <-c if !ok1 || !ok2 { return nil, cerror(c) } db := &DB{ Name: string(nm), Pred: string(pred), } db.lastpdir = "" db.lastpf = nil db.Root = nil for { d, err := zx.RecvDir(c) if err != nil || len(d) == 0 { return db, err } if d["path"] == "/Ctl" || d["path"] == "/Chg" { continue } dprintf("add %s\n", d) if err := db.Add(d); err != nil { return db, err } } } // Save a db to a local file func (db *DB) Save(fname string) error { tname := fname + "~" fd, err := os.Create(tname) if err != nil { return err } dc := make(chan []byte) go func() { close(dc, db.SendTo(dc)) }() _, _, err = nchan.WriteMsgsTo(fd, dc) fd.Close() close(dc, err) if err != nil { return err } return os.Rename(tname, fname) } func LoadDB(fname string) (*DB, error) { fd, err := os.Open(fname) if err != nil { return nil, err } dc := make(chan []byte) go func() { _, _, err := nchan.ReadMsgsFrom(fd, dc) close(dc, err) }() db, err := RecvDBFrom(dc) close(dc, err) return db, err }
func find(dump, dpref, rel string, dc chan<- zx.Dir, ufile zx.Dir) { droot := fpath.Join(dump, dpref) years, err := cmd.GetDir(droot) if err != nil { cmd.Warn("%s", err) return } for i := len(years) - 1; i >= 0; i-- { year := years[i]["name"] if ignored(year, "") { continue } ypath := years[i]["path"] days, err := cmd.GetDir(ypath) if err != nil { cmd.Warn("%s: %s", ypath, err) continue } lastsz, lastmt, lastm := "", "", "" for j := len(days) - 1; j >= 0; j-- { day := days[j]["name"] if ignored(year, day) { continue } fpath := fpath.Join(days[j]["path"], rel) d, err := cmd.Stat(fpath) if err != nil { if !force { cmd.Dprintf("find: %s", err) return } continue } newm, newsz, newmt := d["mode"], d["size"], d["mtime"] if newsz == lastsz && newmt == lastmt && newm == lastm { continue } lastm, lastsz, lastmt = newm, newsz, newmt d["upath"] = ufile["path"] d["uupath"] = ufile["upath"] if ok := dc <- d; !ok { return } if !all { return } } } } func report(dc chan zx.Dir, donec chan bool) { last := "" for d := range dc { if last == "" { last = d["Upath"] if last == "" { last = d["path"] } } p := d["path"] cmd.Dprintf("found '%s'\n", p) var err error switch { case xcmd != "": _, err = cmd.Printf("%s %s %s\n", xcmd, p, last) case dflag: dcmd := fmt.Sprintf(`9 diff -n %s %s`, p, last) _, err = cmd.Printf("%s\n", dcmd) if err != nil { cmd.Warn("diff: %s", err) continue } case lflag: _, err = cmd.Printf("%s\n", d.Fmt()) case cflag: _, err = cmd.Printf("cp %s %s\n", p, d["Upath"]) default: _, err = cmd.Printf("%s\n", d["path"]) } if err != nil { close(dc, err) } last = p } close(donec, cerror(dc)) } func hist(in <-chan face{}) error { dc := make(chan zx.Dir) ec := make(chan bool) go report(dc, ec) var sts error for m := range in { switch m := m.(type) { case zx.Dir: cmd.Dprintf("got %T %s\n", m, m["path"]) file := m["path"] if m["upath"] == "" { m["upath"] = m["path"] } ddir := dump dpref := "" rel := "" switch { case zx.HasPrefix(file, "/zx"): if ddir == "" { ddir = "/dump" } dpref = "/zx" rel = zx.Suffix(file, "/zx") case zx.HasPrefix(file, "/u/gosrc/src/clive"): if ddir == "" { ddir = "/u/dump" } dpref = "clive" rel = zx.Suffix(file, "/u/gosrc/src/clive") case zx.HasPrefix(file, "/u"): if ddir == "" { ddir = "/u/dump" } els := zx.Elems(file) if len(els) < 3 { cmd.Warn("%s: too few path elements", m["upath"]) sts = errNoDump continue } dpref = els[1] rel = zx.Path(els[2:]...) default: cmd.Warn("%s: %s", m["upath"], errNoDump) sts = errNoDump continue } find(ddir, dpref, rel, dc, m.Dup()) default: cmd.Dprintf("got %T\n", m) } } close(dc, cerror(in)) <-ec if sts == nil { sts = cerror(ec) } if sts == nil { sts = cerror(in) } return sts } // Run cnt in the current app context. func main() { c := cmd.AppCtx() cmd.UnixIO("err") opts.NewFlag("D", "debug", &c.Debug) opts.NewFlag("f", "force search past file removals", &force) opts.NewFlag("l", "produce a long listing (or print just the name)", &lflag) opts.NewFlag("c", "copy the file from the dump", &cflag) opts.NewFlag("d", "print file differences", &dflag) opts.NewFlag("x", "cmd: print lines to execute this command between versions", &xcmd) opts.NewFlag("a", "list all copies that differ, not just the last one.", &all) opts.NewFlag("p", "dumpdir: path to dump (default is /dump or /u/dump)", &dump) t := time.Now() when := t opts.NewFlag("w", "date: backward search start time (default is now)", &when) ux := false opts.NewFlag("u", "unix IO", &ux) args := opts.Parse() if (all && cflag) || (force && !all) { cmd.Warn("incompatible flags") opts.Usage() } if ux { cmd.UnixIO("out") } lastyear = "" lastday = "" if !t.Equal(when) { y := when.Year() m := when.Month() d := when.Day() if y == 0 { y = t.Year() } lastyear = fmt.Sprintf("%04d", y) lastday = fmt.Sprintf("%02d%02d", m, d) } if len(args) != 0 { cmd.SetIn("in", cmd.Dirs(args...)) } in := cmd.In("in") if err := hist(in); err != nil { cmd.Fatal(err) } }
func dumpDir(dir, name string, rf zx.File) (string, error) { dprintf("dump dir %s %s %s\n", dir, name, rf.D["path"]) ds := []zx.Dir{} istmp := rf.D["name"] == "tmp" if !istmp { var err error ds, err = zx.GetDir(rf.T, rf.D["path"]) if err != nil { dbg.Warn("dumpdir: %s: %s", rf.D["path"], err) return "", err } } else { vprintf("%s: temp %s\n", os.Args[0], name) } dhash := []string{} nds := []zx.Dir{} for _, d := range ds { if d["name"] == "" { dbg.Warn("dumpdir: %s: no name", rf.D["path"]) } if d["name"] == "NODUMP" { nds = []zx.Dir{} dbg.Warn("dumpdir: %s: no dump", rf.D["path"]) break } if d["name"] == "FROZEN" { vprintf("%s: XXX frozen %s\n", os.Args[0], name) /* dir is frozen and the FROZEN contains * the dump path for dir. */ data, err := zx.GetAll(rf.T, d["path"]) if err != nil { return "", err } s := string(data) if len(strings.Split(s, "/")) != 3 { return "", fmt.Errorf("wrong contents in %s", d["path"]) } vprintf("%s: frozen %s\n", os.Args[0], name) return strings.TrimSpace(s), nil } if excluded(d["name"]) { dprintf("dump ignored %s\n", d["path"]) continue } if t := d["type"]; t != "d" && t != "-" { dbg.Warn("dump ignored %s type '%s'\n", d["path"], t) continue } nds = append(nds, d) } ds = nds for _, d := range ds { dspath := zx.Path(name, d["name"]) df, err := zx.Walk(rf.T, rf.D, d["name"]) if dspath == name { panic("zx dump bug") } if err != nil { dbg.Warn("walk: %s: %s", df["path"], err) dhash = append(dhash, "") dhash = append(dhash, "") continue } var s string var e error switch d["type"] { case "d": s, e = dumpDir(dir, dspath, zx.File{rf.T, df}) case "-": s, e = dumpFile(dir, dspath, zx.File{rf.T, df}) default: panic("dump dir type bug") } if e == nil { dhash = append(dhash, d["name"]) dhash = append(dhash, s) } else { dbg.Warn("%s: %s", df["path"], e) } if err == nil && e != nil { err = e dhash = append(dhash, "") dhash = append(dhash, "") } } dval := strings.Join(dhash, "\n") h := sha1.New() h.Write([]byte(dval)) sum := h.Sum(nil) s := fmt.Sprintf("%02x/%02x/%036x", sum[0], sum[1], sum[2:]) dprintf("dump dir %s %s\n", name, s) dfpath := zx.Path(dir, s) fi, _ := os.Stat(dfpath) if fi != nil { return s, nil } vprintf("%s: new %s\n", os.Args[0], name) return s, newDumpDir(dir, dfpath, rf, ds, dhash) }
func (t *Lfs) get(rid string, off, count int64, dc chan<- []byte, cs *zx.CallStat) (int64, error) { rid, err := zx.AbsPath(rid) if err != nil { return 0, err } if rid == "/Ctl" { return t.getCtl(off, count, dc, cs) } path := zx.Path(t.path, rid) fd, err := os.Open(path) if err==nil && t.ai!=nil && !t.NoPermCheck { _, _, err = t.canWalkTo(rid, 0444) } if err != nil { return 0, err } defer fd.Close() st, err := fd.Stat() if err != nil { return 0, err } if off!=0 && !st.IsDir() { if _, err := fd.Seek(off, 0); err != nil { return 0, err } } if st.IsDir() { ds, err := ioutil.ReadDir(path) nd := 0 tot := 0 ctlsent := false var xds map[string]zx.Dir if t.readattrs { xds, _ = t.dirAttrs(path) } Dloop: for i := 0; i < len(ds); { if off > 0 { off-- if !ctlsent && rid=="/" { ctlsent = true } else { i++ } continue } switch count { case zx.All: break case 0: break Dloop default: count-- } if !ctlsent && rid=="/" { ctlsent = true nd++ d := ctldir n, _ := d.Send(dc) nd++ // but not i++ tot += n continue } fi := ds[i] if fi.Name() == afname { if i == len(ds)-1 { break } ds = append(ds[:i], ds[i+1:]...) fi = ds[i] } cpath := zx.Path(path, fi.Name()) n := 0 sum := "" if fi.IsDir() { n, sum = dirsz(cpath) } d := zx.NewDir(fi, n) d["path"] = zx.Path(rid, fi.Name()) d["spath"] = d["path"] d["tpath"] = t.path if t.readattrs { if xd, ok := xds[fi.Name()]; ok { for k, v := range xd { if zx.IsUpper(k) { d[k] = v } } } else { d["Uid"] = dbg.Usr d["Gid"] = dbg.Usr } } if sum != "" { d["Sum"] = sum } cs.Send(0) n, err := d.Send(dc) if err != nil { return int64(tot), err } nd++ i++ tot += n } return int64(tot), err } if count == zx.All { cs.Sending() nm, n, err := nchan.ReadBytesFrom(fd, dc) cs.Sends(nm, n) return n, err } rr := io.LimitReader(fd, count) cs.Sending() nm, n, err := nchan.ReadBytesFrom(rr, dc) cs.Sends(nm, n) return n, err }