// Creates a directory dst from the information found in src // First error is fatal, other errors are issues replicating attributes func MkdirFrom(src, dst string) (error, []error) { var errs []error src_st, err := os.Stat(src) if err != nil { return err, nil } err = os.Mkdir(dst, src_st.Mode()) if err != nil && !os.IsExist(err) { return err, nil } if stat, ok := src_st.Sys().(*syscall.Stat_t); ok { err = os.Lchown(dst, int(stat.Uid), int(stat.Gid)) if err != nil { errs = append(errs, err) } atime := time.Unix(stat.Atim.Sec, stat.Atim.Nsec) err = os.Chtimes(dst, atime, src_st.ModTime()) if err != nil { errs = append(errs, err) } } xattr, values, err := attrs.GetList(src) if err != nil { errs = append(errs, err) } for i, attrname := range xattr { err = attrs.Set(src, attrname, values[i]) if err != nil { errs = append(errs, err) } } return nil, errs }
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 CopyFileTemp(src, dst string) (string, error, []error) { var errs []error src_st, err := os.Lstat(src) if err != nil { return "", err, nil } symlink := src_st.Mode()&os.ModeSymlink != 0 src_f, err := os.Open(src) if err != nil { return "", err, nil } f, err := ioutil.TempFile(filepath.Dir(dst), "temp") if err != nil { return "", err, nil } fname := f.Name() if symlink { f.Close() err = os.Remove(fname) if err != nil { return "", err, nil } target, err := os.Readlink(src) if err != nil { return "", err, nil } err = os.Symlink(target, fname) if err != nil { return "", err, nil } } else { defer f.Close() _, err = io.Copy(f, src_f) if err != nil { if e := os.Remove(fname); e != nil { errs = append(errs, e) } return "", err, errs } } if stat, ok := src_st.Sys().(*syscall.Stat_t); ok { err = os.Lchown(fname, int(stat.Uid), int(stat.Gid)) if err != nil { errs = append(errs, err) } if !symlink { atime := time.Unix(stat.Atim.Sec, stat.Atim.Nsec) err = os.Chtimes(fname, atime, src_st.ModTime()) if err != nil { errs = append(errs, err) } } } if !symlink { err = os.Chmod(fname, src_st.Mode()) if err != nil { errs = append(errs, err) } // FIXME: extended attributes for symlinks // golang is missing some syscalls xattr, values, err := attrs.GetList(src) if err != nil { errs = append(errs, err) } else { for i, attrname := range xattr { err = attrs.Set(fname, attrname, values[i]) if err != nil { errs = append(errs, err) } } } } return fname, err, errs }