public BtrfsNfo(BtrfsNfos nfos, string basePath, string relPath, string uuid, string parentUUID, long generation, long genAtCreation) { this.nfos = nfos; BasePath = basePath; RelPath = relPath; UUID = uuid; ParentUUID = parentUUID; Generation = generation; GenAtCreation = genAtCreation; }
async Task <BtrfsNfos> ReadBtrfsNfo(string path) { var nfos = new BtrfsNfos(); { var res = await Toolkit.Exec("btrfs", new[] { "sub", "list", path }, ct, development); foreach (var x in res.output.Lines()) { var q = x.IndexOf("path "); if (q != -1) { var relpath = x.Substring(q + "path ".Length); var subvolFullpath = Path.Combine(path, relpath); var subvolInfo = await GetSubVolumeInfo(path, subvolFullpath, ct); nfos.Add(path, relpath, subvolInfo.uuid, subvolInfo.parentUUID, subvolInfo.generation, subvolInfo.genAtCreation); } } } return(nfos); }
public Task Run(string[] args) { if (!ParseArgs(args)) { PrintUsage(); Environment.Exit(1); } var cts = new CancellationTokenSource(); var ct = cts.Token; Console.CancelKeyPress += (s, e) => { e.Cancel = true; cts.Cancel(); }; return(Task.Run(async() => { var res = await Toolkit.Exec("btrfs", new[] { "sub", "list", SourcePath }, ct, development); var nfos = new BtrfsNfos(); var sourcePaths = new List <string>(); foreach (var x in res.output.Lines()) { var q = x.IndexOf("path "); if (q != -1) { var relpath = x.Substring(q + "path ".Length); var subvolFullpath = Path.Combine(SourcePath, relpath); var subvolInfo = await GetSubVolumeInfo(subvolFullpath, ct); nfos.Add(SourcePath, relpath, subvolInfo.uuid, subvolInfo.parentUUID, subvolInfo.generation, subvolInfo.genAtCreation); } } #region log helper var indent = 0; Action <BtrfsNfo> logNfo = null; logNfo = (entry) => { var indentSpaces = " ".Repeat(2 * indent); System.Console.WriteLine($@" {indentSpaces}path:[{entry.Fullpath}] {indentSpaces}uuid:[{entry.UUID}] {indentSpaces}parentUUID:[{entry.ParentUUID}]=[{entry.Parent?.Fullpath}] {indentSpaces}generation:[{entry.Generation}] {indentSpaces}genAtCreation:[{entry.GenAtCreation}] {indentSpaces}children:[{entry.Children.Count()}]"); ++indent; foreach (var c in entry.Children.OrderBy(w => w.GenAtCreation)) { logNfo(c); } --indent; }; Action header1 = () => { System.Console.WriteLine("=".Repeat(78)); }; Action header2 = () => { System.Console.WriteLine("-".Repeat(78)); }; #endregion // log foreach (var entry in nfos.Entries.Where(r => r.Parent == null)) { logNfo(entry); } // plan work var workPlan = new List <BtrfsResynActionNfo>(); Action <BtrfsNfo> planWorkAct = null; planWorkAct = (entry) => { var entryCounterPart = entry.CounterPart(TargetPath); if (entry.Parent == null && !Directory.Exists(entryCounterPart)) { var dstPath = Path.GetDirectoryName(entryCounterPart); if (!Directory.Exists(dstPath)) { workPlan.Add(new BtrfsResynActionNfo(BtrfsRsyncActionMode.ensurePath, Path.GetDirectoryName(entry.Fullpath), dstPath)); } workPlan.Add(new BtrfsResynActionNfo(BtrfsRsyncActionMode.createSubvol, entry.Fullpath, entryCounterPart)); } if (entry.Children.Any()) { foreach (var(child, idx, isLast) in entry.Children.OrderBy(w => w.GenAtCreation).WithIndexIsLast()) { var childCounterPart = child.CounterPart(TargetPath); var childCounterPartExists = Directory.Exists(childCounterPart); if (childCounterPartExists) { if (!SkipSubVolResync) { workPlan.Add(new BtrfsResynActionNfo(BtrfsRsyncActionMode.rsync, child.Fullpath, childCounterPart)); } } else if (entry.Parent == null) { workPlan.Add(new BtrfsResynActionNfo(BtrfsRsyncActionMode.rsync, child.Fullpath, entryCounterPart)); workPlan.Add(new BtrfsResynActionNfo(BtrfsRsyncActionMode.snap, entryCounterPart, childCounterPart)); } if (child.Children.Any()) { planWorkAct(child); } if (isLast) { workPlan.Add(new BtrfsResynActionNfo(BtrfsRsyncActionMode.rsync, entry.Fullpath, entryCounterPart, entry.Children.Select(w => w.CounterPart(TargetPath)))); } } } else { workPlan.Add(new BtrfsResynActionNfo(BtrfsRsyncActionMode.rsync, entry.Fullpath, entryCounterPart)); } }; foreach (var entry in nfos.Entries.Where(r => r.Parent == null)) { planWorkAct(entry); } #region log workplane { System.Console.WriteLine(); header1(); System.Console.WriteLine($"WORKPLAN"); header1(); foreach (var wp in workPlan) { System.Console.WriteLine(wp.ToString()); } } #endregion #region dryrun log helper Action <string, IEnumerable <string> > dryRunProg = (p, a) => { System.Console.WriteLine($"{p} {string.Join(" ", a)}"); }; #endregion System.Console.WriteLine(); header1(); System.Console.WriteLine($"RUNNING"); header1(); foreach (var wp in workPlan) { switch (wp.Mode) { case BtrfsRsyncActionMode.ensurePath: { // TODO: acl and permission set var cmdprog = "mkdir"; var cmdargs = new List <string>() { "-p", wp.DestPath }; if (RunMode == RunMode.dryRun) { dryRunProg(cmdprog, cmdargs); } else { var cmdres = await Toolkit.ExecNoRedirect(cmdprog, cmdargs, ct, development); } } break; case BtrfsRsyncActionMode.createSubvol: { var cmdprog = "btrfs"; var cmdargs = new List <string>() { "sub", "create", wp.DestPath }; if (RunMode == RunMode.dryRun) { dryRunProg(cmdprog, cmdargs); } else { var cmdres = await Toolkit.ExecNoRedirect(cmdprog, cmdargs, ct, development); } } break; case BtrfsRsyncActionMode.rsync: { var cmdprog = "rsync"; var cmdargs = new List <string>(); cmdargs.Add("-Aav"); cmdargs.Add("--delete"); foreach (var excl in wp.rsyncExclusions) { cmdargs.Add($"--exclude={excl}"); } cmdargs.Add(wp.SourcePath + "/"); cmdargs.Add(wp.DestPath + "/"); if (RunMode == RunMode.dryRun) { dryRunProg(cmdprog, cmdargs); } else { var cmdres = await Toolkit.ExecNoRedirect(cmdprog, cmdargs, ct, development); } } break; case BtrfsRsyncActionMode.snap: { var cmdprog = "btrfs"; var cmdargs = new List <string>() { "sub", "snap", wp.SourcePath, wp.DestPath }; if (RunMode == RunMode.dryRun) { dryRunProg(cmdprog, cmdargs); } else { var cmdres = await Toolkit.ExecNoRedirect(cmdprog, cmdargs, ct, development); } } break; } } })); }