diff --git a/create-ntfs-junction.c b/create-ntfs-junction.c new file mode 100644 index 000000000..1b4d08ba3 --- /dev/null +++ b/create-ntfs-junction.c @@ -0,0 +1,161 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This is a simple utility for creating NTFS junction points on XP and later +// versions of Windows. + +#define _WIN32_WINNT 0x0500 + +#include +#include + +// This struct definition is absent from the system header files, +// but is described here: +// http://msdn.microsoft.com/en-us/library/ff552012.aspx +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} REPARSE_DATA_BUFFER; + +int main (int argc, char *argv[]) +{ + char buf[MAX_PATH*sizeof(WCHAR) + sizeof(REPARSE_DATA_BUFFER)]={'\0'}; + char* dest_path=NULL; + char* src_path=NULL; + char* src_link=NULL; + char* src_vol=NULL; + char* dest_vol=NULL; + HANDLE dir=NULL; + REPARSE_DATA_BUFFER* reparse = (REPARSE_DATA_BUFFER*) buf; + int path_len=0, data_len=0; + DWORD ioctl_return = 0xdeadbeef; + + // To allow this to be used as a drop-in replacement for the posix ln command, + // allow (and ignore) extra flags. + if (argc != 3 && (argc !=4 || *argv[1] != '-')) { + fputs("Usage: create-ntfs-junction \n", stderr); + return -1; + } + + src_path = argv[argc-2]; + path_len = strlen(src_path); + if (src_path[path_len-1] == '\\') + src_path[path_len-1] = '\0'; + + dest_path = argv[argc-1]; + path_len = strlen(dest_path); + if (dest_path[path_len-1] == '\\') + dest_path[path_len-1] = '\0'; + + if (GetFileAttributes(src_path) == INVALID_FILE_ATTRIBUTES) { + fprintf(stderr, "%s: No such animal.\n", src_path); + return -1; + } + + if (!GetVolumePathName(src_path, buf, MAX_PATH)) { + fprintf(stderr, "Couldn't get volume name for '%s'.\n", src_path); + return -1; + } + src_vol = _strdup(buf); + + if (!GetVolumePathName(dest_path, buf, MAX_PATH)) { + fprintf(stderr, "Couldn't get volume name for '%s'.\n", dest_path); + return -1; + } + dest_vol = _strdup(buf); + + if (strcmp(src_vol, dest_vol)) { + fprintf(stderr, "Cannot create junction point across volume boundary.\n"); + fprintf(stderr, " (from volume '%s' to volume '%s')\n", src_vol, dest_vol); + return -1; + } + + // End of input sanity checks; file system modifications may now occur. + + if (GetFileAttributes(dest_path) == INVALID_FILE_ATTRIBUTES) { + if (!CreateDirectory(dest_path, NULL)) { + switch(GetLastError()) { + case ERROR_ALREADY_EXISTS: + fprintf(stderr, "Can't create directory %s because it already exists " + "(this should never happen).\n", dest_path); + return -1; + break; + case ERROR_PATH_NOT_FOUND: + fprintf(stderr, "Can't create directory %s because some part of the " + "intermediate path doesn't exist.", dest_path); + return -1; + break; + default: + fprintf(stderr, "Unknown error occurred while trying to create " + "directory %s.", dest_path); + return -1; + break; + } + } + } + + dir = CreateFile(dest_path, + GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + strcpy_s(buf, 5, "\\??\\"); + GetFullPathName(src_path, MAX_PATH, buf+4, NULL); + src_link = _strdup(buf); + + memset(buf, 0, sizeof(buf)); + path_len = MultiByteToWideChar(CP_ACP, + 0, + src_link, + -1, + reparse->MountPointReparseBuffer.PathBuffer, + MAX_PATH*sizeof(WCHAR)); + + reparse->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + reparse->ReparseDataLength = (path_len+2)*sizeof(WCHAR) + 6; + reparse->MountPointReparseBuffer.SubstituteNameLength = + (path_len-1) * sizeof(WCHAR); + reparse->MountPointReparseBuffer.PrintNameOffset = + path_len * sizeof(WCHAR); + data_len = reparse->ReparseDataLength + 8; + + if (!DeviceIoControl(dir, + FSCTL_SET_REPARSE_POINT, + &buf, + data_len, + NULL, + 0, + &ioctl_return, + NULL)) { + fprintf(stderr, "Junction point creation failed (ioctl_return=0x%x) (%d)\n", + ioctl_return, GetLastError()); + return 1; + } + + return 0; +} diff --git a/create-ntfs-junction.exe b/create-ntfs-junction.exe new file mode 100755 index 000000000..12e0b7c5e Binary files /dev/null and b/create-ntfs-junction.exe differ