Liomblr RSS

Archive

Nov
10th
Tue
permalink

[Windows] Snapshot tool using hardlinks

using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Rosids
{
    class Program
    {
        static void Main(string[] args)
        {
            // @todo argument processer
            var src = @"C:\Users\LiosK\Documents\Visual Studio 2008";
            var lnk = @"C:\Users\LiosK\Desktop\Visual Studio 2008";
            var dst = @"C:\Users\LiosK\Desktop\RosidsTest";

            try
            {
                new Program(src, lnk, dst).Execute();
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e.Message);
            }
        }

        readonly DirectoryInfo Source;
        readonly DirectoryInfo LinkSource;
        readonly DirectoryInfo Destination;

        Program(string src, string lnk, string dst)
        {
            Source = new DirectoryInfo(src);
            LinkSource = new DirectoryInfo(lnk);
            Destination = new DirectoryInfo(dst);
        }

        void Execute()
        {
            CheckDirectories();
            Destination.Create();
            Visit(Source, LinkSource, Destination);
        }

        // Checks if Source, LinkSource and Destination satisfy assumptions.
        void CheckDirectories()
        {
            if (!Source.Exists)
            {
                throw new ApplicationException("Source directory does not exist: " + Source.FullName);
            }

            if (!LinkSource.Exists)
            {
                throw new ApplicationException("Link source directory does not exist: " + LinkSource.FullName);
            }

            if (Destination.Exists)
            {
                if (Destination.GetFileSystemInfos().Length > 0)
                {
                    throw new ApplicationException("Destination directory is not empty: " + Destination.FullName);
                }
            }
        }

        void Visit(DirectoryInfo src, DirectoryInfo lnk, DirectoryInfo dst)
        {
            if (!IsExcluded(src))
            {
                dst.Create();

                foreach (var file in src.GetFiles())
                {
                    try
                    {
                        if (!IsExcluded(file))
                        {
                            var lnkFile = Append(lnk, file);
                            if (IsSame(file, lnkFile))
                            {
                                // link
                                CreateHardLink(Append(dst, file).FullName, lnkFile.FullName, null);
                                Console.WriteLine("Linked: " + file.FullName);
                            }
                            else
                            {
                                // copy
                                file.CopyTo(Append(dst, file).FullName);
                                Console.WriteLine("Copied: " + file.FullName);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        // log and skip
                        Console.Error.WriteLine(e.Message + ": " + file.FullName);
                    }
                }

                foreach (var dir in src.GetDirectories())
                {
                    try
                    {
                        Visit(dir, Append(lnk, dir), Append(dst, dir));
                    }
                    catch (Exception e)
                    {
                        // log and skip
                        Console.Error.WriteLine(e.Message + ": " + dir.FullName);
                    }
                }
            }
        }

        bool IsExcluded(DirectoryInfo src)
        {
            return false;
        }

        bool IsExcluded(FileInfo src)
        {
            return false;
        }

        bool IsSame(FileInfo src, FileInfo lnk)
        {
            return src.Exists && lnk.Exists && (src.Name == lnk.Name) && (src.Length == lnk.Length) && (src.LastWriteTimeUtc == lnk.LastWriteTimeUtc);
        }

        // shortcut function
        private DirectoryInfo Append(DirectoryInfo dir, DirectoryInfo subdir)
        {
            return new DirectoryInfo(Path.Combine(dir.FullName, subdir.Name));
        }

        private FileInfo Append(DirectoryInfo dir, FileInfo file)
        {
            return new FileInfo(Path.Combine(dir.FullName, file.Name));
        }

        [DllImport("kernel32.dll")]
        extern static bool CreateHardLink(string file, string existingFile, object secAttr);
    }
}