예제 #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
파일: info.go 프로젝트: mildred/doc
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
}