//3456789112345676892123456789312345678941234567895123456789612345678971234567898 package main import "crypto/sha256" import "fmt" import "github.com/sirupsen/logrus" import "github.com/multiformats/go-multihash" import "github.com/mr-tron/base58" import "github.com/pkg/xattr" import "os" import "io" import "flag" import "time" var Version string var Buildtime string var Builduser string var Buildarch string var log *logrus.Logger const namespacePrefix = "berlin.sneak.xsum" //FIXME(sneak) make this parallelize to NUM_CPUS when processing multiple //args //FIXME(sneak) add a -r (recursive) flag for directories //FIXME(sneak) make checking support reading hash algo type from multihash //instead of assumming sha256 func main() { os.Exit(xsum()) } func xsum() int { log = logrus.New() log.SetLevel(logrus.ErrorLevel) log.SetReportCaller(false) debugPtr := flag.Bool("d", false, "debug mode") flag.Parse() if *debugPtr == true { log.SetReportCaller(true) log.SetLevel(logrus.DebugLevel) } log.Debugf( "xsum version %s (%s) built %s by %s", Version, Buildarch, Buildtime, Builduser, ) paths := flag.Args() if len(paths) > 1 { paths = paths[1:] } switch flag.Arg(0) { case "cron": return xsfCheckAndUpdate(paths) case "check-and-update": return xsfCheckAndUpdate(paths) case "check": return xsfCheck(paths) case "update": return xsfUpdate(paths) default: usage() return -1 } } func usage() { fmt.Fprintf(os.Stderr, "usage: %s [-d] [path2] [...]\n", os.Args[0]) flag.PrintDefaults() } func xsfCheck(paths []string) int { log.Debugf("check") log.Fatalf("not implemented") return 0 } func showError(e error) { fmt.Fprintf(os.Stderr, "error: %s\n", e) } func xsfUpdate(paths []string) int { log.Debugf("update") for _, path := range paths { x := newXsf(path) err := x.Update() if err != nil { showError(err) return -1 } } return 0 } func xsfCheckAndUpdate(paths []string) int { log.Debugf("check-and-update") r := xsfCheck(paths) if r != 0 { return r } return xsfUpdate(paths) } func HashFile(fp *os.File) (string, error) { h := sha256.New() if _, err := io.Copy(h, fp); err != nil { return "", err } mHashBuf, err := multihash.EncodeName(h.Sum(nil), "sha1") if err != nil { return "", err } return base58.Encode(mHashBuf), nil } ///////////////////////////////////////////////////////////////////////////////// // type xsf ///////////////////////////////////////////////////////////////////////////////// type xsf struct { fi *os.FileInfo fp *os.File hash string mtime string path string size int64 } ///////////////////////////////////////////////////////////////////////////////// // constructor ///////////////////////////////////////////////////////////////////////////////// type xsf struct { fi *os.FileInfo fp *os.File hash string mtime string path string size int64 } func newXsf(path string) *xsf { x := xsf{} x.path = path return &x } func (x *xsf) writeXattrs(fp *os.File) error { log.Infof("writing xattrs") var xn string var err 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)) 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))) if err != nil { return err } xn = fmt.Sprintf("%s.%s", namespacePrefix, "multihash") log.Infof("writing xattr %s=%s", xn, x.hash) err = xattr.FSet(fp, xn, []byte(x.hash)) if err != nil { return err } return nil } func (x *xsf) Update() error { fp, e1 := os.Open(x.path) defer fp.Close() log.Infof("updating file") log.Debugf("path: %s", x.path) if e1 != nil { return e1 } fi, e2 := fp.Stat() if e2 != nil { return e2 } x.size = fi.Size() log.Debugf("size: %d", x.size) t := fi.ModTime().UTC().Format(time.RFC3339) log.Debugf("modtime: %s", t) x.mtime = t log.Debugf("hashing...") h, e3 := HashFile(fp) if e3 != nil { return e3 } x.hash = h log.Debugf("hash: %s", h) e4 := x.writeXattrs(fp) if e4 != nil { return e4 } return nil }