func commitDircommit(newpath string, digest []byte) error { // Set the XattrCommit err := attrs.Set(newpath, XattrCommit, digest) if err != nil { return err } // Commit the file to its extended attributes info, err := os.Stat(newpath) if err != nil { return err } _, err = repo.CommitFileHash(newpath, info, digest, false) if err != nil { return err } return nil }
func (p *FilePreparator) prepareCopy(src, dst string) bool { var err error if p.Logger != nil { p.Logger(p, src, dst, false, false) } p.NumFiles += 1 srci, srcerr := os.Lstat(src) dsti, dsterr := os.Lstat(dst) // // File in source but not in destination // if os.IsNotExist(dsterr) && srcerr == nil { if srci.IsDir() { if p.DocIgnore && ignore.IsIgnored(src) { if p.Verbose { fmt.Printf("Ignoring %s\n", src) } return true } res := p.HandleAction(*NewCreateDir(src, dst, srci)) if !res { return false } f, err := os.Open(src) if err != nil { return p.HandleError(err) } defer f.Close() names, err := f.Readdirnames(-1) if err != nil { return p.HandleError(err) } for _, name := range names { if !p.prepareCopy(filepath.Join(src, name), filepath.Join(dst, name)) { return false } } return true } else { var srchash []byte if !srci.IsDir() { srchash, err = repo.GetHash(src, srci, p.Dedup != nil) if err != nil { return p.HandleError(err) } } res := p.HandleAction(*NewCopyFile(src, dst, srchash, srci)) p.TotalBytes += uint64(size(srci)) return res } } // // [Bidir] File in destination but not in source: reverse copy // if p.Bidir && os.IsNotExist(srcerr) && dsterr == nil { // FIXME: this could probably be simplified into // return p.prepareCopy(dst, src) if dsti.IsDir() { if p.DocIgnore && ignore.IsIgnored(dst) { if p.Verbose { fmt.Printf("Ignoring %s\n", dst) } return true } res := p.HandleAction(*NewCreateDir(dst, src, dsti)) if !res { return false } f, err := os.Open(dst) if err != nil { return p.HandleError(err) } defer f.Close() names, err := f.Readdirnames(-1) if err != nil { return p.HandleError(err) } for _, name := range names { if !p.prepareCopy(filepath.Join(src, name), filepath.Join(dst, name)) { return false } } return true } else { var dsthash []byte if !dsti.IsDir() { dsthash, err = repo.GetHash(dst, dsti, p.Dedup != nil) if err != nil { return p.HandleError(err) } } res := p.HandleAction(*NewCopyAction(dst, src, dsthash, size(dsti), "", false, dsti.Mode(), 0)) p.TotalBytes += uint64(size(dsti)) return res } } // // [Dedup] File in destination but not in source: register in dedup // if p.Dedup != nil && os.IsNotExist(srcerr) && dsterr == nil { hash, err := repo.GetHash(dst, dsti, p.CheckHash) if err != nil { if !p.HandleError(err) { return false } } else if hash != nil { p.Dedup[string(hash)] = append(p.Dedup[string(hash)], dst) } } // // Handle stat() errors // if srcerr != nil { return p.HandleError(srcerr) } if dsterr != nil { return p.HandleError(dsterr) } // // Both source and destination are directories, merge // if srci.IsDir() && dsti.IsDir() { if p.DocIgnore && (ignore.IsIgnored(src) || ignore.IsIgnored(dst)) { if p.Verbose { fmt.Printf("Ignoring %s (source and destination)\n", src) } return true } var srcnames map[string]bool if p.Bidir { srcnames = map[string]bool{} } f, err := os.Open(src) if err != nil { return p.HandleError(err) } defer f.Close() names, err := f.Readdirnames(-1) if err != nil { return p.HandleError(err) } for _, name := range names { if p.Bidir { srcnames[name] = true } if !p.prepareCopy(filepath.Join(src, name), filepath.Join(dst, name)) { return false } } if p.Bidir { f, err := os.Open(dst) if err != nil { return p.HandleError(err) } defer f.Close() dstnames, err := f.Readdirnames(-1) if err != nil { return p.HandleError(err) } for _, name := range dstnames { if srcnames[name] { continue } if !p.prepareCopy(filepath.Join(src, name), filepath.Join(dst, name)) { return false } } } return true } // // Source and destination are regular files // If hash is different, there is a conflict // var srch, dsth []byte if !srci.IsDir() { srch, err = repo.GetHash(src, srci, false) computed := false if err == nil && srch == nil { if p.Logger != nil { p.Logger(p, src, dst, true, false) } srch, err = repo.HashFile(src, srci) computed = true } if err == nil && computed && p.Commit { _, err = repo.CommitFileHash(src, srci, srch, false) } if err != nil { return p.HandleError(err) } } if !dsti.IsDir() { dsth, err = repo.GetHash(dst, dsti, false) computed := false if err == nil && dsth == nil { if p.Logger != nil { p.Logger(p, src, dst, false, true) } dsth, err = repo.HashFile(dst, dsti) computed = true } if err == nil && computed && p.Commit { _, err = repo.CommitFileHash(dst, dsti, dsth, false) } if err != nil { return p.HandleError(err) } } if bytes.Equal(srch, dsth) && srci.Mode()&os.ModeSymlink == dsti.Mode()&os.ModeSymlink { return true } if repo.ConflictFile(src) == "" { dstname := repo.FindConflictFileName(dst, srch) if dstname != "" { p.TotalBytes += uint64(size(srci)) if !p.HandleAction(*NewCopyAction(src, dstname, srch, size(srci), dst, true, srci.Mode(), dsti.Mode())) { return false } } } if p.Bidir && repo.ConflictFile(dst) == "" { srcname := repo.FindConflictFileName(src, dsth) if srcname != "" { p.TotalBytes += uint64(size(dsti)) if !p.HandleAction(*NewCopyAction(dst, srcname, dsth, size(dsti), src, true, dsti.Mode(), srci.Mode())) { return false } } } return true }
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 }