Chromium Code Reviews| 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; | 
| +} |