// 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; }