Пример #1
0
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
func mainCheck(args []string) int {
	f := flag.NewFlagSet("status", flag.ExitOnError)
	opt_all := f.Bool("a", false, "Check all files, including modified")
	f.Usage = func() {
		fmt.Print(checkUsage)
		f.PrintDefaults()
	}
	f.Parse(args)
	dir := f.Arg(0)
	if dir == "" {
		dir = "."
	}

	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// Skip .dirstore/ at root
		if filepath.Base(path) == attrs.DirStoreName && filepath.Dir(path) == dir && info.IsDir() {
			return filepath.SkipDir
		} else if info.IsDir() {
			return nil
		}

		hashTimeStr, err := attrs.Get(path, repo.XattrHashTime)
		if err != nil {
			return nil
		}

		hashTime, err := time.Parse(time.RFC3339Nano, string(hashTimeStr))
		if err != nil {
			return err
		}

		timeEqual := hashTime == info.ModTime()
		if *opt_all || timeEqual {

			hash, err := attrs.Get(path, repo.XattrHash)
			if err != nil {
				return err
			}

			digest, err := repo.HashFile(path, info)
			if err != nil {
				return err
			}

			hashEqual := bytes.Equal(hash, digest)

			if !timeEqual && !hashEqual {
				fmt.Printf("+\t%s\t%s\n", base58.Encode(digest), path)
			} else if !hashEqual {
				fmt.Printf("!\t%s\t%s\n", base58.Encode(digest), path)
			} else if !timeEqual {
				fmt.Printf("=\t%s\t%s", base58.Encode(digest), path)
			}
		}

		return nil
	})

	if err != nil {
		fmt.Fprintf(os.Stderr, "%v", err)
		return 1
	}
	return 0
}
Пример #3
0
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
}