//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 "errors" import "os" import "io" import "flag" import "time" import "strconv" 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": x := xsfCheckAndUpdate(paths) if x != nil { return -1 } else { return 0 } case "check-and-update": x := xsfCheckAndUpdate(paths) if x != nil { return -1 } else { return 0 } case "check": x := xsfCheck(paths) if x != nil { return -1 } else { return 0 } case "update": x := xsfUpdate(paths) if x != nil { return -1 } else { return 0 } default: usage() return -1 } } func usage() { fmt.Fprintf(os.Stderr, "usage: %s [-d] [path2] [...]\n", os.Args[0]) flag.PrintDefaults() } func xsfCheck(paths []string) error { log.Debugf("check") log.Fatalf("not implemented") for _, path := range paths { x := newXsf(path) err := x.Check() if err != nil { return err } } return nil } func showError(e error) { fmt.Fprintf(os.Stderr, "error: %s\n", e) } func xsfUpdate(paths []string) error { log.Debugf("update") for _, path := range paths { x := newXsf(path) err := x.Update() if err != nil { showError(err) return err } } return nil } func xsfCheckAndUpdate(paths []string) error { log.Debugf("check-and-update") r := xsfCheck(paths) if r != nil { 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), "sha2-256") if err != nil { return "", err } return base58.Encode(mHashBuf), nil } ///////////////////////////////////////////////////////////////////////////////// // type xsf ///////////////////////////////////////////////////////////////////////////////// type xsf struct { fi *os.FileInfo fp *os.File multihash string xmultihash string mtime string xmtime string path string size uint64 xsize uint64 } ///////////////////////////////////////////////////////////////////////////////// // constructor ///////////////////////////////////////////////////////////////////////////////// func newXsf(path string) *xsf { x := xsf{} x.path = path return &x } func (x *xsf) readXattrs(fp *os.File) error { log.Infof("reading xattrs") var xn string var err error var v []byte xn = fmt.Sprintf("%s.%s", namespacePrefix, "mtime") v, err = xattr.FGet(x.fp, xn) if err != nil { return err } x.xmtime = string(v) xn = fmt.Sprintf("%s.%s", namespacePrefix, "size") v, err = xattr.FGet(x.fp, xn) if err != nil { return err } a, b := strconv.ParseInt(string(v), 10, 64) if b != nil { return b } 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 } 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.multihash) err = xattr.FSet(fp, xn, []byte(x.multihash)) if err != nil { return err } return nil } func (x *xsf) stat(fp *os.File) error { fi, err := fp.Stat() if err != nil { return err } x.size = uint64(fi.Size()) log.Debugf("size: %d", x.size) t := fi.ModTime().UTC().Format(time.RFC3339) log.Debugf("modtime: %s", t) x.mtime = t return nil } func (x *xsf) hash(fp *os.File) error { log.Debugf("hashing...") var err error if x.multihash, err = HashFile(fp); err != nil { return err } log.Debugf("hash: %s", x.multihash) return nil } func (x *xsf) Check() error { fp, err := os.Open(x.path) defer fp.Close() if err != nil { return err } x.stat(x.fp) return nil } func (x *xsf) Update() error { fp, err := os.Open(x.path) defer fp.Close() log.Debugf("updating file (path: %s)", x.path) if err != nil { return err } //FIXME check if size/mtime are defined first //and skip update if match (and key 'multihash' exists) if err = x.stat(fp); err != nil { return err } if err = x.hash(fp); err != nil { return err } if err = x.writeXattrs(fp); err != nil { return err } return nil }