コード例 #1
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
}
コード例 #2
0
ファイル: status.go プロジェクト: mildred/doc
func mainStatus(args []string) int {
	f := flag.NewFlagSet("status", flag.ExitOnError)
	opt_no_par2 := f.Bool("n", false, "Do not show files missing PAR2 redundency data")
	opt_show_only_hash := f.Bool("c", false, "Show only unchanged committed files with their hash")
	opt_no_docignore := f.Bool("no-docignore", false, "Don't treat .docignore files specially")
	f.Usage = func() {
		fmt.Print(usageStatus)
		f.PrintDefaults()
	}
	f.Parse(args)
	dir := f.Arg(0)
	if dir == "" {
		dir = "."
	}

	rep := repo.GetRepo(dir)

	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
		}

		// Skip directories containing an empty .docignore file
		if !*opt_no_docignore && 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.Mode().IsRegular() {
			return nil
		}

		if *opt_show_only_hash {
			hash, err := repo.GetHash(path, info, false)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error())
				return nil
			}

			if hash != nil {
				fmt.Printf("%s\t%s\n", base58.Encode(hash), path)
			}

		} else {

			var conflict string = ""
			if repo.ConflictFile(path) != "" {
				conflict = " c"
			} else if len(repo.ConflictFileAlternatives(path)) > 0 {
				conflict = " C"
			}

			hashTime, err := repo.GetHashTime(path)
			if repo.IsNoData(err) {
				if info.Mode()&os.FileMode(0200) == 0 {
					fmt.Printf("?%s (ro)\t%s\n", conflict, path)
				} else {
					fmt.Printf("?%s\t%s\n", conflict, path)
				}
				return nil
			} else if err != nil {
				fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error())
				return nil
			}

			var redundency string = "*"
			if rep != nil {
				digest, err := repo.GetHash(path, info, true)
				if err != nil {
					fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error())
					return nil
				}
				if par2exists, _ := rep.Par2Exists(digest); par2exists {
					redundency = ""
				}
			}

			if hashTime != info.ModTime() {
				fmt.Printf("+%s%s\t%s\n", conflict, redundency, path)
			} else if conflict != "" || (redundency != "" && !*opt_no_par2) {
				fmt.Printf("%s%s\t%s\n", conflict, redundency, path)
			}
		}

		return nil
	})

	if err != nil {
		fmt.Fprintf(os.Stderr, "%v", err)
		os.Exit(1)
	}
	return status
}
コード例 #3
0
ファイル: dupes.go プロジェクト: mildred/doc
func mainDupes(args []string) int {
	f := flag.NewFlagSet("dupes", flag.ExitOnError)
	opt_show_links := f.Bool("l", false, "Show group of files that share the same inode")
	opt_progress := f.Bool("p", false, "Show progress")
	opt_hash := f.Bool("c", false, "Check real hash in case the file is updated")
	opt_dedup := f.Bool("d", false, "Deduplicate files (make links)")
	f.Usage = func() {
		fmt.Print(dupesUsage)
		f.PrintDefaults()
	}
	f.Parse(args)
	srcs := f.Args()

	if len(srcs) == 0 {
		srcs = append(srcs, ".")
	}

	dupes := map[string]sameFile{}

	num := 0
	errors := 0

	for _, src := range srcs {
		e := repo.Walk(src, func(path string, info os.FileInfo) error {
			// Skip symlinks
			if info.Mode()&os.ModeSymlink != 0 {
				return nil
			}

			hash, err := repo.GetHash(path, info, *opt_hash)
			if err != nil {
				return err
			}
			if hash == nil {
				return nil
			}

			sys, ok := info.Sys().(*syscall.Stat_t)
			if !ok {
				sys.Ino = 0
			}

			f := dupes[string(hash)]
			f.hash = hash
			f.paths = append(f.paths, path)
			f.inodes = append(f.inodes, sys.Ino)
			f.devices = append(f.devices, sys.Dev)
			dupes[string(hash)] = f

			num = num + 1
			if *opt_progress {
				fmt.Printf("\r\x1b[2K%d %s\r", num, path)
			}

			return nil
		}, func(path string, info os.FileInfo, err error) bool {
			fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error())
			return true
		})
		errors = errors + len(e)
	}

	for _, f := range dupes {
		if len(f.paths) <= 1 {
			continue
		}
		files := map[uint64][]string{}
		for i, ino := range f.inodes {
			files[ino] = append(files[ino], f.paths[i])
		}
		if len(files) == 1 && !*opt_show_links {
			continue
		}
		fmt.Println()
		hash := base58.Encode(f.hash)
		for _, paths := range files {
			for _, path := range paths {
				fmt.Printf("%s\t%d\t%s\n", hash, len(paths), path)
			}
		}
		if len(files) > 1 && *opt_dedup {
			err := deduplicate(f)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s", err.Error())
				errors = errors + 1
			}
		}
	}

	if errors > 0 {
		return 1
	}
	return 0
}