Index: tools/junction/junction.c |
diff --git a/tools/junction/junction.c b/tools/junction/junction.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..555bfa56dbf441cb2367ed7c7cd0c28ab84f9c17 |
--- /dev/null |
+++ b/tools/junction/junction.c |
@@ -0,0 +1,169 @@ |
+// 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 <stdio.h> |
+#include <windows.h> |
+ |
+// 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; |
M-A Ruel
2012/05/23 00:28:09
I highly recommend converting everything to wchar_
|
+ 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 fs_flags = 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: 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?
|
+ 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: invalid file attributes.\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); |
M-A Ruel
2012/05/23 00:28:09
Head explodes. :)
|
+ |
+ 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; |
+ } |
+ |
+ GetVolumeInformation(src_vol, NULL, 0, NULL, NULL, &fs_flags, buf, MAX_PATH); |
+ if (!(fs_flags & FILE_SUPPORTS_REPARSE_POINTS)) { |
+ fprintf(stderr, "File system type '%s' does not support reparse points.\n", |
+ buf); |
+ 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 " |
nsylvain
2012/03/16 18:24:18
you should probably print the error here.
|
+ "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; |
+} |