예제 #1
0
파일: hash.go 프로젝트: mildred/doc
// Return the hash for path stored in the xattrs. If the hash is out of date,
// the hash is computed anew, unless `compute` is false in which case nil is
// returned.
func GetHash(path string, info os.FileInfo, compute bool) (mh.Multihash, error) {
	if info.Mode()&os.ModeSymlink != 0 {
		return symlinkHash(path)
	}

	hashTimeStr, err := attrs.Get(path, XattrHashTime)
	if err != nil {
		if compute {
			return HashFile(path, info)
		} else if IsNoData(err) {
			// ignore error
			return nil, nil
		} else {
			return nil, err
		}
	}

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

	if hashTime != info.ModTime() {
		if compute {
			return HashFile(path, info)
		} else {
			return nil, nil
		}
	}

	return attrs.Get(path, XattrHash)
}
예제 #2
0
파일: hash.go 프로젝트: mildred/doc
func GetHashTime(path string) (time.Time, error) {
	hashTimeStr, err := attrs.Get(path, XattrHashTime)
	if err != nil {
		return time.Time{}, err
	}
	return time.Parse(time.RFC3339Nano, string(hashTimeStr))
}
예제 #3
0
파일: conflicts.go 프로젝트: mildred/doc
func ConflictFile(path string) string {
	conflict, err := attrs.Get(path, XattrConflict)
	if err != nil {
		return ""
	} else {
		return string(conflict)
	}
}
예제 #4
0
파일: hash.go 프로젝트: mildred/doc
// Commit file to given hash, force writing xattrs if force is true.
func CommitFileHash(path string, info os.FileInfo, digest []byte, force bool) (forced bool, err error) {
	timeData := []byte(info.ModTime().Format(time.RFC3339Nano))

	hash, err := attrs.Get(path, XattrHash)
	if err != nil || !bytes.Equal(hash, digest) {
		forced, err = attrs.SetForce(path, XattrHash, digest, info, force)
	} else {
		digest = nil
	}

	hashTimeStr, err := attrs.Get(path, XattrHashTime)
	var hashTime time.Time
	if err == nil {
		hashTime, err = time.Parse(time.RFC3339Nano, string(hashTimeStr))
	}
	if err != nil || hashTime != info.ModTime() {
		forced, err = attrs.SetForce(path, XattrHashTime, timeData, info, force)
	}

	return
}
예제 #5
0
파일: conflicts.go 프로젝트: mildred/doc
func ConflictFileAlternatives(path string) []string {
	var alternatives []string
	for i := 0; true; i++ {
		alt, err := attrs.Get(path, fmt.Sprintf("%s.%d", XattrConflict, i))
		if err == nil {
			alternatives = append(alternatives, string(alt))
		} else {
			break
		}
	}
	return alternatives
}
예제 #6
0
파일: check.go 프로젝트: mildred/doc
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
}
예제 #7
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
}
예제 #8
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
}
예제 #9
0
파일: save.go 프로젝트: mildred/doc
func mainSave(args []string) int {
	f := flag.NewFlagSet("save", flag.ExitOnError)
	opt_force := f.Bool("force", false, "Force writing xattrs on read only files")
	opt_nodocignore := f.Bool("no-docignore", false, "Don't respect .docignore")
	f.Usage = func() {
		fmt.Print(saveUsage)
		f.PrintDefaults()
	}
	f.Parse(args)
	dir := f.Arg(0)
	if dir == "" {
		dir = "."
	}

	dirstore := repo.GetRepo(dir)
	if dirstore == nil {
		fmt.Fprintf(os.Stderr, "%s: Could not find repository, please run doc init\n", dir)
		os.Exit(1)
	}

	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
		}

		if !*opt_nodocignore && 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.IsDir() || !info.Mode().IsRegular() {
			return nil
		}

		hashTime, err := repo.GetHashTime(path)
		if err != nil && !repo.IsNoData(err) {
			fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error())
			return nil
		}

		var digest []byte

		if err != nil || hashTime != info.ModTime() {
			digest, err = commitFile(path, info, *opt_force)
			if err != nil {
				status = 1
				fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error())
			} else if digest != nil {
				fmt.Printf("%s %s\n", base58.Encode(digest), path)
			}
		} else {
			digest, err = attrs.Get(path, repo.XattrHash)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
				return nil
			}
		}

		err = dirstore.Create(path, digest)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
			return nil
		}

		return nil
	})

	if err != nil {
		fmt.Fprintf(os.Stderr, "%v", err)
		os.Exit(1)
	}

	return status
}