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; | |
M-A Ruel
2012/05/23 00:28:09
I highly recommend converting everything to wchar_
| |
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 fs_flags = 0; | |
54 DWORD ioctl_return = 0xdeadbeef; | |
55 | |
56 // To allow this to be used as a drop-in replacement for the posix ln command, | |
57 // allow (and ignore) extra flags. | |
58 if (argc != 3 && (argc !=4 || *argv[1] != '-')) { | |
59 fputs("Usage: junction <destination dir> <source dir>\n", stderr); | |
M-A Ruel
2012/05/23 00:28:09
Why fputs here but fprintf around lines 91-92?
| |
60 return -1; | |
61 } | |
62 | |
63 src_path = argv[argc-2]; | |
64 path_len = strlen(src_path); | |
65 if (src_path[path_len-1] == '\\') | |
66 src_path[path_len-1] = '\0'; | |
67 | |
68 dest_path = argv[argc-1]; | |
69 path_len = strlen(dest_path); | |
70 if (dest_path[path_len-1] == '\\') | |
71 dest_path[path_len-1] = '\0'; | |
72 | |
73 if (GetFileAttributes(src_path) == INVALID_FILE_ATTRIBUTES) { | |
74 fprintf(stderr, "%s: invalid file attributes.\n", src_path); | |
75 return -1; | |
76 } | |
77 | |
78 if (!GetVolumePathName(src_path, buf, MAX_PATH)) { | |
79 fprintf(stderr, "Couldn't get volume name for '%s'.\n", src_path); | |
80 return -1; | |
81 } | |
82 src_vol = _strdup(buf); | |
M-A Ruel
2012/05/23 00:28:09
Head explodes. :)
| |
83 | |
84 if (!GetVolumePathName(dest_path, buf, MAX_PATH)) { | |
85 fprintf(stderr, "Couldn't get volume name for '%s'.\n", dest_path); | |
86 return -1; | |
87 } | |
88 dest_vol = _strdup(buf); | |
89 | |
90 if (strcmp(src_vol, dest_vol)) { | |
91 fprintf(stderr, "Cannot create junction point across volume boundary.\n"); | |
92 fprintf(stderr, " (from volume '%s' to volume '%s')\n", src_vol, dest_vol); | |
93 return -1; | |
94 } | |
95 | |
96 GetVolumeInformation(src_vol, NULL, 0, NULL, NULL, &fs_flags, buf, MAX_PATH); | |
97 if (!(fs_flags & FILE_SUPPORTS_REPARSE_POINTS)) { | |
98 fprintf(stderr, "File system type '%s' does not support reparse points.\n", | |
99 buf); | |
100 return -1; | |
101 } | |
102 | |
103 // End of input sanity checks; file system modifications may now occur. | |
104 | |
105 if (GetFileAttributes(dest_path) == INVALID_FILE_ATTRIBUTES) { | |
106 if (!CreateDirectory(dest_path, NULL)) { | |
107 switch(GetLastError()) { | |
108 case ERROR_ALREADY_EXISTS: | |
109 fprintf(stderr, "Can't create directory %s because it already exists " | |
110 "(this should never happen).\n", dest_path); | |
111 return -1; | |
112 break; | |
113 case ERROR_PATH_NOT_FOUND: | |
114 fprintf(stderr, "Can't create directory %s because some part of the " | |
115 "intermediate path doesn't exist.", dest_path); | |
116 return -1; | |
117 break; | |
118 default: | |
119 fprintf(stderr, "Unknown error occurred while trying to create " | |
nsylvain
2012/03/16 18:24:18
you should probably print the error here.
| |
120 "directory %s.", dest_path); | |
121 return -1; | |
122 break; | |
123 } | |
124 } | |
125 } | |
126 | |
127 dir = CreateFile(dest_path, | |
128 GENERIC_WRITE, | |
129 0, | |
130 NULL, | |
131 OPEN_EXISTING, | |
132 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, | |
133 NULL); | |
134 | |
135 strcpy_s(buf, 5, "\\??\\"); | |
136 GetFullPathName(src_path, MAX_PATH, buf+4, NULL); | |
137 src_link = _strdup(buf); | |
138 | |
139 memset(buf, 0, sizeof(buf)); | |
140 path_len = MultiByteToWideChar(CP_ACP, | |
141 0, | |
142 src_link, | |
143 -1, | |
144 reparse->MountPointReparseBuffer.PathBuffer, | |
145 MAX_PATH*sizeof(WCHAR)); | |
146 | |
147 reparse->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; | |
148 reparse->ReparseDataLength = (path_len+2)*sizeof(WCHAR) + 6; | |
149 reparse->MountPointReparseBuffer.SubstituteNameLength = | |
150 (path_len-1) * sizeof(WCHAR); | |
151 reparse->MountPointReparseBuffer.PrintNameOffset = | |
152 path_len * sizeof(WCHAR); | |
153 data_len = reparse->ReparseDataLength + 8; | |
154 | |
155 if (!DeviceIoControl(dir, | |
156 FSCTL_SET_REPARSE_POINT, | |
157 &buf, | |
158 data_len, | |
159 NULL, | |
160 0, | |
161 &ioctl_return, | |
162 NULL)) { | |
163 fprintf(stderr, "Junction point creation failed (ioctl_return=0x%x) (%d)\n", | |
164 ioctl_return, GetLastError()); | |
165 return 1; | |
166 } | |
167 | |
168 return 0; | |
169 } | |
OLD | NEW |