Пример #1
0
func (fs *Fs) chkWalk(p string, mkall bool) error {
	if fs.ai == nil {
		return nil
	}
	els := zx.Elems(p)
	rp := "/"
	for len(els) > 0 {
		d, err := fs.stat(rp, false)
		rp = fpath.Join(rp, els[0])
		if err != nil {
			if !mkall || !zx.IsNotExist(err) {
				return err
			}
			path := fpath.Join(fs.root, rp)
			if e := os.Mkdir(path, 0755); e != nil {
				return err
			}
		}
		if !d.CanWalk(fs.ai) {
			return fmt.Errorf("%s: %s", d["path"], zx.ErrPerm)
		}
		if len(els) == 1 {
			return nil
		}
		els = els[1:]
	}
	return nil
}
Пример #2
0
//	exp	p	->
//	/a/b	/x	-> false, true
//	/a/b	/a	-> false, false
//	/a/b	/a/b/c	-> false, true
func pathMatch(exp, p string) (value, pruned bool, err error) {
	if len(exp) > 0 && exp[0] != '/' {
		m, err := filepath.Match(exp, p)
		return m, false, err
	}
	els := zx.Elems(exp)
	pels := zx.Elems(p)
	if len(pels) > len(els) {
		return false, true, nil
	}
	for i := 0; i < len(pels); i++ {
		m, err := filepath.Match(els[i], pels[i])
		if !m {
			return false, true, err
		}
	}
	return len(pels) == len(els), false, nil
}
Пример #3
0
// 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)
	}
}
Пример #4
0
// 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
}
Пример #5
0
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
}
Пример #6
0
func setUids(d zx.Dir) {
	if d["Uid"] == "" {
		u := dbg.Usr
		if strings.HasPrefix(d["path"], "/usr/") {
			els := zx.Elems(d["path"])
			if len(els) > 1 {
				u = els[1]
			}
		}
		d["Uid"] = u
	}
	if d["Gid"] == "" {
		d["Gid"] = d["Uid"]
	}
	if d["Wuid"] == "" {
		d["Wuid"] = d["Uid"]
	}
}
Пример #7
0
// if the error is a file not found, then file is returned with
// the parent file and left contains what's left to walk.
func (t *Fs) walk(rid string, pfp **mFile) (cf *mFile, left []string, e error) {
	els := zx.Elems(rid)
	f := t.root
	for len(els) > 0 {
		if !t.noPerms() && !f.d.CanWalk(t.ai) {
			return f, els, fmt.Errorf("%s: %s", f, dbg.ErrPerm)
		}
		if len(els) == 1 && pfp != nil {
			*pfp = f
		}
		nf, err := f.walk1(els[0])
		if err != nil {
			return f, els, err
		}
		f = nf
		els = els[1:]
	}
	return f, nil, nil
}
Пример #8
0
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)
	}
}
Пример #9
0
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
}