// Return the hash for path stored in the xattrs. If the hash is out of date, // the hash is computed anew, unless `compute` is false in which case nil is // returned. func GetHash(path string, info os.FileInfo, compute bool) (mh.Multihash, error) { if info.Mode()&os.ModeSymlink != 0 { return symlinkHash(path) } hashTimeStr, err := attrs.Get(path, XattrHashTime) if err != nil { if compute { return HashFile(path, info) } else if IsNoData(err) { // ignore error return nil, nil } else { return nil, err } } hashTime, err := time.Parse(time.RFC3339Nano, string(hashTimeStr)) if err != nil { return nil, err } if hashTime != info.ModTime() { if compute { return HashFile(path, info) } else { return nil, nil } } return attrs.Get(path, XattrHash) }
func GetHashTime(path string) (time.Time, error) { hashTimeStr, err := attrs.Get(path, XattrHashTime) if err != nil { return time.Time{}, err } return time.Parse(time.RFC3339Nano, string(hashTimeStr)) }
func ConflictFile(path string) string { conflict, err := attrs.Get(path, XattrConflict) if err != nil { return "" } else { return string(conflict) } }
// Commit file to given hash, force writing xattrs if force is true. func CommitFileHash(path string, info os.FileInfo, digest []byte, force bool) (forced bool, err error) { timeData := []byte(info.ModTime().Format(time.RFC3339Nano)) hash, err := attrs.Get(path, XattrHash) if err != nil || !bytes.Equal(hash, digest) { forced, err = attrs.SetForce(path, XattrHash, digest, info, force) } else { digest = nil } hashTimeStr, err := attrs.Get(path, XattrHashTime) var hashTime time.Time if err == nil { hashTime, err = time.Parse(time.RFC3339Nano, string(hashTimeStr)) } if err != nil || hashTime != info.ModTime() { forced, err = attrs.SetForce(path, XattrHashTime, timeData, info, force) } return }
func ConflictFileAlternatives(path string) []string { var alternatives []string for i := 0; true; i++ { alt, err := attrs.Get(path, fmt.Sprintf("%s.%d", XattrConflict, i)) if err == nil { alternatives = append(alternatives, string(alt)) } else { break } } return alternatives }
func mainCheck(args []string) int { f := flag.NewFlagSet("status", flag.ExitOnError) opt_all := f.Bool("a", false, "Check all files, including modified") f.Usage = func() { fmt.Print(checkUsage) f.PrintDefaults() } f.Parse(args) dir := f.Arg(0) if dir == "" { dir = "." } err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } // Skip .dirstore/ at root if filepath.Base(path) == attrs.DirStoreName && filepath.Dir(path) == dir && info.IsDir() { return filepath.SkipDir } else if info.IsDir() { return nil } hashTimeStr, err := attrs.Get(path, repo.XattrHashTime) if err != nil { return nil } hashTime, err := time.Parse(time.RFC3339Nano, string(hashTimeStr)) if err != nil { return err } timeEqual := hashTime == info.ModTime() if *opt_all || timeEqual { hash, err := attrs.Get(path, repo.XattrHash) if err != nil { return err } digest, err := repo.HashFile(path, info) if err != nil { return err } hashEqual := bytes.Equal(hash, digest) if !timeEqual && !hashEqual { fmt.Printf("+\t%s\t%s\n", base58.Encode(digest), path) } else if !hashEqual { fmt.Printf("!\t%s\t%s\n", base58.Encode(digest), path) } else if !timeEqual { fmt.Printf("=\t%s\t%s", base58.Encode(digest), path) } } return nil }) if err != nil { fmt.Fprintf(os.Stderr, "%v", err) return 1 } return 0 }
func (act *CopyAction) Run() error { var err error if act.Link { err = os.Link(act.Src, act.Dst) if err != nil { return fmt.Errorf("link %s: %s", act.Dst, err.Error()) } } else if act.manualMode && act.srcInfo.Mode()&^(os.ModeDir /*|os.ModeSymlink*/) == 0 { // FIXME: enable symlinks stat, ok := act.srcInfo.Sys().(*syscall.Stat_t) if !ok { panic("Could not get Stat_t") } symlink := act.srcInfo.Mode()&os.ModeSymlink != 0 if act.srcInfo.IsDir() { err = os.Mkdir(act.Dst, 0700) if err != nil { return err } } else if symlink { link, err := os.Readlink(act.Src) if err != nil { return err } err = os.Symlink(link, act.Dst) if err != nil { return err } } else { f, err := os.OpenFile(act.Dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return err } defer f.Close() f0, err := os.Open(act.Src) if err != nil { return err } defer f0.Close() _, err = io.Copy(f, f0) if err != nil { return err } } err = os.Lchown(act.Dst, int(stat.Uid), int(stat.Gid)) if err != nil { log.Println(err) err = nil } if !symlink { atime := time.Unix(stat.Atim.Sec, stat.Atim.Nsec) err = os.Chtimes(act.Dst, atime, act.srcInfo.ModTime()) if err != nil { return err } err = os.Chmod(act.Dst, act.SrcMode) if err != nil { return err } // FIXME: extended attributes for symlinks // golang is missing some syscalls xattr, values, err := attrs.GetList(act.Src) if err != nil { return err } for i, attrname := range xattr { err = attrs.Set(act.Src, attrname, values[i]) if err != nil { return err } } } return nil } else { os.MkdirAll(filepath.Dir(act.Dst), 0755) // Ignore error cmd := exec.Command("/bin/cp", "-a", "--no-preserve=mode", "--reflink=auto", "-d", "-T", act.Src, act.Dst) cmd.Stderr = os.Stderr err = cmd.Run() if err != nil { return fmt.Errorf("cp %s %s: %s", act.Src, act.Dst, err.Error()) } err = os.Chmod(act.Dst, act.SrcMode) if err != nil { return err } } if act.Conflict { if act.SrcMode&os.ModeSymlink == 0 { err = repo.MarkConflictFor(act.Dst, filepath.Base(act.OriginalDst)) if err != nil { return fmt.Errorf("%s: could not mark conflict: %s", act.Dst, err.Error()) } } if act.OrigDstMode&os.ModeSymlink == 0 { err = repo.AddConflictAlternative(act.OriginalDst, filepath.Base(act.Dst)) if err != nil { return fmt.Errorf("%s: could add conflict alternative: %s", act.Dst, err.Error()) } } } if act.SrcMode&os.ModeSymlink == 0 { if act.Hash != nil { info, err := os.Lstat(act.Dst) if err != nil { return fmt.Errorf("%s: could add lstat: %s", act.Dst, err.Error()) } _, err = repo.CommitFileHash(act.Dst, info, act.Hash, false) if err != nil { return fmt.Errorf("%s: could not commit: %s", act.Dst, err.Error()) } } else { hash, err := attrs.Get(act.Src, repo.XattrHash) if err == nil { err = attrs.Set(act.Dst, repo.XattrHash, hash) if err != nil { return fmt.Errorf("%s: could add xattr %s: %s", act.Dst, repo.XattrHash, err.Error()) } } hashTime, err := attrs.Get(act.Src, repo.XattrHashTime) if err == nil { err = attrs.Set(act.Dst, repo.XattrHashTime, hashTime) if err != nil { return fmt.Errorf("%s: could add xattr %s: %s", act.Dst, repo.XattrHashTime, err.Error()) } } } } return nil }
func mainInfo(args []string) int { f := flag.NewFlagSet("info", flag.ExitOnError) opt_check := f.Bool("c", false, "Run integrity check") f.Usage = func() { fmt.Print(infoUsage) f.PrintDefaults() } f.Parse(args) dir := f.Arg(0) if dir == "" { dir = "." } rep := repo.GetRepo(dir) status := 0 first := true err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error()) status = 1 return err } // Skip .dirstore/ at root if filepath.Base(path) == attrs.DirStoreName && filepath.Dir(path) == dir && info.IsDir() { return filepath.SkipDir } else if !info.Mode().IsRegular() { return nil } if first { first = false } else { fmt.Println() } fmt.Printf("File: %s\n", path) if conflict := repo.ConflictFile(path); conflict != "" { fmt.Printf("Conflict With: %s\n", conflict) } for _, alt := range repo.ConflictFileAlternatives(path) { fmt.Printf("Conflict Alternatives: %s\n", alt) } var realHash mh.Multihash if *opt_check { realHash, err = repo.HashFile(path, info) if err != nil { fmt.Fprintf(os.Stderr, "%s: %v\n", path, err) return nil } } hashTime, err := repo.GetHashTime(path) if repo.IsNoData(err) { if *opt_check { fmt.Printf("Actual Hash: %s\n", base58.Encode(realHash)) } fmt.Printf("Status: New\n") } else { if err != nil { fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error()) return nil } fmt.Printf("Hash Time: %v\n", hashTime.Format(time.RFC3339Nano)) hash, err := attrs.Get(path, repo.XattrHash) if err != nil { fmt.Fprintf(os.Stderr, "%s: %v\n", path, err) return nil } var par2exists = false if rep != nil { par2exists, _ = rep.Par2Exists(hash) } fmt.Printf("Recorded Hash: %s (reduncency %s)\n", base58.Encode(hash), boolToAvailableStr(par2exists)) if *opt_check { par2exists = false if rep != nil { par2exists, _ = rep.Par2Exists(realHash) } fmt.Printf("Actual Hash: %s (redundency %s)\n", base58.Encode(realHash), boolToAvailableStr(par2exists)) } if hashTime != info.ModTime() { fmt.Printf("Status: Dirty\n") } else { if *opt_check && !bytes.Equal(realHash, hash) { fmt.Printf("Status: Corrupted\n") } else { fmt.Printf("Status: Clean\n") } } } return nil }) if err != nil { fmt.Fprintf(os.Stderr, "%v", err) os.Exit(1) } return status }
func mainSave(args []string) int { f := flag.NewFlagSet("save", flag.ExitOnError) opt_force := f.Bool("force", false, "Force writing xattrs on read only files") opt_nodocignore := f.Bool("no-docignore", false, "Don't respect .docignore") f.Usage = func() { fmt.Print(saveUsage) f.PrintDefaults() } f.Parse(args) dir := f.Arg(0) if dir == "" { dir = "." } dirstore := repo.GetRepo(dir) if dirstore == nil { fmt.Fprintf(os.Stderr, "%s: Could not find repository, please run doc init\n", dir) os.Exit(1) } status := 0 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error()) status = 1 return err } if !*opt_nodocignore && ignore.IsIgnored(path) { return filepath.SkipDir } // Skip .dirstore/ at root if filepath.Base(path) == attrs.DirStoreName && filepath.Dir(path) == dir && info.IsDir() { return filepath.SkipDir } else if info.IsDir() || !info.Mode().IsRegular() { return nil } hashTime, err := repo.GetHashTime(path) if err != nil && !repo.IsNoData(err) { fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error()) return nil } var digest []byte if err != nil || hashTime != info.ModTime() { digest, err = commitFile(path, info, *opt_force) if err != nil { status = 1 fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error()) } else if digest != nil { fmt.Printf("%s %s\n", base58.Encode(digest), path) } } else { digest, err = attrs.Get(path, repo.XattrHash) if err != nil { fmt.Fprintf(os.Stderr, "%s: %v\n", path, err) return nil } } err = dirstore.Create(path, digest) if err != nil { fmt.Fprintf(os.Stderr, "%s: %v\n", path, err) return nil } return nil }) if err != nil { fmt.Fprintf(os.Stderr, "%v", err) os.Exit(1) } return status }