OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 // |
| 5 // This is a simple utility for creating NTFS junction points on XP and later |
| 6 // versions of Windows. |
| 7 |
| 8 #define _WIN32_WINNT 0x0500 |
| 9 |
| 10 #include <stdio.h> |
| 11 #include <windows.h> |
| 12 |
| 13 // This struct definition is absent from the system header files, |
| 14 // but is described here: |
| 15 // http://msdn.microsoft.com/en-us/library/ff552012.aspx |
| 16 typedef struct _REPARSE_DATA_BUFFER { |
| 17 ULONG ReparseTag; |
| 18 USHORT ReparseDataLength; |
| 19 USHORT Reserved; |
| 20 union { |
| 21 struct { |
| 22 USHORT SubstituteNameOffset; |
| 23 USHORT SubstituteNameLength; |
| 24 USHORT PrintNameOffset; |
| 25 USHORT PrintNameLength; |
| 26 ULONG Flags; |
| 27 WCHAR PathBuffer[1]; |
| 28 } SymbolicLinkReparseBuffer; |
| 29 struct { |
| 30 USHORT SubstituteNameOffset; |
| 31 USHORT SubstituteNameLength; |
| 32 USHORT PrintNameOffset; |
| 33 USHORT PrintNameLength; |
| 34 WCHAR PathBuffer[1]; |
| 35 } MountPointReparseBuffer; |
| 36 struct { |
| 37 UCHAR DataBuffer[1]; |
| 38 } GenericReparseBuffer; |
| 39 }; |
| 40 } REPARSE_DATA_BUFFER; |
| 41 |
| 42 int main (int argc, char *argv[]) |
| 43 { |
| 44 char buf[MAX_PATH*sizeof(WCHAR) + sizeof(REPARSE_DATA_BUFFER)]={'\0'}; |
| 45 char* dest_path=NULL; |
| 46 char* src_path=NULL; |
| 47 char* src_link=NULL; |
| 48 char* src_vol=NULL; |
| 49 char* dest_vol=NULL; |
| 50 HANDLE dir=NULL; |
| 51 REPARSE_DATA_BUFFER* reparse = (REPARSE_DATA_BUFFER*) buf; |
| 52 int path_len=0, data_len=0; |
| 53 DWORD ioctl_return = 0xdeadbeef; |
| 54 |
| 55 // To allow this to be used as a drop-in replacement for the posix ln command, |
| 56 // allow (and ignore) extra flags. |
| 57 if (argc != 3 && (argc !=4 || *argv[1] != '-')) { |
| 58 fputs("Usage: create-ntfs-junction <destination dir> <source dir>\n", stderr
); |
| 59 return -1; |
| 60 } |
| 61 |
| 62 src_path = argv[argc-2]; |
| 63 path_len = strlen(src_path); |
| 64 if (src_path[path_len-1] == '\\') |
| 65 src_path[path_len-1] = '\0'; |
| 66 |
| 67 dest_path = argv[argc-1]; |
| 68 path_len = strlen(dest_path); |
| 69 if (dest_path[path_len-1] == '\\') |
| 70 dest_path[path_len-1] = '\0'; |
| 71 |
| 72 if (GetFileAttributes(src_path) == INVALID_FILE_ATTRIBUTES) { |
| 73 fprintf(stderr, "%s: No such animal.\n", src_path); |
| 74 return -1; |
| 75 } |
| 76 |
| 77 if (!GetVolumePathName(src_path, buf, MAX_PATH)) { |
| 78 fprintf(stderr, "Couldn't get volume name for '%s'.\n", src_path); |
| 79 return -1; |
| 80 } |
| 81 src_vol = _strdup(buf); |
| 82 |
| 83 if (!GetVolumePathName(dest_path, buf, MAX_PATH)) { |
| 84 fprintf(stderr, "Couldn't get volume name for '%s'.\n", dest_path); |
| 85 return -1; |
| 86 } |
| 87 dest_vol = _strdup(buf); |
| 88 |
| 89 if (strcmp(src_vol, dest_vol)) { |
| 90 fprintf(stderr, "Cannot create junction point across volume boundary.\n"); |
| 91 fprintf(stderr, " (from volume '%s' to volume '%s')\n", src_vol, dest_vol); |
| 92 return -1; |
| 93 } |
| 94 |
| 95 // End of input sanity checks; file system modifications may now occur. |
| 96 |
| 97 if (GetFileAttributes(dest_path) == INVALID_FILE_ATTRIBUTES) { |
| 98 if (!CreateDirectory(dest_path, NULL)) { |
| 99 switch(GetLastError()) { |
| 100 case ERROR_ALREADY_EXISTS: |
| 101 fprintf(stderr, "Can't create directory %s because it already exists " |
| 102 "(this should never happen).\n", dest_path); |
| 103 return -1; |
| 104 break; |
| 105 case ERROR_PATH_NOT_FOUND: |
| 106 fprintf(stderr, "Can't create directory %s because some part of the " |
| 107 "intermediate path doesn't exist.", dest_path); |
| 108 return -1; |
| 109 break; |
| 110 default: |
| 111 fprintf(stderr, "Unknown error occurred while trying to create " |
| 112 "directory %s.", dest_path); |
| 113 return -1; |
| 114 break; |
| 115 } |
| 116 } |
| 117 } |
| 118 |
| 119 dir = CreateFile(dest_path, |
| 120 GENERIC_WRITE, |
| 121 0, |
| 122 NULL, |
| 123 OPEN_EXISTING, |
| 124 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, |
| 125 NULL); |
| 126 |
| 127 strcpy_s(buf, 5, "\\??\\"); |
| 128 GetFullPathName(src_path, MAX_PATH, buf+4, NULL); |
| 129 src_link = _strdup(buf); |
| 130 |
| 131 memset(buf, 0, sizeof(buf)); |
| 132 path_len = MultiByteToWideChar(CP_ACP, |
| 133 0, |
| 134 src_link, |
| 135 -1, |
| 136 reparse->MountPointReparseBuffer.PathBuffer, |
| 137 MAX_PATH*sizeof(WCHAR)); |
| 138 |
| 139 reparse->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; |
| 140 reparse->ReparseDataLength = (path_len+2)*sizeof(WCHAR) + 6; |
| 141 reparse->MountPointReparseBuffer.SubstituteNameLength = |
| 142 (path_len-1) * sizeof(WCHAR); |
| 143 reparse->MountPointReparseBuffer.PrintNameOffset = |
| 144 path_len * sizeof(WCHAR); |
| 145 data_len = reparse->ReparseDataLength + 8; |
| 146 |
| 147 if (!DeviceIoControl(dir, |
| 148 FSCTL_SET_REPARSE_POINT, |
| 149 &buf, |
| 150 data_len, |
| 151 NULL, |
| 152 0, |
| 153 &ioctl_return, |
| 154 NULL)) { |
| 155 fprintf(stderr, "Junction point creation failed (ioctl_return=0x%x) (%d)\n", |
| 156 ioctl_return, GetLastError()); |
| 157 return 1; |
| 158 } |
| 159 |
| 160 return 0; |
| 161 } |
OLD | NEW |