Getting more disk space, Symbolic Links and Azure Virtual Machines

AzureAzure lets you get free virtual machines.
What does this mean? You can use a free Azure account to create a new machine that only you can access, and can manipulate it as you see fit, almost as if you had a brand new computer to use (you can’t spill coffee on it). It can be configured with various amounts of memory, computing power.

You can use Remote Desktop Connection to connect to the new machine and use it as if you were right in front of it. Your monitor will show the Virtual Machine and the keyboard and mouse will be attached to the VM. It’s a “virtual” machine, so it doesn’t really exist as a physical computer, but it is hosted on a real computer.

The host machine can host many virtual machines simultaneously, each independent of the other, yielding huge economies of scale: think how often you use your computer, or more precisely, how often you don’t use your computer.

Unless you are a BlockChain miner (currently in the news a lot because of the huge computation power required), your machine will likely be unused much of the time, and thus the actual cost is just a few files on the disk of the host computer. As you use your virtual machine, parts of the file are swapped into the host, while others may be swapped out.

Virtual Machines have been around for a while: I’ve been hosting them in various Windows versions for over a decade. HyperVisor manager in Windows Server is a control center for managing VMs
I like to say that the 2 best inventions in computing are Cut/Paste, then Virtual Machines.

Here you can see one of my physical machines “CalvinH1” hosting 4 Virtual Machines:
image

Not only are there Virtual Machines, but you can create Virtual Disks for the VMs in both HyperVisor and Azure. These are stored on the host machine as files, which are pretty inexpensive to store these days. I remember getting a 5 Megabyte hard disk for several hundred dollars.
Once you create and associate a new Virtual Disk to a Virtual Machine via HyperVisor or Azure, you need to attach the disk to the machine. You then connect to the VM via Remote Desktop, open File Explorer, right click on “This PC”, choose Computer Management->Storage->Disk Management to see your new uninitialized disk (just as if attaching a real physical disk to a real physical machine).

You can format newly attached drives, create new volumes, and even mount a volume into a folder.
I have an enlistment for a very large GIT Repo on one of my Azure machines, which had a 127G initial drive size for volume C:. I ran out of space, so I created a couple volumes and mounted them to folders, like “c:\Nugetcache” and “c:\vs\VSOut”.

TreeMap is very handy at identifying large disk folders, which are candidates for moving or deleting.

image

 

image

 

Windows comes with a utility to create symbolic links: mklink
C:\>mklink /?
Creates a symbolic link.

MKLINK [[/D] | [/H] | [/J]] Link Target

        /D Creates a directory symbolic link. Default is a file
symbolic link.
/H Creates a hard link instead of a symbolic link.
/J Creates a Directory Junction.
Link Specifies the new symbolic link name.
Target Specifies the path (relative or absolute) that the new link
refers to.

Be very careful when deleting a symbolic link: ensure that you’re not deleting the target of the link, but just the link itself!

If you are running out of space on a volume, you can move some large folders out to make more room.
Identify the large folder. E.G. the Windows SDK “C:\Program Files (x86)\Windows Kits” can be several Gigabytes.
Shutdown any program that might be using the folder, like Visual Studio (ensure MsBuild shut down too)
Copy/delete original windows kits directory: e.g. "C:\Program Files (x86)\Windows Kits" to “e:\Windows Kits"
Mklink /j "C:\Program Files (x86)\Windows Kits" "e:\Windows Kits"

You can use the program below to understand more about Symbolic Links.

File->new->project->C#->Console App

Paste in the code below.
For testing, you can choose Project->properties->Debug tab, and in the Command Line Arguments, put is some sample arguments for testing. I used e.g.  /d foo "C:\footemp", which would make a link in the current directory to a folder called “c:\footemp”

The resulting Symbolic link “foo” appears:

image

 

And double clicking on foo goes to the target.

If you make a Symbolic link to a target that doesn’t exist, the link gets created, but navigating there via File Explorer yields:

image

 

If you prepend the linkname with  “\\?\” then you can make paths that are longer.  Try experimenting with the commented code.
"C:\Users\calvinh\source\repos\SYMBOL~1\SYMBOL~1\bin\Debug\LONGAB~1"

