//_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) }
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) }
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) }
// 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() }
// 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() }
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") }
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") }
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) }
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) }
// 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() }
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 } } }
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) }
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) }