Fossil SCM
Cleaning up some source comments & such. Also modified a memory block to be on the heap instead of the stack.
Commit
30ff96e7a5c4ff78e69655bfac0c9282bfef33d5
Parent
31b0a9d737c6417…
2 files changed
+12
+99
-38
+12
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -242,10 +242,22 @@ | ||
| 242 | 242 | blob_write_to_file(&content, zLinkFile); |
| 243 | 243 | blob_reset(&content); |
| 244 | 244 | } |
| 245 | 245 | } |
| 246 | 246 | |
| 247 | +/* | |
| 248 | +** This code is used in multiple places around fossil. Rather than having | |
| 249 | +** four or five slightly different cut & paste chunks of code, this function | |
| 250 | +** does the task of deleting a (possible) link (if necessary) and then | |
| 251 | +** either creating a link or a file based on input parameters. | |
| 252 | +** mayNeedDelete is true if we might need to call link_delete, false otherwise. | |
| 253 | +** needLink is true if we need to create a symlink, false otherwise. | |
| 254 | +** mayBeLink is true if zName might be a symlink based on the state of the | |
| 255 | +** checkout and/or file system, false otherwise. | |
| 256 | +** blob is the binary data to either write as a symlink or as a file. | |
| 257 | +** zName is the name of the file or symlink to write. | |
| 258 | +*/ | |
| 247 | 259 | void create_symlink_or_file(int mayNeedDelete, int needLink, int mayBeLink, Blob* blob, const char* zName){ |
| 248 | 260 | if (mayNeedDelete && (needLink || mayBeLink)) |
| 249 | 261 | link_delete(zName); |
| 250 | 262 | if (needLink) |
| 251 | 263 | symlink_create(blob_str(blob), zName); |
| 252 | 264 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -242,10 +242,22 @@ | |
| 242 | blob_write_to_file(&content, zLinkFile); |
| 243 | blob_reset(&content); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | void create_symlink_or_file(int mayNeedDelete, int needLink, int mayBeLink, Blob* blob, const char* zName){ |
| 248 | if (mayNeedDelete && (needLink || mayBeLink)) |
| 249 | link_delete(zName); |
| 250 | if (needLink) |
| 251 | symlink_create(blob_str(blob), zName); |
| 252 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -242,10 +242,22 @@ | |
| 242 | blob_write_to_file(&content, zLinkFile); |
| 243 | blob_reset(&content); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | /* |
| 248 | ** This code is used in multiple places around fossil. Rather than having |
| 249 | ** four or five slightly different cut & paste chunks of code, this function |
| 250 | ** does the task of deleting a (possible) link (if necessary) and then |
| 251 | ** either creating a link or a file based on input parameters. |
| 252 | ** mayNeedDelete is true if we might need to call link_delete, false otherwise. |
| 253 | ** needLink is true if we need to create a symlink, false otherwise. |
| 254 | ** mayBeLink is true if zName might be a symlink based on the state of the |
| 255 | ** checkout and/or file system, false otherwise. |
| 256 | ** blob is the binary data to either write as a symlink or as a file. |
| 257 | ** zName is the name of the file or symlink to write. |
| 258 | */ |
| 259 | void create_symlink_or_file(int mayNeedDelete, int needLink, int mayBeLink, Blob* blob, const char* zName){ |
| 260 | if (mayNeedDelete && (needLink || mayBeLink)) |
| 261 | link_delete(zName); |
| 262 | if (needLink) |
| 263 | symlink_create(blob_str(blob), zName); |
| 264 |
+99
-38
| --- src/winfile.c | ||
| +++ src/winfile.c | ||
| @@ -28,48 +28,72 @@ | ||
| 28 | 28 | |
| 29 | 29 | #ifndef LABEL_SECURITY_INFORMATION |
| 30 | 30 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 31 | 31 | #endif |
| 32 | 32 | |
| 33 | -/* copy & paste from ntifs.h */ | |
| 33 | +/* a couple defines to make the borrowed struct below compile */ | |
| 34 | +#define _ANONYMOUS_UNION | |
| 35 | +#define DUMMYUNIONNAME | |
| 36 | + | |
| 37 | +/* | |
| 38 | +** this structure copied on 20 Sept 2014 from | |
| 39 | +** https://reactos-mirror.googlecode.com/svn-history/r54752/branches/usb-bringup/include/ddk/ntifs.h | |
| 40 | +** which is a public domain file from the ReactOS DDK package. | |
| 41 | +*/ | |
| 42 | + | |
| 34 | 43 | typedef struct _REPARSE_DATA_BUFFER { |
| 35 | - ULONG ReparseTag; | |
| 44 | + ULONG ReparseTag; | |
| 36 | 45 | USHORT ReparseDataLength; |
| 37 | 46 | USHORT Reserved; |
| 38 | - union { | |
| 47 | + _ANONYMOUS_UNION union { | |
| 39 | 48 | struct { |
| 40 | 49 | USHORT SubstituteNameOffset; |
| 41 | 50 | USHORT SubstituteNameLength; |
| 42 | 51 | USHORT PrintNameOffset; |
| 43 | 52 | USHORT PrintNameLength; |
| 44 | - ULONG Flags; | |
| 45 | - WCHAR PathBuffer[1]; | |
| 53 | + ULONG Flags; | |
| 54 | + WCHAR PathBuffer[1]; | |
| 46 | 55 | } SymbolicLinkReparseBuffer; |
| 47 | 56 | struct { |
| 48 | 57 | USHORT SubstituteNameOffset; |
| 49 | 58 | USHORT SubstituteNameLength; |
| 50 | 59 | USHORT PrintNameOffset; |
| 51 | 60 | USHORT PrintNameLength; |
| 52 | - WCHAR PathBuffer[1]; | |
| 61 | + WCHAR PathBuffer[1]; | |
| 53 | 62 | } MountPointReparseBuffer; |
| 54 | 63 | struct { |
| 55 | 64 | UCHAR DataBuffer[1]; |
| 56 | 65 | } GenericReparseBuffer; |
| 57 | - }; | |
| 66 | + } DUMMYUNIONNAME; | |
| 58 | 67 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 59 | 68 | |
| 60 | 69 | #define LINK_BUFFER_SIZE 1024 |
| 61 | 70 | |
| 71 | +/* | |
| 72 | +** Fill stat buf with information received from GetFileAttributesExW(). | |
| 73 | +** Does not follow symbolic links, returning instead information about | |
| 74 | +** the link itself. | |
| 75 | +** Returns 0 on success, 1 on failure. | |
| 76 | +*/ | |
| 62 | 77 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 63 | 78 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 64 | 79 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 65 | 80 | if( rc ){ |
| 66 | - char *tname = fossil_filename_to_utf8(zFilename); | |
| 67 | - char tlink[LINK_BUFFER_SIZE]; | |
| 68 | - ssize_t tlen = win32_readlink(tname, tlink, sizeof(tlink)); | |
| 81 | + ssize_t tlen = 0; /* assume it is not a symbolic link */ | |
| 82 | + | |
| 83 | + /* if it is a reparse point it *might* be a symbolic link */ | |
| 84 | + /* so defer to win32_readlink to actually check */ | |
| 85 | + if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT){ | |
| 86 | + char *tname = fossil_filename_to_utf8(zFilename); | |
| 87 | + char tlink[LINK_BUFFER_SIZE]; | |
| 88 | + tlen = win32_readlink(tname, tlink, sizeof(tlink)); | |
| 89 | + fossil_filename_free(tname); | |
| 90 | + } | |
| 91 | + | |
| 69 | 92 | ULARGE_INTEGER ull; |
| 70 | 93 | |
| 94 | + /* if a link was retrieved, it is a symlink, otherwise a dir or file */ | |
| 71 | 95 | buf->st_mode = (tlen > 0) ? S_IFLNK : |
| 72 | 96 | ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 73 | 97 | S_IFDIR : S_IFREG); |
| 74 | 98 | |
| 75 | 99 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| @@ -80,40 +104,47 @@ | ||
| 80 | 104 | } |
| 81 | 105 | return !rc; |
| 82 | 106 | } |
| 83 | 107 | |
| 84 | 108 | /* |
| 85 | -** Fill stat buf with information received from stat() or lstat(). | |
| 86 | -** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on. | |
| 87 | -** | |
| 109 | +** Fill stat buf with information received from win32_lstat(). | |
| 110 | +** If a symbolic link is found, follow it and return information about | |
| 111 | +** the target, repeating until an actual target is found. | |
| 112 | +** Limit the number of loop iterations so as to avoid an infinite loop | |
| 113 | +** due to circular links. This should never happen because | |
| 114 | +** GetFinalPathNameByHandleW() should always preclude that need, but being | |
| 115 | +** prepared to loop seems prudent, or at least not harmful. | |
| 116 | +** Returns 0 on success, 1 on failure. | |
| 88 | 117 | */ |
| 89 | 118 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 90 | 119 | int rc; |
| 91 | 120 | HANDLE file; |
| 92 | 121 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 93 | 122 | DWORD len; |
| 123 | + int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */ | |
| 94 | 124 | |
| 95 | - while (1){ | |
| 125 | + while (iterationsRemaining-- > 0){ | |
| 96 | 126 | rc = win32_lstat(zFilename, buf); |
| 127 | + | |
| 97 | 128 | /* exit on error or not link */ |
| 98 | 129 | if ((rc != 0) || (buf->st_mode != S_IFLNK)) |
| 99 | 130 | break; |
| 100 | 131 | |
| 101 | 132 | /* it is a link, so open the linked file */ |
| 102 | 133 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); |
| 103 | 134 | if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){ |
| 104 | - rc = -1; | |
| 135 | + rc = 1; | |
| 105 | 136 | break; |
| 106 | 137 | } |
| 107 | 138 | |
| 108 | 139 | /* get the final path name and close the handle */ |
| 109 | 140 | len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); |
| 110 | 141 | CloseHandle(file); |
| 111 | 142 | |
| 112 | 143 | /* if any problems getting the final path name error so exit */ |
| 113 | 144 | if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){ |
| 114 | - rc = -1; | |
| 145 | + rc = 1; | |
| 115 | 146 | break; |
| 116 | 147 | } |
| 117 | 148 | |
| 118 | 149 | /* prepare to try again just in case we have a chain to follow */ |
| 119 | 150 | /* this shouldn't happen, but just trying to be safe */ |
| @@ -121,10 +152,15 @@ | ||
| 121 | 152 | } |
| 122 | 153 | |
| 123 | 154 | return rc; |
| 124 | 155 | } |
| 125 | 156 | |
| 157 | +/* | |
| 158 | +** An implementation of a posix-like readlink function for win32. | |
| 159 | +** Copies the target of a symbolic link to buf if possible. | |
| 160 | +** Returns the length of the link copied to buf on success, -1 on failure. | |
| 161 | +*/ | |
| 126 | 162 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 127 | 163 | /* assume we're going to fail */ |
| 128 | 164 | ssize_t rv = -1; |
| 129 | 165 | |
| 130 | 166 | /* does path reference a reparse point? */ |
| @@ -137,61 +173,79 @@ | ||
| 137 | 173 | FILE_FLAG_OPEN_REPARSE_POINT, NULL); |
| 138 | 174 | if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){ |
| 139 | 175 | |
| 140 | 176 | /* use DeviceIoControl to get the reparse point data */ |
| 141 | 177 | |
| 142 | - union { | |
| 143 | - REPARSE_DATA_BUFFER data; | |
| 144 | - char buffer[sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t)]; | |
| 145 | - } u; | |
| 146 | - DWORD bytes; | |
| 147 | - | |
| 148 | - u.data.ReparseTag = IO_REPARSE_TAG_SYMLINK; | |
| 149 | - u.data.ReparseDataLength = 0; | |
| 150 | - u.data.Reserved = 0; | |
| 178 | + int data_size = sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t); | |
| 179 | + REPARSE_DATA_BUFFER* data = fossil_malloc(data_size); | |
| 180 | + DWORD data_used; | |
| 181 | + | |
| 182 | + data->ReparseTag = IO_REPARSE_TAG_SYMLINK; | |
| 183 | + data->ReparseDataLength = 0; | |
| 184 | + data->Reserved = 0; | |
| 151 | 185 | |
| 152 | 186 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 153 | - &u, sizeof(u), &bytes, NULL); | |
| 187 | + data, data_size, &data_used, NULL); | |
| 154 | 188 | |
| 155 | 189 | /* did the reparse point data fit into the desired buffer? */ |
| 156 | - if (rc && (bytes < sizeof(u))){ | |
| 190 | + if (rc && (data_used < data_size)){ | |
| 157 | 191 | /* it fit, so setup the print name for further processing */ |
| 158 | 192 | USHORT |
| 159 | - offset = u.data.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), | |
| 160 | - length = u.data.SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t); | |
| 193 | + offset = data->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), | |
| 194 | + length = data->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t); | |
| 161 | 195 | char *temp; |
| 162 | - u.data.SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; | |
| 196 | + data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; | |
| 163 | 197 | |
| 164 | 198 | /* convert the filename to utf8, copy it, and discard the converted copy */ |
| 165 | - temp = fossil_filename_to_utf8(u.data.SymbolicLinkReparseBuffer.PathBuffer + offset); | |
| 199 | + temp = fossil_filename_to_utf8(data->SymbolicLinkReparseBuffer.PathBuffer + offset); | |
| 166 | 200 | rv = strlen(temp); |
| 167 | 201 | if (rv >= bufsiz) |
| 168 | 202 | rv = bufsiz; |
| 169 | 203 | memcpy(buf, temp, rv); |
| 170 | 204 | fossil_filename_free(temp); |
| 171 | 205 | } |
| 206 | + | |
| 207 | + fossil_free(data); | |
| 172 | 208 | |
| 173 | 209 | /* all done, close the reparse point */ |
| 174 | 210 | CloseHandle(file); |
| 175 | 211 | } |
| 176 | 212 | } |
| 177 | 213 | |
| 178 | 214 | return rv; |
| 179 | 215 | } |
| 180 | 216 | |
| 217 | +/* | |
| 218 | +** Either unlink a file or remove a directory on win32 systems. | |
| 219 | +** To delete a symlink on a posix system, you simply unlink the entry. | |
| 220 | +** Unfortunately for our purposes, win32 differentiates between symlinks for | |
| 221 | +** files and for directories. Thus you must unlink a file symlink or rmdir a | |
| 222 | +** directory symlink. This is a convenience function used when we know we're | |
| 223 | +** deleting a symlink of some type. | |
| 224 | +** Returns 0 on success, 1 on failure. | |
| 225 | +*/ | |
| 181 | 226 | int win32_unlink_rmdir(const wchar_t *zFilename){ |
| 182 | - int rc = -1; | |
| 227 | + int rc = 0; | |
| 183 | 228 | fossilStat stat; |
| 184 | 229 | if (win32_stat(zFilename, &stat) == 0){ |
| 185 | 230 | if (stat.st_mode == S_IFDIR) |
| 186 | - rc = RemoveDirectoryW(zFilename) ? 0 : -1; | |
| 231 | + rc = RemoveDirectoryW(zFilename); | |
| 187 | 232 | else |
| 188 | - rc = DeleteFileW(zFilename) ? 0 : -1; | |
| 233 | + rc = DeleteFileW(zFilename); | |
| 189 | 234 | } |
| 190 | - return rc; | |
| 235 | + return !rc; | |
| 191 | 236 | } |
| 192 | 237 | |
| 238 | +/* | |
| 239 | +** An implementation of a posix-like symlink function for win32. | |
| 240 | +** Attempts to create a file or directory symlink based on the target. | |
| 241 | +** Defaults to a file symlink if the target does not exist / can't be checked. | |
| 242 | +** Finally, if the symlink cannot be created for whatever reason (perhaps | |
| 243 | +** newpath is on a network share or a FAT derived file system), default to | |
| 244 | +** creation of a text file with the context of the link. | |
| 245 | +** Returns 0 on success, 1 on failure. | |
| 246 | +*/ | |
| 193 | 247 | int win32_symlink(const char *oldpath, const char *newpath){ |
| 194 | 248 | fossilStat stat; |
| 195 | 249 | int created = 0; |
| 196 | 250 | DWORD flags = 0; |
| 197 | 251 | wchar_t *zMbcs; |
| @@ -207,12 +261,11 @@ | ||
| 207 | 261 | /* remove newpath before creating the symlink */ |
| 208 | 262 | zMbcs = fossil_utf8_to_filename(newpath); |
| 209 | 263 | win32_unlink_rmdir(zMbcs); |
| 210 | 264 | fossil_filename_free(zMbcs); |
| 211 | 265 | |
| 212 | - if (CreateSymbolicLink(newpath, oldpath, flags)) | |
| 213 | - created = 1; | |
| 266 | + created = CreateSymbolicLink(newpath, oldpath, flags); | |
| 214 | 267 | |
| 215 | 268 | /* if the symlink was not created, create a plain text file */ |
| 216 | 269 | if (!created){ |
| 217 | 270 | Blob content; |
| 218 | 271 | blob_set(&content, oldpath); |
| @@ -219,13 +272,21 @@ | ||
| 219 | 272 | blob_write_to_file(&content, newpath); |
| 220 | 273 | blob_reset(&content); |
| 221 | 274 | created = 1; |
| 222 | 275 | } |
| 223 | 276 | |
| 224 | - return created ? 0 : -1; | |
| 277 | + return !created; | |
| 225 | 278 | } |
| 226 | 279 | |
| 280 | +/* | |
| 281 | +** Check if symlinks are potentially supported on the current OS. | |
| 282 | +** Theoretically this code should work on any NT based version of windows | |
| 283 | +** but I have no way of testing that. The initial check for | |
| 284 | +** IsWindowsVistaOrGreater() should in theory eliminate any system prior to | |
| 285 | +** Windows Vista, but I have no way to test that at this time. | |
| 286 | +** Return 1 if supported, 0 if not. | |
| 287 | +*/ | |
| 227 | 288 | int win32_symlinks_supported(){ |
| 228 | 289 | TOKEN_PRIVILEGES tp; |
| 229 | 290 | LUID luid; |
| 230 | 291 | HANDLE process, token; |
| 231 | 292 | DWORD status; |
| 232 | 293 |
| --- src/winfile.c | |
| +++ src/winfile.c | |
| @@ -28,48 +28,72 @@ | |
| 28 | |
| 29 | #ifndef LABEL_SECURITY_INFORMATION |
| 30 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 31 | #endif |
| 32 | |
| 33 | /* copy & paste from ntifs.h */ |
| 34 | typedef struct _REPARSE_DATA_BUFFER { |
| 35 | ULONG ReparseTag; |
| 36 | USHORT ReparseDataLength; |
| 37 | USHORT Reserved; |
| 38 | union { |
| 39 | struct { |
| 40 | USHORT SubstituteNameOffset; |
| 41 | USHORT SubstituteNameLength; |
| 42 | USHORT PrintNameOffset; |
| 43 | USHORT PrintNameLength; |
| 44 | ULONG Flags; |
| 45 | WCHAR PathBuffer[1]; |
| 46 | } SymbolicLinkReparseBuffer; |
| 47 | struct { |
| 48 | USHORT SubstituteNameOffset; |
| 49 | USHORT SubstituteNameLength; |
| 50 | USHORT PrintNameOffset; |
| 51 | USHORT PrintNameLength; |
| 52 | WCHAR PathBuffer[1]; |
| 53 | } MountPointReparseBuffer; |
| 54 | struct { |
| 55 | UCHAR DataBuffer[1]; |
| 56 | } GenericReparseBuffer; |
| 57 | }; |
| 58 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 59 | |
| 60 | #define LINK_BUFFER_SIZE 1024 |
| 61 | |
| 62 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 63 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 64 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 65 | if( rc ){ |
| 66 | char *tname = fossil_filename_to_utf8(zFilename); |
| 67 | char tlink[LINK_BUFFER_SIZE]; |
| 68 | ssize_t tlen = win32_readlink(tname, tlink, sizeof(tlink)); |
| 69 | ULARGE_INTEGER ull; |
| 70 | |
| 71 | buf->st_mode = (tlen > 0) ? S_IFLNK : |
| 72 | ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 73 | S_IFDIR : S_IFREG); |
| 74 | |
| 75 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| @@ -80,40 +104,47 @@ | |
| 80 | } |
| 81 | return !rc; |
| 82 | } |
| 83 | |
| 84 | /* |
| 85 | ** Fill stat buf with information received from stat() or lstat(). |
| 86 | ** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on. |
| 87 | ** |
| 88 | */ |
| 89 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 90 | int rc; |
| 91 | HANDLE file; |
| 92 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 93 | DWORD len; |
| 94 | |
| 95 | while (1){ |
| 96 | rc = win32_lstat(zFilename, buf); |
| 97 | /* exit on error or not link */ |
| 98 | if ((rc != 0) || (buf->st_mode != S_IFLNK)) |
| 99 | break; |
| 100 | |
| 101 | /* it is a link, so open the linked file */ |
| 102 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); |
| 103 | if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){ |
| 104 | rc = -1; |
| 105 | break; |
| 106 | } |
| 107 | |
| 108 | /* get the final path name and close the handle */ |
| 109 | len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); |
| 110 | CloseHandle(file); |
| 111 | |
| 112 | /* if any problems getting the final path name error so exit */ |
| 113 | if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){ |
| 114 | rc = -1; |
| 115 | break; |
| 116 | } |
| 117 | |
| 118 | /* prepare to try again just in case we have a chain to follow */ |
| 119 | /* this shouldn't happen, but just trying to be safe */ |
| @@ -121,10 +152,15 @@ | |
| 121 | } |
| 122 | |
| 123 | return rc; |
| 124 | } |
| 125 | |
| 126 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 127 | /* assume we're going to fail */ |
| 128 | ssize_t rv = -1; |
| 129 | |
| 130 | /* does path reference a reparse point? */ |
| @@ -137,61 +173,79 @@ | |
| 137 | FILE_FLAG_OPEN_REPARSE_POINT, NULL); |
| 138 | if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){ |
| 139 | |
| 140 | /* use DeviceIoControl to get the reparse point data */ |
| 141 | |
| 142 | union { |
| 143 | REPARSE_DATA_BUFFER data; |
| 144 | char buffer[sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t)]; |
| 145 | } u; |
| 146 | DWORD bytes; |
| 147 | |
| 148 | u.data.ReparseTag = IO_REPARSE_TAG_SYMLINK; |
| 149 | u.data.ReparseDataLength = 0; |
| 150 | u.data.Reserved = 0; |
| 151 | |
| 152 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 153 | &u, sizeof(u), &bytes, NULL); |
| 154 | |
| 155 | /* did the reparse point data fit into the desired buffer? */ |
| 156 | if (rc && (bytes < sizeof(u))){ |
| 157 | /* it fit, so setup the print name for further processing */ |
| 158 | USHORT |
| 159 | offset = u.data.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), |
| 160 | length = u.data.SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t); |
| 161 | char *temp; |
| 162 | u.data.SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; |
| 163 | |
| 164 | /* convert the filename to utf8, copy it, and discard the converted copy */ |
| 165 | temp = fossil_filename_to_utf8(u.data.SymbolicLinkReparseBuffer.PathBuffer + offset); |
| 166 | rv = strlen(temp); |
| 167 | if (rv >= bufsiz) |
| 168 | rv = bufsiz; |
| 169 | memcpy(buf, temp, rv); |
| 170 | fossil_filename_free(temp); |
| 171 | } |
| 172 | |
| 173 | /* all done, close the reparse point */ |
| 174 | CloseHandle(file); |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | return rv; |
| 179 | } |
| 180 | |
| 181 | int win32_unlink_rmdir(const wchar_t *zFilename){ |
| 182 | int rc = -1; |
| 183 | fossilStat stat; |
| 184 | if (win32_stat(zFilename, &stat) == 0){ |
| 185 | if (stat.st_mode == S_IFDIR) |
| 186 | rc = RemoveDirectoryW(zFilename) ? 0 : -1; |
| 187 | else |
| 188 | rc = DeleteFileW(zFilename) ? 0 : -1; |
| 189 | } |
| 190 | return rc; |
| 191 | } |
| 192 | |
| 193 | int win32_symlink(const char *oldpath, const char *newpath){ |
| 194 | fossilStat stat; |
| 195 | int created = 0; |
| 196 | DWORD flags = 0; |
| 197 | wchar_t *zMbcs; |
| @@ -207,12 +261,11 @@ | |
| 207 | /* remove newpath before creating the symlink */ |
| 208 | zMbcs = fossil_utf8_to_filename(newpath); |
| 209 | win32_unlink_rmdir(zMbcs); |
| 210 | fossil_filename_free(zMbcs); |
| 211 | |
| 212 | if (CreateSymbolicLink(newpath, oldpath, flags)) |
| 213 | created = 1; |
| 214 | |
| 215 | /* if the symlink was not created, create a plain text file */ |
| 216 | if (!created){ |
| 217 | Blob content; |
| 218 | blob_set(&content, oldpath); |
| @@ -219,13 +272,21 @@ | |
| 219 | blob_write_to_file(&content, newpath); |
| 220 | blob_reset(&content); |
| 221 | created = 1; |
| 222 | } |
| 223 | |
| 224 | return created ? 0 : -1; |
| 225 | } |
| 226 | |
| 227 | int win32_symlinks_supported(){ |
| 228 | TOKEN_PRIVILEGES tp; |
| 229 | LUID luid; |
| 230 | HANDLE process, token; |
| 231 | DWORD status; |
| 232 |
| --- src/winfile.c | |
| +++ src/winfile.c | |
| @@ -28,48 +28,72 @@ | |
| 28 | |
| 29 | #ifndef LABEL_SECURITY_INFORMATION |
| 30 | # define LABEL_SECURITY_INFORMATION (0x00000010L) |
| 31 | #endif |
| 32 | |
| 33 | /* a couple defines to make the borrowed struct below compile */ |
| 34 | #define _ANONYMOUS_UNION |
| 35 | #define DUMMYUNIONNAME |
| 36 | |
| 37 | /* |
| 38 | ** this structure copied on 20 Sept 2014 from |
| 39 | ** https://reactos-mirror.googlecode.com/svn-history/r54752/branches/usb-bringup/include/ddk/ntifs.h |
| 40 | ** which is a public domain file from the ReactOS DDK package. |
| 41 | */ |
| 42 | |
| 43 | typedef struct _REPARSE_DATA_BUFFER { |
| 44 | ULONG ReparseTag; |
| 45 | USHORT ReparseDataLength; |
| 46 | USHORT Reserved; |
| 47 | _ANONYMOUS_UNION union { |
| 48 | struct { |
| 49 | USHORT SubstituteNameOffset; |
| 50 | USHORT SubstituteNameLength; |
| 51 | USHORT PrintNameOffset; |
| 52 | USHORT PrintNameLength; |
| 53 | ULONG Flags; |
| 54 | WCHAR PathBuffer[1]; |
| 55 | } SymbolicLinkReparseBuffer; |
| 56 | struct { |
| 57 | USHORT SubstituteNameOffset; |
| 58 | USHORT SubstituteNameLength; |
| 59 | USHORT PrintNameOffset; |
| 60 | USHORT PrintNameLength; |
| 61 | WCHAR PathBuffer[1]; |
| 62 | } MountPointReparseBuffer; |
| 63 | struct { |
| 64 | UCHAR DataBuffer[1]; |
| 65 | } GenericReparseBuffer; |
| 66 | } DUMMYUNIONNAME; |
| 67 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
| 68 | |
| 69 | #define LINK_BUFFER_SIZE 1024 |
| 70 | |
| 71 | /* |
| 72 | ** Fill stat buf with information received from GetFileAttributesExW(). |
| 73 | ** Does not follow symbolic links, returning instead information about |
| 74 | ** the link itself. |
| 75 | ** Returns 0 on success, 1 on failure. |
| 76 | */ |
| 77 | int win32_lstat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 78 | WIN32_FILE_ATTRIBUTE_DATA attr; |
| 79 | int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); |
| 80 | if( rc ){ |
| 81 | ssize_t tlen = 0; /* assume it is not a symbolic link */ |
| 82 | |
| 83 | /* if it is a reparse point it *might* be a symbolic link */ |
| 84 | /* so defer to win32_readlink to actually check */ |
| 85 | if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT){ |
| 86 | char *tname = fossil_filename_to_utf8(zFilename); |
| 87 | char tlink[LINK_BUFFER_SIZE]; |
| 88 | tlen = win32_readlink(tname, tlink, sizeof(tlink)); |
| 89 | fossil_filename_free(tname); |
| 90 | } |
| 91 | |
| 92 | ULARGE_INTEGER ull; |
| 93 | |
| 94 | /* if a link was retrieved, it is a symlink, otherwise a dir or file */ |
| 95 | buf->st_mode = (tlen > 0) ? S_IFLNK : |
| 96 | ((attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? |
| 97 | S_IFDIR : S_IFREG); |
| 98 | |
| 99 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| @@ -80,40 +104,47 @@ | |
| 104 | } |
| 105 | return !rc; |
| 106 | } |
| 107 | |
| 108 | /* |
| 109 | ** Fill stat buf with information received from win32_lstat(). |
| 110 | ** If a symbolic link is found, follow it and return information about |
| 111 | ** the target, repeating until an actual target is found. |
| 112 | ** Limit the number of loop iterations so as to avoid an infinite loop |
| 113 | ** due to circular links. This should never happen because |
| 114 | ** GetFinalPathNameByHandleW() should always preclude that need, but being |
| 115 | ** prepared to loop seems prudent, or at least not harmful. |
| 116 | ** Returns 0 on success, 1 on failure. |
| 117 | */ |
| 118 | int win32_stat(const wchar_t *zFilename, struct fossilStat *buf){ |
| 119 | int rc; |
| 120 | HANDLE file; |
| 121 | wchar_t nextFilename[LINK_BUFFER_SIZE]; |
| 122 | DWORD len; |
| 123 | int iterationsRemaining = 8; /* 8 is arbitrary, can be modified as needed */ |
| 124 | |
| 125 | while (iterationsRemaining-- > 0){ |
| 126 | rc = win32_lstat(zFilename, buf); |
| 127 | |
| 128 | /* exit on error or not link */ |
| 129 | if ((rc != 0) || (buf->st_mode != S_IFLNK)) |
| 130 | break; |
| 131 | |
| 132 | /* it is a link, so open the linked file */ |
| 133 | file = CreateFileW(zFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); |
| 134 | if ((file == NULL) || (file == INVALID_HANDLE_VALUE)){ |
| 135 | rc = 1; |
| 136 | break; |
| 137 | } |
| 138 | |
| 139 | /* get the final path name and close the handle */ |
| 140 | len = GetFinalPathNameByHandleW(file, nextFilename, LINK_BUFFER_SIZE - 1, 0); |
| 141 | CloseHandle(file); |
| 142 | |
| 143 | /* if any problems getting the final path name error so exit */ |
| 144 | if ((len <= 0) || (len > LINK_BUFFER_SIZE - 1)){ |
| 145 | rc = 1; |
| 146 | break; |
| 147 | } |
| 148 | |
| 149 | /* prepare to try again just in case we have a chain to follow */ |
| 150 | /* this shouldn't happen, but just trying to be safe */ |
| @@ -121,10 +152,15 @@ | |
| 152 | } |
| 153 | |
| 154 | return rc; |
| 155 | } |
| 156 | |
| 157 | /* |
| 158 | ** An implementation of a posix-like readlink function for win32. |
| 159 | ** Copies the target of a symbolic link to buf if possible. |
| 160 | ** Returns the length of the link copied to buf on success, -1 on failure. |
| 161 | */ |
| 162 | ssize_t win32_readlink(const char *path, char *buf, size_t bufsiz){ |
| 163 | /* assume we're going to fail */ |
| 164 | ssize_t rv = -1; |
| 165 | |
| 166 | /* does path reference a reparse point? */ |
| @@ -137,61 +173,79 @@ | |
| 173 | FILE_FLAG_OPEN_REPARSE_POINT, NULL); |
| 174 | if ((file != NULL) && (file != INVALID_HANDLE_VALUE)){ |
| 175 | |
| 176 | /* use DeviceIoControl to get the reparse point data */ |
| 177 | |
| 178 | int data_size = sizeof(REPARSE_DATA_BUFFER) + LINK_BUFFER_SIZE * sizeof(wchar_t); |
| 179 | REPARSE_DATA_BUFFER* data = fossil_malloc(data_size); |
| 180 | DWORD data_used; |
| 181 | |
| 182 | data->ReparseTag = IO_REPARSE_TAG_SYMLINK; |
| 183 | data->ReparseDataLength = 0; |
| 184 | data->Reserved = 0; |
| 185 | |
| 186 | int rc = DeviceIoControl(file, FSCTL_GET_REPARSE_POINT, NULL, 0, |
| 187 | data, data_size, &data_used, NULL); |
| 188 | |
| 189 | /* did the reparse point data fit into the desired buffer? */ |
| 190 | if (rc && (data_used < data_size)){ |
| 191 | /* it fit, so setup the print name for further processing */ |
| 192 | USHORT |
| 193 | offset = data->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), |
| 194 | length = data->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t); |
| 195 | char *temp; |
| 196 | data->SymbolicLinkReparseBuffer.PathBuffer[offset + length] = 0; |
| 197 | |
| 198 | /* convert the filename to utf8, copy it, and discard the converted copy */ |
| 199 | temp = fossil_filename_to_utf8(data->SymbolicLinkReparseBuffer.PathBuffer + offset); |
| 200 | rv = strlen(temp); |
| 201 | if (rv >= bufsiz) |
| 202 | rv = bufsiz; |
| 203 | memcpy(buf, temp, rv); |
| 204 | fossil_filename_free(temp); |
| 205 | } |
| 206 | |
| 207 | fossil_free(data); |
| 208 | |
| 209 | /* all done, close the reparse point */ |
| 210 | CloseHandle(file); |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | return rv; |
| 215 | } |
| 216 | |
| 217 | /* |
| 218 | ** Either unlink a file or remove a directory on win32 systems. |
| 219 | ** To delete a symlink on a posix system, you simply unlink the entry. |
| 220 | ** Unfortunately for our purposes, win32 differentiates between symlinks for |
| 221 | ** files and for directories. Thus you must unlink a file symlink or rmdir a |
| 222 | ** directory symlink. This is a convenience function used when we know we're |
| 223 | ** deleting a symlink of some type. |
| 224 | ** Returns 0 on success, 1 on failure. |
| 225 | */ |
| 226 | int win32_unlink_rmdir(const wchar_t *zFilename){ |
| 227 | int rc = 0; |
| 228 | fossilStat stat; |
| 229 | if (win32_stat(zFilename, &stat) == 0){ |
| 230 | if (stat.st_mode == S_IFDIR) |
| 231 | rc = RemoveDirectoryW(zFilename); |
| 232 | else |
| 233 | rc = DeleteFileW(zFilename); |
| 234 | } |
| 235 | return !rc; |
| 236 | } |
| 237 | |
| 238 | /* |
| 239 | ** An implementation of a posix-like symlink function for win32. |
| 240 | ** Attempts to create a file or directory symlink based on the target. |
| 241 | ** Defaults to a file symlink if the target does not exist / can't be checked. |
| 242 | ** Finally, if the symlink cannot be created for whatever reason (perhaps |
| 243 | ** newpath is on a network share or a FAT derived file system), default to |
| 244 | ** creation of a text file with the context of the link. |
| 245 | ** Returns 0 on success, 1 on failure. |
| 246 | */ |
| 247 | int win32_symlink(const char *oldpath, const char *newpath){ |
| 248 | fossilStat stat; |
| 249 | int created = 0; |
| 250 | DWORD flags = 0; |
| 251 | wchar_t *zMbcs; |
| @@ -207,12 +261,11 @@ | |
| 261 | /* remove newpath before creating the symlink */ |
| 262 | zMbcs = fossil_utf8_to_filename(newpath); |
| 263 | win32_unlink_rmdir(zMbcs); |
| 264 | fossil_filename_free(zMbcs); |
| 265 | |
| 266 | created = CreateSymbolicLink(newpath, oldpath, flags); |
| 267 | |
| 268 | /* if the symlink was not created, create a plain text file */ |
| 269 | if (!created){ |
| 270 | Blob content; |
| 271 | blob_set(&content, oldpath); |
| @@ -219,13 +272,21 @@ | |
| 272 | blob_write_to_file(&content, newpath); |
| 273 | blob_reset(&content); |
| 274 | created = 1; |
| 275 | } |
| 276 | |
| 277 | return !created; |
| 278 | } |
| 279 | |
| 280 | /* |
| 281 | ** Check if symlinks are potentially supported on the current OS. |
| 282 | ** Theoretically this code should work on any NT based version of windows |
| 283 | ** but I have no way of testing that. The initial check for |
| 284 | ** IsWindowsVistaOrGreater() should in theory eliminate any system prior to |
| 285 | ** Windows Vista, but I have no way to test that at this time. |
| 286 | ** Return 1 if supported, 0 if not. |
| 287 | */ |
| 288 | int win32_symlinks_supported(){ |
| 289 | TOKEN_PRIVILEGES tp; |
| 290 | LUID luid; |
| 291 | HANDLE process, token; |
| 292 | DWORD status; |
| 293 |