예제 #1
0
파일: commit.go 프로젝트: mildred/doc
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
}
예제 #2
0
파일: scan.go 프로젝트: mildred/doc
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
}
예제 #3
0
파일: action.go 프로젝트: mildred/doc
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
}