| 1 | #include "git-compat-util.h" |
| 2 | #include "fsmonitor-ll.h" |
| 3 | #include "fsmonitor-path-utils.h" |
| 4 | #include "gettext.h" |
| 5 | #include "trace.h" |
| 6 | |
| 7 | /* |
| 8 | * Check remote working directory protocol. |
| 9 | * |
| 10 | * Return -1 if client machine cannot get remote protocol information. |
| 11 | */ |
| 12 | static int check_remote_protocol(wchar_t *wpath) |
| 13 | { |
| 14 | HANDLE h; |
| 15 | FILE_REMOTE_PROTOCOL_INFO proto_info; |
| 16 | |
| 17 | h = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, |
| 18 | FILE_FLAG_BACKUP_SEMANTICS, NULL); |
| 19 | |
| 20 | if (h == INVALID_HANDLE_VALUE) { |
| 21 | error(_("[GLE %ld] unable to open for read '%ls'"), |
| 22 | GetLastError(), wpath); |
| 23 | return -1; |
| 24 | } |
| 25 | |
| 26 | if (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo, |
| 27 | &proto_info, sizeof(proto_info))) { |
| 28 | error(_("[GLE %ld] unable to get protocol information for '%ls'"), |
| 29 | GetLastError(), wpath); |
| 30 | CloseHandle(h); |
| 31 | return -1; |
| 32 | } |
| 33 | |
| 34 | CloseHandle(h); |
| 35 | |
| 36 | trace_printf_key(&trace_fsmonitor, |
| 37 | "check_remote_protocol('%ls') remote protocol %#8.8lx", |
| 38 | wpath, proto_info.Protocol); |
| 39 | |
| 40 | return 0; |
| 41 | } |
| 42 | |
| 43 | /* |
| 44 | * Notes for testing: |
| 45 | * |
| 46 | * (a) Windows allows a network share to be mapped to a drive letter. |
| 47 | * (This is the normal method to access it.) |
| 48 | * |
| 49 | * $ NET USE Z: \\server\share |
| 50 | * $ git -C Z:/repo status |
| 51 | * |
| 52 | * (b) Windows allows a network share to be referenced WITHOUT mapping |
| 53 | * it to drive letter. |
| 54 | * |
| 55 | * $ NET USE \\server\share\dir |
| 56 | * $ git -C //server/share/repo status |
| 57 | * |
| 58 | * (c) Windows allows "SUBST" to create a fake drive mapping to an |
| 59 | * arbitrary path (which may be remote) |
| 60 | * |
| 61 | * $ SUBST Q: Z:\repo |
| 62 | * $ git -C Q:/ status |
| 63 | * |
| 64 | * (d) Windows allows a directory symlink to be created on a local |
| 65 | * file system that points to a remote repo. |
| 66 | * |
| 67 | * $ mklink /d ./link //server/share/repo |
| 68 | * $ git -C ./link status |
| 69 | */ |
| 70 | int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info) |
| 71 | { |
| 72 | wchar_t wpath[MAX_PATH]; |
| 73 | wchar_t wfullpath[MAX_PATH]; |
| 74 | size_t wlen; |
| 75 | UINT driveType; |
| 76 | |
| 77 | /* |
| 78 | * Do everything in wide chars because the drive letter might be |
| 79 | * a multi-byte sequence. See win32_has_dos_drive_prefix(). |
| 80 | */ |
| 81 | if (xutftowcs_path(wpath, path) < 0) { |
| 82 | return -1; |
| 83 | } |
| 84 | |
| 85 | /* |
| 86 | * GetDriveTypeW() requires a final slash. We assume that the |
| 87 | * worktree pathname points to an actual directory. |
| 88 | */ |
| 89 | wlen = wcslen(wpath); |
| 90 | if (wpath[wlen - 1] != L'\\' && wpath[wlen - 1] != L'/') { |
| 91 | wpath[wlen++] = L'\\'; |
| 92 | wpath[wlen] = 0; |
| 93 | } |
| 94 | |
| 95 | /* |
| 96 | * Normalize the path. If nothing else, this converts forward |
| 97 | * slashes to backslashes. This is essential to get GetDriveTypeW() |
| 98 | * correctly handle some UNC "\\server\share\..." paths. |
| 99 | */ |
| 100 | if (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) { |
| 101 | return -1; |
| 102 | } |
| 103 | |
| 104 | driveType = GetDriveTypeW(wfullpath); |
| 105 | trace_printf_key(&trace_fsmonitor, |
| 106 | "DriveType '%s' L'%ls' (%u)", |
| 107 | path, wfullpath, driveType); |
| 108 | |
| 109 | if (driveType == DRIVE_REMOTE) { |
| 110 | fs_info->is_remote = 1; |
| 111 | if (check_remote_protocol(wfullpath) < 0) |
| 112 | return -1; |
| 113 | } else { |
| 114 | fs_info->is_remote = 0; |
| 115 | } |
| 116 | |
| 117 | trace_printf_key(&trace_fsmonitor, |
| 118 | "'%s' is_remote: %d", |
| 119 | path, fs_info->is_remote); |
| 120 | |
| 121 | return 0; |
| 122 | } |
| 123 | |
| 124 | int fsmonitor__is_fs_remote(const char *path) |
| 125 | { |
| 126 | struct fs_info fs; |
| 127 | if (fsmonitor__get_fs_info(path, &fs)) |
| 128 | return -1; |
| 129 | return fs.is_remote; |
| 130 | } |
| 131 | |
| 132 | /* |
| 133 | * No-op for now. |
| 134 | */ |
| 135 | int fsmonitor__get_alias(const char *path UNUSED, |
| 136 | struct alias_info *info UNUSED) |
| 137 | { |
| 138 | return 0; |
| 139 | } |
| 140 | |
| 141 | /* |
| 142 | * No-op for now. |
| 143 | */ |
| 144 | char *fsmonitor__resolve_alias(const char *path UNUSED, |
| 145 | const struct alias_info *info UNUSED) |
| 146 | { |
| 147 | return NULL; |
| 148 | } |