diff --git a/main.go b/main.go index 71a8abd..eb53a76 100644 --- a/main.go +++ b/main.go @@ -63,6 +63,7 @@ func xsum() int { case "cron": x := xsfCheckAndUpdate(paths) if x != nil { + log.Error(x) return -1 } else { return 0 @@ -70,6 +71,7 @@ func xsum() int { case "check-and-update": x := xsfCheckAndUpdate(paths) if x != nil { + log.Error(x) return -1 } else { return 0 @@ -77,6 +79,7 @@ func xsum() int { case "check": x := xsfCheck(paths) if x != nil { + log.Error(x) return -1 } else { return 0 @@ -84,6 +87,7 @@ func xsum() int { case "update": x := xsfUpdate(paths) if x != nil { + log.Error(x) return -1 } else { return 0 @@ -101,7 +105,6 @@ func usage() { func xsfCheck(paths []string) error { log.Debugf("check") - log.Fatalf("not implemented") for _, path := range paths { x := newXsf(path) err := x.Check() @@ -154,6 +157,15 @@ func HashFile(fp *os.File) (string, error) { return base58.Encode(mHashBuf), nil } +func stringInSlice(a string, list []string) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} + ///////////////////////////////////////////////////////////////////////////////// // type xsf ///////////////////////////////////////////////////////////////////////////////// @@ -180,21 +192,63 @@ func newXsf(path string) *xsf { return &x } -func (x *xsf) readXattrs(fp *os.File) error { - log.Infof("reading xattrs") - var xn string - var err error - var v []byte +//FIXME calling .List() three times might be slow, memoize if necessary - xn = fmt.Sprintf("%s.%s", namespacePrefix, "mtime") - v, err = xattr.FGet(x.fp, xn) +func (x *xsf) hasMtimeXattr() bool { + xn := fmt.Sprintf("%s.%s", namespacePrefix, "mtime") + l, err := xattr.FList(x.fp) + if err != nil { + return false + } + return stringInSlice(xn, l) +} + +func (x *xsf) readMtimeXattr() error { + log.Infof("reading mtime xattr") + xn := fmt.Sprintf("%s.%s", namespacePrefix, "mtime") + + v, err := xattr.FGet(x.fp, xn) if err != nil { return err } x.xmtime = string(v) + return nil +} - xn = fmt.Sprintf("%s.%s", namespacePrefix, "size") - v, err = xattr.FGet(x.fp, xn) +func (x *xsf) hasMultihashXattr() bool { + xn := fmt.Sprintf("%s.%s", namespacePrefix, "multihash") + l, err := xattr.FList(x.fp) + if err != nil { + return false + } + return stringInSlice(xn, l) +} + +func (x *xsf) readMultihashXattr() error { + log.Infof("reading multihash xattr") + xn := fmt.Sprintf("%s.%s", namespacePrefix, "multihash") + v, err := xattr.FGet(x.fp, xn) + + if err != nil { + return err + } + x.xmultihash = string(v) + return nil +} + +func (x *xsf) hasSizeXattr() bool { + xn := fmt.Sprintf("%s.%s", namespacePrefix, "size") + l, err := xattr.FList(x.fp) + if err != nil { + return false + } + return stringInSlice(xn, l) +} + +func (x *xsf) readSizeXattr() error { + log.Infof("reading size xattr") + xn := fmt.Sprintf("%s.%s", namespacePrefix, "size") + v, err := xattr.FGet(x.fp, xn) if err != nil { return err } @@ -205,19 +259,10 @@ func (x *xsf) readXattrs(fp *os.File) error { } x.xsize = uint64(a) - - xn = fmt.Sprintf("%s.%s", namespacePrefix, "multihash") - v, err = xattr.FGet(x.fp, xn) - - if err != nil { - return err - } - x.xmultihash = string(v) - - return nil //FIXME + return nil } -func (x *xsf) writeXattrs(fp *os.File) error { +func (x *xsf) writeXattrs() error { log.Infof("writing xattrs") var xn string @@ -225,21 +270,21 @@ func (x *xsf) writeXattrs(fp *os.File) error { xn = fmt.Sprintf("%s.%s", namespacePrefix, "mtime") log.Infof("writing xattr %s=%s", xn, x.mtime) - err = xattr.FSet(fp, xn, []byte(x.mtime)) + err = xattr.FSet(x.fp, xn, []byte(x.mtime)) if err != nil { return err } xn = fmt.Sprintf("%s.%s", namespacePrefix, "size") log.Infof("writing xattr %s=%s", xn, fmt.Sprintf("%d", x.size)) - err = xattr.FSet(fp, xn, []byte(fmt.Sprintf("%d", x.size))) + err = xattr.FSet(x.fp, xn, []byte(fmt.Sprintf("%d", x.size))) if err != nil { return err } xn = fmt.Sprintf("%s.%s", namespacePrefix, "multihash") log.Infof("writing xattr %s=%s", xn, x.multihash) - err = xattr.FSet(fp, xn, []byte(x.multihash)) + err = xattr.FSet(x.fp, xn, []byte(x.multihash)) if err != nil { return err } @@ -247,8 +292,8 @@ func (x *xsf) writeXattrs(fp *os.File) error { return nil } -func (x *xsf) stat(fp *os.File) error { - fi, err := fp.Stat() +func (x *xsf) stat() error { + fi, err := x.fp.Stat() if err != nil { return err } @@ -260,10 +305,10 @@ func (x *xsf) stat(fp *os.File) error { return nil } -func (x *xsf) hash(fp *os.File) error { +func (x *xsf) hash() error { log.Debugf("hashing...") var err error - if x.multihash, err = HashFile(fp); err != nil { + if x.multihash, err = HashFile(x.fp); err != nil { return err } log.Debugf("hash: %s", x.multihash) @@ -276,14 +321,100 @@ func (x *xsf) Check() error { if err != nil { return err } - x.stat(x.fp) - return nil + x.fp = fp + + serr := x.stat() + if serr != nil { + log.Errorf("error stat(): %s", serr) + return serr + } + + if x.missingXattrs() == true { + log.Infof("can't check file %s, does not have appropriate xattrs", x.path) + return nil + } + + //check to see if file needs update (wrong mtime, wrong size) + if x.needsUpdate() == true { + log.Infof("can't check file %s, needs update (xattrs not current)", x.path) + return nil + } + + //finally hash the file + err2 := x.readMultihashXattr() + if err2 != nil { + log.Errorf("error reading file hash: %s", err2) + return err2 + } + + predictedHash := x.xmultihash + + err3 := x.hash() + if err3 != nil { + log.Errorf("error hashing file: %s", err2) + return err3 + } + + actualHash := x.multihash + + if predictedHash != actualHash { + log.Errorf("file corruption detected: expected=%s actual=%s", predictedHash, actualHash) + return err + } else { + log.Infof("file OK hash=%s", actualHash) + return nil + } +} + +func (x *xsf) missingXattrs() bool { + if x.hasMtimeXattr() == false { + log.Debugf("file needs update, missing mtime xattr") + return true + } + + if x.hasMultihashXattr() == false { + log.Debugf("file needs update, missing multihash xattr") + return true + } + + if x.hasSizeXattr() == false { + log.Debugf("file needs update, missing size xattr") + return true + } + + return false +} + +func (x *xsf) needsUpdate() bool { + // this expects stat() to have been called on the xsf + // by Update already, so we have x.mtime et al populated from the + // filesystem + + // if the file doesn't have all 3 xattrs, it needs an update. + if x.missingXattrs() == false { + return true + } + + // if the size doesn't match, it needs an update + if x.size != x.xsize { + log.Debugf("file needs update, size is %s, xattr size is %s", x.size, x.xsize) + return true + } + + // if the mtime is not the same, it needs an update + if x.mtime != x.xmtime { + log.Debugf("file needs update, mtime is %s, xattr mtime is %s", x.mtime, x.xmtime) + return true + } + + return false } func (x *xsf) Update() error { - fp, err := os.Open(x.path) - defer fp.Close() log.Debugf("updating file (path: %s)", x.path) + fp, err := os.Open(x.path) + x.fp = fp + defer fp.Close() if err != nil { return err } @@ -291,15 +422,15 @@ func (x *xsf) Update() error { //FIXME check if size/mtime are defined first //and skip update if match (and key 'multihash' exists) - if err = x.stat(fp); err != nil { + if err = x.stat(); err != nil { return err } - if err = x.hash(fp); err != nil { + if err = x.hash(); err != nil { return err } - if err = x.writeXattrs(fp); err != nil { + if err = x.writeXattrs(); err != nil { return err }