Ejemplo n.º 1
0
//_log returns all of the commits the repo which have generation >= from.
func _log(repo, from string, order int, cont func(io.Reader) error) error {
	var sort string
	if order == Desc {
		sort = "-ogen"
	} else {
		sort = "+ogen"
	}

	if from == "" {
		reader, err := executil.RunStdout("btrfs", "subvolume", "list", "-o", "-c", "-u", "-q", "--sort", sort, FilePath(path.Join(repo)))
		if err != nil {
			return err
		}
		return cont(reader)
	}
	t, err := transid(repo, from)
	if err != nil {
		return err
	}
	reader, err := executil.RunStdout("btrfs", "subvolume", "list", "-o", "-c", "-u", "-q", "-C", "+"+t, "--sort", sort, FilePath(path.Join(repo)))
	if err != nil {
		return err
	}
	return cont(reader)
}
Ejemplo n.º 2
0
func execSend(path string, parent string, diff io.Writer) (retErr error) {
	defer func() {
		protolog.Info(&Send{path, parent, errorToString(retErr)})
	}()
	if parent == "" {
		return executil.RunStdout(diff, "btrfs", "send", path)
	}
	return executil.RunStdout(diff, "btrfs", "send", "-p", parent, path)
}
Ejemplo n.º 3
0
func execSubvolumeFindNew(commit string, fromCommit string, out io.Writer) (retErr error) {
	defer func() {
		protolog.Info(&SubvolumeFindNew{commit, fromCommit, errorToString(retErr)})
	}()
	if fromCommit == "" {
		return executil.RunStdout(out, "btrfs", "subvolume", "find-new", commit, "0")
	}
	transid, err := execTransID(fromCommit)
	if err != nil {
		return err
	}
	return executil.RunStdout(out, "btrfs", "subvolume", "find-new", commit, transid)
}
Ejemplo n.º 4
0
// transid returns transid of a path in a repo. This function is used in
// several other internal functions.
func transid(repo, commit string) (string, error) {
	if commit == "" {
		return "0", nil
	}
	//  "9223372036854775810" == 2 ** 63 we use a very big number there so that
	//  we get the transid of the from path. According to the internet this is
	//  the nicest way to get it from btrfs.
	var transid string
	reader, err := executil.RunStdout("btrfs", "subvolume", "find-new", FilePath(path.Join(repo, commit)), "9223372036854775808")
	if err != nil {
		return "", err
	}
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		// scanner.Text() looks like this:
		// transid marker was 907
		// 0       1      2   3
		tokens := strings.Split(scanner.Text(), " ")
		if len(tokens) != 4 {
			return "", errors.New("Failed to parse find-new output.")
		}
		// We want to increment the transid because it's inclusive, if we
		// don't increment we'll get things from the previous commit as
		// well.
		transid = tokens[3]
	}
	return transid, scanner.Err()
}
Ejemplo n.º 5
0
// FindNew returns an array of filenames that were created or modified between `from` and `to`
func FindNew(repo, from, to string) ([]string, error) {
	var files []string
	t, err := transid(repo, from)
	if err != nil {
		return files, err
	}
	reader, err := executil.RunStdout("btrfs", "subvolume", "find-new", FilePath(path.Join(repo, to)), t)
	if err != nil {
		return files, err
	}
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		log.Print(scanner.Text())
		// scanner.Text() looks like this:
		// inode 6683 file offset 0 len 107 disk start 0 offset 0 gen 909 flags INLINE jobs/rPqZxsaspy
		// 0     1    2    3      4 5   6   7    8     9 10     11 12 13 14     15     16
		tokens := strings.Split(scanner.Text(), " ")
		// Make sure the line is parseable as a file and the path isn't hidden.
		if len(tokens) == 17 {
			if !strings.HasPrefix(tokens[16], ".") { // check if it's a hidden file
				files = append(files, tokens[16])
			}
		} else if len(tokens) == 4 {
			continue //skip transid messages
		} else {
			return files, errors.New("Failed to parse find-new output.")
		}
	}
	return files, scanner.Err()
}
Ejemplo n.º 6
0
func execTransID(path string) (string, error) {
	//  "9223372036854775810" == 2 ** 63 we use a very big number there so that
	//  we get the transid of the from path. According to the internet this is
	//  the nicest way to get it from btrfs.
	var buffer bytes.Buffer
	if err := executil.RunStdout(&buffer, "btrfs", "subvolume", "find-new", path, "9223372036854775808"); err != nil {
		return "", err
	}
	scanner := bufio.NewScanner(&buffer)
	for scanner.Scan() {
		// scanner.Text() looks like this:
		// transid marker was 907
		// 0       1      2   3
		tokens := strings.Split(scanner.Text(), " ")
		if len(tokens) != 4 {
			return "", fmt.Errorf("pachyderm: failed to parse find-new output")
		}
		// We want to increment the transid because it's inclusive, if we
		// don't increment we'll get things from the previous commit as
		// well.
		return tokens[3], nil
	}
	if scanner.Err() != nil {
		return "", scanner.Err()
	}
	return "", fmt.Errorf("pachyderm: empty output from find-new")
}
Ejemplo n.º 7
0
func execTransID(path string) (result string, retErr error) {
	defer func() {
		protolog.Info(&TransID{path, result, errorToString(retErr)})
	}()
	//  "9223372036854775810" == 2 ** 63 we use a very big number there so that
	//  we get the transid of the from path. According to the internet this is
	//  the nicest way to get it from btrfs.
	var buffer bytes.Buffer
	if err := executil.RunStdout(&buffer, "btrfs", "subvolume", "find-new", path, "9223372036854775808"); err != nil {
		return "", err
	}
	scanner := bufio.NewScanner(&buffer)
	for scanner.Scan() {
		// scanner.Text() looks like this:
		// transid marker was 907
		// 0       1      2   3
		tokens := strings.Split(scanner.Text(), " ")
		if len(tokens) != 4 {
			return "", fmt.Errorf("pachyderm: failed to parse find-new output")
		}
		return tokens[3], nil
	}
	if scanner.Err() != nil {
		return "", scanner.Err()
	}
	return "", fmt.Errorf("pachyderm: empty output from find-new")
}
Ejemplo n.º 8
0
func execSubvolumeList(path string, fromCommit string, ascending bool, out io.Writer) error {
	var sort string
	if ascending {
		sort = "+ogen"
	} else {
		sort = "-ogen"
	}

	if fromCommit == "" {
		return executil.RunStdout(out, "btrfs", "subvolume", "list", "-a", "--sort", sort, path)
	}
	transid, err := execTransID(fromCommit)
	if err != nil {
		return err
	}
	return executil.RunStdout(out, "btrfs", "subvolume", "list", "-aC", "+"+transid, "--sort", sort, path)
}
Ejemplo n.º 9
0
func execSubvolumeList(path string, fromCommit string, ascending bool, out io.Writer) (retErr error) {
	defer func() {
		protolog.Info(&SubvolumeList{path, fromCommit, ascending, errorToString(retErr)})
	}()
	var sort string
	if ascending {
		sort = "+ogen"
	} else {
		sort = "-ogen"
	}

	if fromCommit == "" {
		return executil.RunStdout(out, "btrfs", "subvolume", "list", "-a", "--sort", sort, path)
	}
	transid, err := execTransID(fromCommit)
	if err != nil {
		return err
	}
	return executil.RunStdout(out, "btrfs", "subvolume", "list", "-aC", "+"+transid, "--sort", sort, path)
}
Ejemplo n.º 10
0
// IsCommit returns true if the volume is a commit and false if it's a branch.
func IsCommit(name string) (bool, error) {
	// "-t s" indicates to btrfs that this is a subvolume without the "-t s"
	// btrfs will still output what we want, but it will have a nonzero return
	// code
	reader, err := executil.RunStdout("btrfs", "property", "get", "-t", "s", FilePath(name))
	if err != nil {
		return false, err
	}
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		if strings.Contains(scanner.Text(), "ro=true") {
			return true, nil
		}
	}
	return false, scanner.Err()
}
Ejemplo n.º 11
0
func checkVersion() {
	reader, err := executil.RunStdout("btrfs", "--version")
	if err != nil {
		checkVersionErr = err
		return
	}
	data, err := ioutil.ReadAll(reader)
	if err != nil {
		checkVersionErr = err
		return
	}
	versionString := strings.TrimSpace(string(data))
	splittedVersion := strings.Split(versionString, " v")
	if len(splittedVersion) != 2 {
		checkVersionErr = fmt.Errorf("unknown version string: %s", versionString)
		return
	}
	split := strings.Split(splittedVersion[1], ".")
	versionNumbers := len(split)
	if versionNumbers != 2 && versionNumbers != 3 {
		checkVersionErr = fmt.Errorf("unknown version string: %s", versionString)
		return
	}
	major, err := strconv.ParseInt(split[0], 10, 64)
	if err != nil {
		checkVersionErr = err
		return
	}
	if major < majorVersion {
		checkVersionErr = fmt.Errorf("need at least btrfs version %d.%d, got %s", majorVersion, minorVersion, splittedVersion[1])
		return
	} else if major == majorVersion {
		minor, err := strconv.ParseInt(split[1], 10, 64)
		if err != nil {
			checkVersionErr = err
			return
		}
		if minor < minorVersion {
			checkVersionErr = fmt.Errorf("need at least btrfs version %d.%d, got %s", majorVersion, minorVersion, splittedVersion[1])
			return
		}
	}
}
Ejemplo n.º 12
0
func execPropertyGetReadonly(path string) (bool, error) {
	reader, err := executil.RunStdout("btrfs", "property", "get", path)
	if err != nil {
		return false, err
	}
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		text := scanner.Text()
		if strings.Contains(text, "ro=true") {
			return true, nil
		}
		if strings.Contains(text, "ro=false") {
			return false, nil
		}
	}
	if err := scanner.Err(); err != nil {
		return false, err
	}
	return false, fmt.Errorf("did not find ro=true or ro=false for %s", path)
}
Ejemplo n.º 13
0
func execSend(path string, parent string, diff io.Writer) error {
	if parent == "" {
		return executil.RunStdout(diff, "btrfs", "send", path)
	}
	return executil.RunStdout(diff, "btrfs", "send", "-p", parent, path)
}