Put a breakpoint on the CreateSymbolicLinkW line and you can experiment with changing values, changing the code, Set Next Statement (to change the next statement to execute)
See Naming Files, Paths, and Namespaces for more on how to use very long ( up to 32,767 char) path names

 

<code>

 

 using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace SymbolicLInk
{
    class Program
    {
        static int Main(string[] args)
        {
            var instance = new Program();
            return instance.DoMain(args);
        }

        private int DoMain(string[] args)
        {
            var flag = SYMBOLIC_LINK_FLAG.File;
            var linkName = string.Empty;
            var linkTarget = string.Empty;
            for (int i = 0; i < args.Length; i++)
            {
                switch (args[i][0])
                {
                    case '-':
                    case '/':
                        if (args[i].Length > 1)
                        {
                            switch (args[i][1])
                            {
                                case 'D':
                                case 'd':
                                    flag = SYMBOLIC_LINK_FLAG.Directory;
                                    break;
                            }
                        }
                        break;
                    default:
                        if (string.IsNullOrEmpty(linkName))
                        {
                            linkName = args[i];
                        }
                        else
                        {
                            if (string.IsNullOrEmpty(linkTarget))
                            {
                                linkTarget = args[i];
                            }
                            else
                                return ShowHelp();
                        }
                        break;
                }
            }
            if (string.IsNullOrEmpty(linkName) || string.IsNullOrEmpty(linkTarget))
            {
                return ShowHelp();
            }
            //var longstr = $"a{new string('b', 242)}c";
            //var longname = $@"Long{longstr}dirname";
            //var lenLink = longname.Length;
            //linkName = $@"\\?\{System.Environment.CurrentDirectory}\{longname}";
            ////Starting with Windows 10, version 1607, for the unicode version of this function (CreateSymbolicLinkW), you can opt-in to remove the MAX_PATH limitation without prepending "\\?\". See the "Maximum Path Length Limitation" section of Naming Files, Paths, and Namespaces for details.
            //var lenLinkTotal = linkName.Length;

            if (CreateSymbolicLinkW(linkName, linkTarget, flag))
            {
                OutputString($"Successfully created link from '{linkName}' to '{linkTarget}'");
            }
            else
            {
                var lastErr = Marshal.GetLastWin32Error();
                var errInfo = string.Empty;
                switch (lastErr)  // from Tools->Error Lookup
                {
                    case 3: //The system cannot find the path specified. 
                        errInfo = "The system cannot find the path specified.";
                        break;
                    case 123: //The filename, directory name, or volume label syntax is incorrect. 
                        errInfo = "The filename, directory name, or volume label syntax is incorrect.";
                        break;
                    case 183: //Cannot create a file when that file already exists. 
                        errInfo = "Cannot create a file when that file already exists.";
                        break;
                    case 1314: // A required privilege is not held by the client. 
                        errInfo = "A required privilege is not held by the client. Are you running as Admin?";
                        break;
                    default:
                        break;
                }
                OutputString($"Failed to create link from '{linkName}' to '{linkTarget}'   {Marshal.GetLastWin32Error()} {errInfo}");
            }
            return 0; // success
        }

        private int ShowHelp()
        {
            OutputString("Symbolic Link");
            OutputString("Usage: SymbolicLink [/D] link target");
            OutputString("  link is the name to create for the link");
            OutputString("  target is the redirected location");
            OutputString("To delete the link, delete from File Explorer as if it were a normal file or directory");
            return 1; //failure
        }
        void OutputString(string str)
        {
            Debug.WriteLine(str);
            Console.WriteLine(str);
        }

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        [return: MarshalAs(System.Runtime.InteropServices.UnmanagedType.I1)]
        static extern bool CreateSymbolicLinkW(string lpSymlinkFileName, string lpTargetFileName, SYMBOLIC_LINK_FLAG dwFlags);
        public enum SYMBOLIC_LINK_FLAG
        {
            File = 0,
            Directory = 1
        }


    }
}

</code>