Mercurial
comparison third_party/libuv/src/win/fs.c @ 160:948de3f54cea
[ThirdParty] Added libuv
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 14 Jan 2026 19:39:52 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 159:05cf9467a1c3 | 160:948de3f54cea |
|---|---|
| 1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. | |
| 2 * | |
| 3 * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 4 * of this software and associated documentation files (the "Software"), to | |
| 5 * deal in the Software without restriction, including without limitation the | |
| 6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
| 7 * sell copies of the Software, and to permit persons to whom the Software is | |
| 8 * furnished to do so, subject to the following conditions: | |
| 9 * | |
| 10 * The above copyright notice and this permission notice shall be included in | |
| 11 * all copies or substantial portions of the Software. | |
| 12 * | |
| 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
| 19 * IN THE SOFTWARE. | |
| 20 */ | |
| 21 | |
| 22 #include <assert.h> | |
| 23 #include <stdlib.h> | |
| 24 #include <direct.h> | |
| 25 #include <errno.h> | |
| 26 #include <fcntl.h> | |
| 27 #include <io.h> | |
| 28 #include <limits.h> | |
| 29 #include <sys/stat.h> | |
| 30 #include <sys/utime.h> | |
| 31 #include <stdio.h> | |
| 32 | |
| 33 #include "uv.h" | |
| 34 | |
| 35 /* <winioctl.h> requires <windows.h>, included via "uv.h" above, but needs to | |
| 36 be included before our "winapi.h", included via "internal.h" below. */ | |
| 37 #include <winioctl.h> | |
| 38 | |
| 39 #include "internal.h" | |
| 40 #include "req-inl.h" | |
| 41 #include "handle-inl.h" | |
| 42 #include "fs-fd-hash-inl.h" | |
| 43 | |
| 44 | |
| 45 #define UV_FS_FREE_PATHS 0x0002 | |
| 46 #define UV_FS_FREE_PTR 0x0008 | |
| 47 #define UV_FS_CLEANEDUP 0x0010 | |
| 48 | |
| 49 #ifndef FILE_DISPOSITION_DELETE | |
| 50 #define FILE_DISPOSITION_DELETE 0x0001 | |
| 51 #endif /* FILE_DISPOSITION_DELETE */ | |
| 52 | |
| 53 #ifndef FILE_DISPOSITION_POSIX_SEMANTICS | |
| 54 #define FILE_DISPOSITION_POSIX_SEMANTICS 0x0002 | |
| 55 #endif /* FILE_DISPOSITION_POSIX_SEMANTICS */ | |
| 56 | |
| 57 #ifndef FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE | |
| 58 #define FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE 0x0010 | |
| 59 #endif /* FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE */ | |
| 60 | |
| 61 NTSTATUS uv__RtlUnicodeStringInit( | |
| 62 PUNICODE_STRING DestinationString, | |
| 63 PWSTR SourceString, | |
| 64 size_t SourceStringLen | |
| 65 ) { | |
| 66 if (SourceStringLen > 0x7FFF) | |
| 67 return STATUS_INVALID_PARAMETER; | |
| 68 DestinationString->MaximumLength = DestinationString->Length = | |
| 69 SourceStringLen * sizeof(SourceString[0]); | |
| 70 DestinationString->Buffer = SourceString; | |
| 71 return STATUS_SUCCESS; | |
| 72 } | |
| 73 | |
| 74 #define INIT(subtype) \ | |
| 75 do { \ | |
| 76 if (req == NULL) \ | |
| 77 return UV_EINVAL; \ | |
| 78 uv__fs_req_init(loop, req, subtype, cb); \ | |
| 79 } \ | |
| 80 while (0) | |
| 81 | |
| 82 #define POST \ | |
| 83 do { \ | |
| 84 if (cb != NULL) { \ | |
| 85 uv__req_register(loop); \ | |
| 86 uv__work_submit(loop, \ | |
| 87 &req->work_req, \ | |
| 88 UV__WORK_FAST_IO, \ | |
| 89 uv__fs_work, \ | |
| 90 uv__fs_done); \ | |
| 91 return 0; \ | |
| 92 } else { \ | |
| 93 uv__fs_work(&req->work_req); \ | |
| 94 return req->result; \ | |
| 95 } \ | |
| 96 } \ | |
| 97 while (0) | |
| 98 | |
| 99 #define SET_REQ_RESULT(req, result_value) \ | |
| 100 do { \ | |
| 101 req->result = (result_value); \ | |
| 102 assert(req->result != -1); \ | |
| 103 } while (0) | |
| 104 | |
| 105 #define SET_REQ_WIN32_ERROR(req, sys_errno) \ | |
| 106 do { \ | |
| 107 req->sys_errno_ = (sys_errno); \ | |
| 108 req->result = uv_translate_sys_error(req->sys_errno_); \ | |
| 109 } while (0) | |
| 110 | |
| 111 #define SET_REQ_UV_ERROR(req, uv_errno, sys_errno) \ | |
| 112 do { \ | |
| 113 req->result = (uv_errno); \ | |
| 114 req->sys_errno_ = (sys_errno); \ | |
| 115 } while (0) | |
| 116 | |
| 117 #define VERIFY_FD(fd, req) \ | |
| 118 if (fd == -1) { \ | |
| 119 req->result = UV_EBADF; \ | |
| 120 req->sys_errno_ = ERROR_INVALID_HANDLE; \ | |
| 121 return; \ | |
| 122 } | |
| 123 | |
| 124 #define NSEC_PER_TICK 100 | |
| 125 #define TICKS_PER_SEC ((int64_t) 1e9 / NSEC_PER_TICK) | |
| 126 static const int64_t WIN_TO_UNIX_TICK_OFFSET = 11644473600 * TICKS_PER_SEC; | |
| 127 | |
| 128 static void uv__filetime_to_timespec(uv_timespec_t *ts, int64_t filetime) { | |
| 129 filetime -= WIN_TO_UNIX_TICK_OFFSET; | |
| 130 ts->tv_sec = filetime / TICKS_PER_SEC; | |
| 131 ts->tv_nsec = (filetime % TICKS_PER_SEC) * NSEC_PER_TICK; | |
| 132 if (ts->tv_nsec < 0) { | |
| 133 ts->tv_sec -= 1; | |
| 134 ts->tv_nsec += 1e9; | |
| 135 } | |
| 136 } | |
| 137 | |
| 138 #define TIME_T_TO_FILETIME(time, filetime_ptr) \ | |
| 139 do { \ | |
| 140 int64_t bigtime = ((time) * TICKS_PER_SEC + WIN_TO_UNIX_TICK_OFFSET); \ | |
| 141 (filetime_ptr)->dwLowDateTime = (uint64_t) bigtime & 0xFFFFFFFF; \ | |
| 142 (filetime_ptr)->dwHighDateTime = (uint64_t) bigtime >> 32; \ | |
| 143 } while(0) | |
| 144 | |
| 145 #define IS_SLASH(c) ((c) == L'\\' || (c) == L'/') | |
| 146 #define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \ | |
| 147 ((c) >= L'A' && (c) <= L'Z')) | |
| 148 | |
| 149 #define MIN(a,b) (((a) < (b)) ? (a) : (b)) | |
| 150 | |
| 151 const WCHAR JUNCTION_PREFIX[] = L"\\??\\"; | |
| 152 const WCHAR JUNCTION_PREFIX_LEN = 4; | |
| 153 | |
| 154 const WCHAR LONG_PATH_PREFIX[] = L"\\\\?\\"; | |
| 155 const WCHAR LONG_PATH_PREFIX_LEN = 4; | |
| 156 | |
| 157 const WCHAR UNC_PATH_PREFIX[] = L"\\\\?\\UNC\\"; | |
| 158 const WCHAR UNC_PATH_PREFIX_LEN = 8; | |
| 159 | |
| 160 static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; | |
| 161 | |
| 162 static DWORD uv__allocation_granularity; | |
| 163 | |
| 164 typedef enum { | |
| 165 FS__STAT_PATH_SUCCESS, | |
| 166 FS__STAT_PATH_ERROR, | |
| 167 FS__STAT_PATH_TRY_SLOW | |
| 168 } fs__stat_path_return_t; | |
| 169 | |
| 170 INLINE static void fs__stat_assign_statbuf_null(uv_stat_t* statbuf); | |
| 171 INLINE static void fs__stat_assign_statbuf(uv_stat_t* statbuf, | |
| 172 FILE_STAT_BASIC_INFORMATION stat_info, int do_lstat); | |
| 173 | |
| 174 | |
| 175 void uv__fs_init(void) { | |
| 176 SYSTEM_INFO system_info; | |
| 177 | |
| 178 GetSystemInfo(&system_info); | |
| 179 uv__allocation_granularity = system_info.dwAllocationGranularity; | |
| 180 | |
| 181 uv__fd_hash_init(); | |
| 182 } | |
| 183 | |
| 184 | |
| 185 INLINE static int fs__readlink_handle(HANDLE handle, | |
| 186 char** target_ptr, | |
| 187 size_t* target_len_ptr) { | |
| 188 char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; | |
| 189 REPARSE_DATA_BUFFER* reparse_data = (REPARSE_DATA_BUFFER*) buffer; | |
| 190 WCHAR* w_target; | |
| 191 DWORD w_target_len; | |
| 192 DWORD bytes; | |
| 193 size_t i; | |
| 194 size_t len; | |
| 195 | |
| 196 if (!DeviceIoControl(handle, | |
| 197 FSCTL_GET_REPARSE_POINT, | |
| 198 NULL, | |
| 199 0, | |
| 200 buffer, | |
| 201 sizeof buffer, | |
| 202 &bytes, | |
| 203 NULL)) { | |
| 204 return -1; | |
| 205 } | |
| 206 | |
| 207 if (reparse_data->ReparseTag == IO_REPARSE_TAG_SYMLINK) { | |
| 208 /* Real symlink */ | |
| 209 w_target = reparse_data->SymbolicLinkReparseBuffer.PathBuffer + | |
| 210 (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset / | |
| 211 sizeof(WCHAR)); | |
| 212 w_target_len = | |
| 213 reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength / | |
| 214 sizeof(WCHAR); | |
| 215 | |
| 216 /* Real symlinks can contain pretty much everything, but the only thing we | |
| 217 * really care about is undoing the implicit conversion to an NT namespaced | |
| 218 * path that CreateSymbolicLink will perform on absolute paths. If the path | |
| 219 * is win32-namespaced then the user must have explicitly made it so, and | |
| 220 * we better just return the unmodified reparse data. */ | |
| 221 if (w_target_len >= 4 && | |
| 222 w_target[0] == L'\\' && | |
| 223 w_target[1] == L'?' && | |
| 224 w_target[2] == L'?' && | |
| 225 w_target[3] == L'\\') { | |
| 226 /* Starts with \??\ */ | |
| 227 if (w_target_len >= 6 && | |
| 228 ((w_target[4] >= L'A' && w_target[4] <= L'Z') || | |
| 229 (w_target[4] >= L'a' && w_target[4] <= L'z')) && | |
| 230 w_target[5] == L':' && | |
| 231 (w_target_len == 6 || w_target[6] == L'\\')) { | |
| 232 /* \??\<drive>:\ */ | |
| 233 w_target += 4; | |
| 234 w_target_len -= 4; | |
| 235 | |
| 236 } else if (w_target_len >= 8 && | |
| 237 (w_target[4] == L'U' || w_target[4] == L'u') && | |
| 238 (w_target[5] == L'N' || w_target[5] == L'n') && | |
| 239 (w_target[6] == L'C' || w_target[6] == L'c') && | |
| 240 w_target[7] == L'\\') { | |
| 241 /* \??\UNC\<server>\<share>\ - make sure the final path looks like | |
| 242 * \\<server>\<share>\ */ | |
| 243 w_target += 6; | |
| 244 w_target[0] = L'\\'; | |
| 245 w_target_len -= 6; | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { | |
| 250 /* Junction. */ | |
| 251 w_target = reparse_data->MountPointReparseBuffer.PathBuffer + | |
| 252 (reparse_data->MountPointReparseBuffer.SubstituteNameOffset / | |
| 253 sizeof(WCHAR)); | |
| 254 w_target_len = reparse_data->MountPointReparseBuffer.SubstituteNameLength / | |
| 255 sizeof(WCHAR); | |
| 256 | |
| 257 /* Only treat junctions that look like \??\<drive>:\ as symlink. Junctions | |
| 258 * can also be used as mount points, like \??\Volume{<guid>}, but that's | |
| 259 * confusing for programs since they wouldn't be able to actually | |
| 260 * understand such a path when returned by uv_readlink(). UNC paths are | |
| 261 * never valid for junctions so we don't care about them. */ | |
| 262 if (!(w_target_len >= 6 && | |
| 263 w_target[0] == L'\\' && | |
| 264 w_target[1] == L'?' && | |
| 265 w_target[2] == L'?' && | |
| 266 w_target[3] == L'\\' && | |
| 267 ((w_target[4] >= L'A' && w_target[4] <= L'Z') || | |
| 268 (w_target[4] >= L'a' && w_target[4] <= L'z')) && | |
| 269 w_target[5] == L':' && | |
| 270 (w_target_len == 6 || w_target[6] == L'\\'))) { | |
| 271 SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); | |
| 272 return -1; | |
| 273 } | |
| 274 | |
| 275 /* Remove leading \??\ */ | |
| 276 w_target += 4; | |
| 277 w_target_len -= 4; | |
| 278 | |
| 279 } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK) { | |
| 280 /* String #3 in the list has the target filename. */ | |
| 281 if (reparse_data->AppExecLinkReparseBuffer.StringCount < 3) { | |
| 282 SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); | |
| 283 return -1; | |
| 284 } | |
| 285 w_target = reparse_data->AppExecLinkReparseBuffer.StringList; | |
| 286 /* The StringList buffer contains a list of strings separated by "\0", */ | |
| 287 /* with "\0\0" terminating the list. Move to the 3rd string in the list: */ | |
| 288 for (i = 0; i < 2; ++i) { | |
| 289 len = wcslen(w_target); | |
| 290 if (len == 0) { | |
| 291 SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); | |
| 292 return -1; | |
| 293 } | |
| 294 w_target += len + 1; | |
| 295 } | |
| 296 w_target_len = wcslen(w_target); | |
| 297 if (w_target_len == 0) { | |
| 298 SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); | |
| 299 return -1; | |
| 300 } | |
| 301 /* Make sure it is an absolute path. */ | |
| 302 if (!(w_target_len >= 3 && | |
| 303 ((w_target[0] >= L'a' && w_target[0] <= L'z') || | |
| 304 (w_target[0] >= L'A' && w_target[0] <= L'Z')) && | |
| 305 w_target[1] == L':' && | |
| 306 w_target[2] == L'\\')) { | |
| 307 SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); | |
| 308 return -1; | |
| 309 } | |
| 310 | |
| 311 } else { | |
| 312 /* Reparse tag does not indicate a symlink. */ | |
| 313 SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); | |
| 314 return -1; | |
| 315 } | |
| 316 | |
| 317 assert(target_ptr == NULL || *target_ptr == NULL); | |
| 318 return uv_utf16_to_wtf8(w_target, w_target_len, target_ptr, target_len_ptr); | |
| 319 } | |
| 320 | |
| 321 | |
| 322 INLINE static int fs__capture_path(uv_fs_t* req, const char* path, | |
| 323 const char* new_path, const int copy_path) { | |
| 324 WCHAR* buf; | |
| 325 WCHAR* pos; | |
| 326 size_t buf_sz = 0; | |
| 327 size_t path_len = 0; | |
| 328 ssize_t pathw_len = 0; | |
| 329 ssize_t new_pathw_len = 0; | |
| 330 | |
| 331 /* new_path can only be set if path is also set. */ | |
| 332 assert(new_path == NULL || path != NULL); | |
| 333 | |
| 334 if (path != NULL) { | |
| 335 pathw_len = uv_wtf8_length_as_utf16(path); | |
| 336 if (pathw_len < 0) | |
| 337 return ERROR_INVALID_NAME; | |
| 338 buf_sz += pathw_len * sizeof(WCHAR); | |
| 339 } | |
| 340 | |
| 341 if (path != NULL && copy_path) { | |
| 342 path_len = 1 + strlen(path); | |
| 343 buf_sz += path_len; | |
| 344 } | |
| 345 | |
| 346 if (new_path != NULL) { | |
| 347 new_pathw_len = uv_wtf8_length_as_utf16(new_path); | |
| 348 if (new_pathw_len < 0) | |
| 349 return ERROR_INVALID_NAME; | |
| 350 buf_sz += new_pathw_len * sizeof(WCHAR); | |
| 351 } | |
| 352 | |
| 353 | |
| 354 if (buf_sz == 0) { | |
| 355 req->file.pathw = NULL; | |
| 356 req->fs.info.new_pathw = NULL; | |
| 357 req->path = NULL; | |
| 358 return 0; | |
| 359 } | |
| 360 | |
| 361 buf = uv__malloc(buf_sz); | |
| 362 if (buf == NULL) { | |
| 363 return ERROR_OUTOFMEMORY; | |
| 364 } | |
| 365 | |
| 366 pos = buf; | |
| 367 | |
| 368 if (path != NULL) { | |
| 369 uv_wtf8_to_utf16(path, pos, pathw_len); | |
| 370 req->file.pathw = pos; | |
| 371 pos += pathw_len; | |
| 372 } else { | |
| 373 req->file.pathw = NULL; | |
| 374 } | |
| 375 | |
| 376 if (new_path != NULL) { | |
| 377 uv_wtf8_to_utf16(new_path, pos, new_pathw_len); | |
| 378 req->fs.info.new_pathw = pos; | |
| 379 pos += new_pathw_len; | |
| 380 } else { | |
| 381 req->fs.info.new_pathw = NULL; | |
| 382 } | |
| 383 | |
| 384 req->path = path; | |
| 385 if (path != NULL && copy_path) { | |
| 386 memcpy(pos, path, path_len); | |
| 387 assert(path_len == buf_sz - (pos - buf) * sizeof(WCHAR)); | |
| 388 req->path = (char*) pos; | |
| 389 } | |
| 390 | |
| 391 req->flags |= UV_FS_FREE_PATHS; | |
| 392 | |
| 393 return 0; | |
| 394 } | |
| 395 | |
| 396 | |
| 397 INLINE static void uv__fs_req_init(uv_loop_t* loop, uv_fs_t* req, | |
| 398 uv_fs_type fs_type, const uv_fs_cb cb) { | |
| 399 uv__once_init(); | |
| 400 UV_REQ_INIT(req, UV_FS); | |
| 401 req->loop = loop; | |
| 402 req->flags = 0; | |
| 403 req->fs_type = fs_type; | |
| 404 req->sys_errno_ = 0; | |
| 405 req->result = 0; | |
| 406 req->ptr = NULL; | |
| 407 req->path = NULL; | |
| 408 req->cb = cb; | |
| 409 memset(&req->fs, 0, sizeof(req->fs)); | |
| 410 } | |
| 411 | |
| 412 | |
| 413 void fs__open(uv_fs_t* req) { | |
| 414 DWORD access; | |
| 415 DWORD share; | |
| 416 DWORD disposition; | |
| 417 DWORD attributes = 0; | |
| 418 HANDLE file; | |
| 419 int fd, current_umask; | |
| 420 int flags = req->fs.info.file_flags; | |
| 421 struct uv__fd_info_s fd_info; | |
| 422 | |
| 423 /* Adjust flags to be compatible with the memory file mapping. Save the | |
| 424 * original flags to emulate the correct behavior. */ | |
| 425 if (flags & UV_FS_O_FILEMAP) { | |
| 426 fd_info.flags = flags; | |
| 427 fd_info.current_pos.QuadPart = 0; | |
| 428 | |
| 429 if ((flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) == | |
| 430 UV_FS_O_WRONLY) { | |
| 431 /* CreateFileMapping always needs read access */ | |
| 432 flags = (flags & ~UV_FS_O_WRONLY) | UV_FS_O_RDWR; | |
| 433 } | |
| 434 | |
| 435 if (flags & UV_FS_O_APPEND) { | |
| 436 /* Clear the append flag and ensure RDRW mode */ | |
| 437 flags &= ~UV_FS_O_APPEND; | |
| 438 flags &= ~(UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); | |
| 439 flags |= UV_FS_O_RDWR; | |
| 440 } | |
| 441 } | |
| 442 | |
| 443 /* Obtain the active umask. umask() never fails and returns the previous | |
| 444 * umask. */ | |
| 445 current_umask = _umask(0); | |
| 446 _umask(current_umask); | |
| 447 | |
| 448 /* convert flags and mode to CreateFile parameters */ | |
| 449 switch (flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) { | |
| 450 case UV_FS_O_RDONLY: | |
| 451 access = FILE_GENERIC_READ; | |
| 452 break; | |
| 453 case UV_FS_O_WRONLY: | |
| 454 access = FILE_GENERIC_WRITE; | |
| 455 break; | |
| 456 case UV_FS_O_RDWR: | |
| 457 access = FILE_GENERIC_READ | FILE_GENERIC_WRITE; | |
| 458 break; | |
| 459 default: | |
| 460 goto einval; | |
| 461 } | |
| 462 | |
| 463 if (flags & UV_FS_O_APPEND) { | |
| 464 access &= ~FILE_WRITE_DATA; | |
| 465 access |= FILE_APPEND_DATA; | |
| 466 } | |
| 467 | |
| 468 /* | |
| 469 * Here is where we deviate significantly from what CRT's _open() | |
| 470 * does. We indiscriminately use all the sharing modes, to match | |
| 471 * UNIX semantics. In particular, this ensures that the file can | |
| 472 * be deleted even whilst it's open, fixing issue | |
| 473 * https://github.com/nodejs/node-v0.x-archive/issues/1449. | |
| 474 * We still support exclusive sharing mode, since it is necessary | |
| 475 * for opening raw block devices, otherwise Windows will prevent | |
| 476 * any attempt to write past the master boot record. | |
| 477 */ | |
| 478 if (flags & UV_FS_O_EXLOCK) { | |
| 479 share = 0; | |
| 480 } else { | |
| 481 share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; | |
| 482 } | |
| 483 | |
| 484 switch (flags & (UV_FS_O_CREAT | UV_FS_O_EXCL | UV_FS_O_TRUNC)) { | |
| 485 case 0: | |
| 486 case UV_FS_O_EXCL: | |
| 487 disposition = OPEN_EXISTING; | |
| 488 break; | |
| 489 case UV_FS_O_CREAT: | |
| 490 disposition = OPEN_ALWAYS; | |
| 491 break; | |
| 492 case UV_FS_O_CREAT | UV_FS_O_EXCL: | |
| 493 case UV_FS_O_CREAT | UV_FS_O_TRUNC | UV_FS_O_EXCL: | |
| 494 disposition = CREATE_NEW; | |
| 495 break; | |
| 496 case UV_FS_O_TRUNC: | |
| 497 case UV_FS_O_TRUNC | UV_FS_O_EXCL: | |
| 498 disposition = TRUNCATE_EXISTING; | |
| 499 break; | |
| 500 case UV_FS_O_CREAT | UV_FS_O_TRUNC: | |
| 501 disposition = CREATE_ALWAYS; | |
| 502 break; | |
| 503 default: | |
| 504 goto einval; | |
| 505 } | |
| 506 | |
| 507 attributes |= FILE_ATTRIBUTE_NORMAL; | |
| 508 if (flags & UV_FS_O_CREAT) { | |
| 509 if (!((req->fs.info.mode & ~current_umask) & _S_IWRITE)) { | |
| 510 attributes |= FILE_ATTRIBUTE_READONLY; | |
| 511 } | |
| 512 } | |
| 513 | |
| 514 if (flags & UV_FS_O_TEMPORARY ) { | |
| 515 attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY; | |
| 516 access |= DELETE; | |
| 517 } | |
| 518 | |
| 519 if (flags & UV_FS_O_SHORT_LIVED) { | |
| 520 attributes |= FILE_ATTRIBUTE_TEMPORARY; | |
| 521 } | |
| 522 | |
| 523 switch (flags & (UV_FS_O_SEQUENTIAL | UV_FS_O_RANDOM)) { | |
| 524 case 0: | |
| 525 break; | |
| 526 case UV_FS_O_SEQUENTIAL: | |
| 527 attributes |= FILE_FLAG_SEQUENTIAL_SCAN; | |
| 528 break; | |
| 529 case UV_FS_O_RANDOM: | |
| 530 attributes |= FILE_FLAG_RANDOM_ACCESS; | |
| 531 break; | |
| 532 default: | |
| 533 goto einval; | |
| 534 } | |
| 535 | |
| 536 if (flags & UV_FS_O_DIRECT) { | |
| 537 /* | |
| 538 * FILE_APPEND_DATA and FILE_FLAG_NO_BUFFERING are mutually exclusive. | |
| 539 * Windows returns 87, ERROR_INVALID_PARAMETER if these are combined. | |
| 540 * | |
| 541 * FILE_APPEND_DATA is included in FILE_GENERIC_WRITE: | |
| 542 * | |
| 543 * FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE | | |
| 544 * FILE_WRITE_DATA | | |
| 545 * FILE_WRITE_ATTRIBUTES | | |
| 546 * FILE_WRITE_EA | | |
| 547 * FILE_APPEND_DATA | | |
| 548 * SYNCHRONIZE | |
| 549 * | |
| 550 * Note: Appends are also permitted by FILE_WRITE_DATA. | |
| 551 * | |
| 552 * In order for direct writes and direct appends to succeed, we therefore | |
| 553 * exclude FILE_APPEND_DATA if FILE_WRITE_DATA is specified, and otherwise | |
| 554 * fail if the user's sole permission is a direct append, since this | |
| 555 * particular combination is invalid. | |
| 556 */ | |
| 557 if (access & FILE_APPEND_DATA) { | |
| 558 if (access & FILE_WRITE_DATA) { | |
| 559 access &= ~FILE_APPEND_DATA; | |
| 560 } else { | |
| 561 goto einval; | |
| 562 } | |
| 563 } | |
| 564 attributes |= FILE_FLAG_NO_BUFFERING; | |
| 565 } | |
| 566 | |
| 567 switch (flags & (UV_FS_O_DSYNC | UV_FS_O_SYNC)) { | |
| 568 case 0: | |
| 569 break; | |
| 570 case UV_FS_O_DSYNC: | |
| 571 case UV_FS_O_SYNC: | |
| 572 attributes |= FILE_FLAG_WRITE_THROUGH; | |
| 573 break; | |
| 574 default: | |
| 575 goto einval; | |
| 576 } | |
| 577 | |
| 578 /* Setting this flag makes it possible to open a directory. */ | |
| 579 attributes |= FILE_FLAG_BACKUP_SEMANTICS; | |
| 580 | |
| 581 file = CreateFileW(req->file.pathw, | |
| 582 access, | |
| 583 share, | |
| 584 NULL, | |
| 585 disposition, | |
| 586 attributes, | |
| 587 NULL); | |
| 588 if (file == INVALID_HANDLE_VALUE) { | |
| 589 DWORD error = GetLastError(); | |
| 590 if (error == ERROR_FILE_EXISTS && (flags & UV_FS_O_CREAT) && | |
| 591 !(flags & UV_FS_O_EXCL)) { | |
| 592 /* Special case: when ERROR_FILE_EXISTS happens and UV_FS_O_CREAT was | |
| 593 * specified, it means the path referred to a directory. */ | |
| 594 SET_REQ_UV_ERROR(req, UV_EISDIR, error); | |
| 595 } else { | |
| 596 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 597 } | |
| 598 return; | |
| 599 } | |
| 600 | |
| 601 fd = _open_osfhandle((intptr_t) file, flags); | |
| 602 if (fd < 0) { | |
| 603 /* The only known failure mode for _open_osfhandle() is EMFILE, in which | |
| 604 * case GetLastError() will return zero. However we'll try to handle other | |
| 605 * errors as well, should they ever occur. | |
| 606 */ | |
| 607 if (errno == EMFILE) | |
| 608 SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES); | |
| 609 else if (GetLastError() != ERROR_SUCCESS) | |
| 610 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 611 else | |
| 612 SET_REQ_WIN32_ERROR(req, (DWORD) UV_UNKNOWN); | |
| 613 CloseHandle(file); | |
| 614 return; | |
| 615 } | |
| 616 | |
| 617 if (flags & UV_FS_O_FILEMAP) { | |
| 618 FILE_STANDARD_INFO file_info; | |
| 619 if (!GetFileInformationByHandleEx(file, | |
| 620 FileStandardInfo, | |
| 621 &file_info, | |
| 622 sizeof file_info)) { | |
| 623 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 624 CloseHandle(file); | |
| 625 return; | |
| 626 } | |
| 627 fd_info.is_directory = file_info.Directory; | |
| 628 | |
| 629 if (fd_info.is_directory) { | |
| 630 fd_info.size.QuadPart = 0; | |
| 631 fd_info.mapping = INVALID_HANDLE_VALUE; | |
| 632 } else { | |
| 633 if (!GetFileSizeEx(file, &fd_info.size)) { | |
| 634 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 635 CloseHandle(file); | |
| 636 return; | |
| 637 } | |
| 638 | |
| 639 if (fd_info.size.QuadPart == 0) { | |
| 640 fd_info.mapping = INVALID_HANDLE_VALUE; | |
| 641 } else { | |
| 642 DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | | |
| 643 UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE; | |
| 644 fd_info.mapping = CreateFileMapping(file, | |
| 645 NULL, | |
| 646 flProtect, | |
| 647 fd_info.size.HighPart, | |
| 648 fd_info.size.LowPart, | |
| 649 NULL); | |
| 650 if (fd_info.mapping == NULL) { | |
| 651 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 652 CloseHandle(file); | |
| 653 return; | |
| 654 } | |
| 655 } | |
| 656 } | |
| 657 | |
| 658 uv__fd_hash_add(fd, &fd_info); | |
| 659 } | |
| 660 | |
| 661 SET_REQ_RESULT(req, fd); | |
| 662 return; | |
| 663 | |
| 664 einval: | |
| 665 SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); | |
| 666 } | |
| 667 | |
| 668 void fs__close(uv_fs_t* req) { | |
| 669 int fd = req->file.fd; | |
| 670 int result; | |
| 671 struct uv__fd_info_s fd_info; | |
| 672 | |
| 673 VERIFY_FD(fd, req); | |
| 674 | |
| 675 if (uv__fd_hash_remove(fd, &fd_info)) { | |
| 676 if (fd_info.mapping != INVALID_HANDLE_VALUE) { | |
| 677 CloseHandle(fd_info.mapping); | |
| 678 } | |
| 679 } | |
| 680 | |
| 681 if (fd > 2) | |
| 682 result = _close(fd); | |
| 683 else | |
| 684 result = 0; | |
| 685 | |
| 686 /* _close doesn't set _doserrno on failure, but it does always set errno | |
| 687 * to EBADF on failure. | |
| 688 */ | |
| 689 if (result == -1) { | |
| 690 assert(errno == EBADF); | |
| 691 SET_REQ_UV_ERROR(req, UV_EBADF, ERROR_INVALID_HANDLE); | |
| 692 } else { | |
| 693 SET_REQ_RESULT(req, 0); | |
| 694 } | |
| 695 } | |
| 696 | |
| 697 | |
| 698 LONG fs__filemap_ex_filter(LONG excode, PEXCEPTION_POINTERS pep, | |
| 699 int* perror) { | |
| 700 if (excode != (LONG)EXCEPTION_IN_PAGE_ERROR) { | |
| 701 return EXCEPTION_CONTINUE_SEARCH; | |
| 702 } | |
| 703 | |
| 704 assert(perror != NULL); | |
| 705 if (pep != NULL && pep->ExceptionRecord != NULL && | |
| 706 pep->ExceptionRecord->NumberParameters >= 3) { | |
| 707 NTSTATUS status = (NTSTATUS)pep->ExceptionRecord->ExceptionInformation[3]; | |
| 708 *perror = pRtlNtStatusToDosError(status); | |
| 709 if (*perror != ERROR_SUCCESS) { | |
| 710 return EXCEPTION_EXECUTE_HANDLER; | |
| 711 } | |
| 712 } | |
| 713 *perror = UV_UNKNOWN; | |
| 714 return EXCEPTION_EXECUTE_HANDLER; | |
| 715 } | |
| 716 | |
| 717 | |
| 718 void fs__read_filemap(uv_fs_t* req, struct uv__fd_info_s* fd_info) { | |
| 719 int fd = req->file.fd; /* VERIFY_FD done in fs__read */ | |
| 720 int rw_flags = fd_info->flags & | |
| 721 (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); | |
| 722 size_t read_size, done_read; | |
| 723 unsigned int index; | |
| 724 LARGE_INTEGER pos, end_pos; | |
| 725 size_t view_offset; | |
| 726 LARGE_INTEGER view_base; | |
| 727 void* view; | |
| 728 | |
| 729 if (rw_flags == UV_FS_O_WRONLY) { | |
| 730 SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FLAGS); | |
| 731 return; | |
| 732 } | |
| 733 if (fd_info->is_directory) { | |
| 734 SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION); | |
| 735 return; | |
| 736 } | |
| 737 | |
| 738 if (req->fs.info.offset == -1) { | |
| 739 pos = fd_info->current_pos; | |
| 740 } else { | |
| 741 pos.QuadPart = req->fs.info.offset; | |
| 742 } | |
| 743 | |
| 744 /* Make sure we wont read past EOF. */ | |
| 745 if (pos.QuadPart >= fd_info->size.QuadPart) { | |
| 746 SET_REQ_RESULT(req, 0); | |
| 747 return; | |
| 748 } | |
| 749 | |
| 750 read_size = 0; | |
| 751 for (index = 0; index < req->fs.info.nbufs; ++index) { | |
| 752 read_size += req->fs.info.bufs[index].len; | |
| 753 } | |
| 754 read_size = (size_t) MIN((LONGLONG) read_size, | |
| 755 fd_info->size.QuadPart - pos.QuadPart); | |
| 756 if (read_size == 0) { | |
| 757 SET_REQ_RESULT(req, 0); | |
| 758 return; | |
| 759 } | |
| 760 | |
| 761 end_pos.QuadPart = pos.QuadPart + read_size; | |
| 762 | |
| 763 view_offset = pos.QuadPart % uv__allocation_granularity; | |
| 764 view_base.QuadPart = pos.QuadPart - view_offset; | |
| 765 view = MapViewOfFile(fd_info->mapping, | |
| 766 FILE_MAP_READ, | |
| 767 view_base.HighPart, | |
| 768 view_base.LowPart, | |
| 769 view_offset + read_size); | |
| 770 if (view == NULL) { | |
| 771 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 772 return; | |
| 773 } | |
| 774 | |
| 775 done_read = 0; | |
| 776 for (index = 0; | |
| 777 index < req->fs.info.nbufs && done_read < read_size; | |
| 778 ++index) { | |
| 779 size_t this_read_size = MIN(req->fs.info.bufs[index].len, | |
| 780 read_size - done_read); | |
| 781 #ifdef _MSC_VER | |
| 782 int err = 0; | |
| 783 __try { | |
| 784 #endif | |
| 785 memcpy(req->fs.info.bufs[index].base, | |
| 786 (char*)view + view_offset + done_read, | |
| 787 this_read_size); | |
| 788 #ifdef _MSC_VER | |
| 789 } | |
| 790 __except (fs__filemap_ex_filter(GetExceptionCode(), | |
| 791 GetExceptionInformation(), &err)) { | |
| 792 SET_REQ_WIN32_ERROR(req, err); | |
| 793 UnmapViewOfFile(view); | |
| 794 return; | |
| 795 } | |
| 796 #endif | |
| 797 done_read += this_read_size; | |
| 798 } | |
| 799 assert(done_read == read_size); | |
| 800 | |
| 801 if (!UnmapViewOfFile(view)) { | |
| 802 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 803 return; | |
| 804 } | |
| 805 | |
| 806 if (req->fs.info.offset == -1) { | |
| 807 fd_info->current_pos = end_pos; | |
| 808 uv__fd_hash_add(fd, fd_info); | |
| 809 } | |
| 810 | |
| 811 SET_REQ_RESULT(req, read_size); | |
| 812 return; | |
| 813 } | |
| 814 | |
| 815 void fs__read(uv_fs_t* req) { | |
| 816 int fd = req->file.fd; | |
| 817 int64_t offset = req->fs.info.offset; | |
| 818 HANDLE handle; | |
| 819 OVERLAPPED overlapped, *overlapped_ptr; | |
| 820 LARGE_INTEGER offset_; | |
| 821 DWORD bytes; | |
| 822 DWORD error; | |
| 823 int result; | |
| 824 unsigned int index; | |
| 825 LARGE_INTEGER original_position; | |
| 826 LARGE_INTEGER zero_offset; | |
| 827 int restore_position; | |
| 828 struct uv__fd_info_s fd_info; | |
| 829 | |
| 830 VERIFY_FD(fd, req); | |
| 831 | |
| 832 if (uv__fd_hash_get(fd, &fd_info)) { | |
| 833 fs__read_filemap(req, &fd_info); | |
| 834 return; | |
| 835 } | |
| 836 | |
| 837 zero_offset.QuadPart = 0; | |
| 838 restore_position = 0; | |
| 839 handle = uv__get_osfhandle(fd); | |
| 840 | |
| 841 if (handle == INVALID_HANDLE_VALUE) { | |
| 842 SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE); | |
| 843 return; | |
| 844 } | |
| 845 | |
| 846 if (offset != -1) { | |
| 847 memset(&overlapped, 0, sizeof overlapped); | |
| 848 overlapped_ptr = &overlapped; | |
| 849 if (SetFilePointerEx(handle, zero_offset, &original_position, | |
| 850 FILE_CURRENT)) { | |
| 851 restore_position = 1; | |
| 852 } | |
| 853 } else { | |
| 854 overlapped_ptr = NULL; | |
| 855 } | |
| 856 | |
| 857 index = 0; | |
| 858 bytes = 0; | |
| 859 do { | |
| 860 DWORD incremental_bytes; | |
| 861 | |
| 862 if (offset != -1) { | |
| 863 offset_.QuadPart = offset + bytes; | |
| 864 overlapped.Offset = offset_.LowPart; | |
| 865 overlapped.OffsetHigh = offset_.HighPart; | |
| 866 } | |
| 867 | |
| 868 result = ReadFile(handle, | |
| 869 req->fs.info.bufs[index].base, | |
| 870 req->fs.info.bufs[index].len, | |
| 871 &incremental_bytes, | |
| 872 overlapped_ptr); | |
| 873 bytes += incremental_bytes; | |
| 874 ++index; | |
| 875 } while (result && index < req->fs.info.nbufs); | |
| 876 | |
| 877 if (restore_position) | |
| 878 SetFilePointerEx(handle, original_position, NULL, FILE_BEGIN); | |
| 879 | |
| 880 if (result || bytes > 0) { | |
| 881 SET_REQ_RESULT(req, bytes); | |
| 882 } else { | |
| 883 error = GetLastError(); | |
| 884 if (error == ERROR_ACCESS_DENIED) { | |
| 885 error = ERROR_INVALID_FLAGS; | |
| 886 } | |
| 887 | |
| 888 if (error == ERROR_HANDLE_EOF || error == ERROR_BROKEN_PIPE) { | |
| 889 SET_REQ_RESULT(req, bytes); | |
| 890 } else { | |
| 891 SET_REQ_WIN32_ERROR(req, error); | |
| 892 } | |
| 893 } | |
| 894 } | |
| 895 | |
| 896 | |
| 897 void fs__write_filemap(uv_fs_t* req, HANDLE file, | |
| 898 struct uv__fd_info_s* fd_info) { | |
| 899 int fd = req->file.fd; /* VERIFY_FD done in fs__write */ | |
| 900 int force_append = fd_info->flags & UV_FS_O_APPEND; | |
| 901 int rw_flags = fd_info->flags & | |
| 902 (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); | |
| 903 size_t write_size, done_write; | |
| 904 unsigned int index; | |
| 905 LARGE_INTEGER pos, end_pos; | |
| 906 size_t view_offset; | |
| 907 LARGE_INTEGER view_base; | |
| 908 void* view; | |
| 909 FILETIME ft; | |
| 910 | |
| 911 if (rw_flags == UV_FS_O_RDONLY) { | |
| 912 SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FLAGS); | |
| 913 return; | |
| 914 } | |
| 915 if (fd_info->is_directory) { | |
| 916 SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION); | |
| 917 return; | |
| 918 } | |
| 919 | |
| 920 write_size = 0; | |
| 921 for (index = 0; index < req->fs.info.nbufs; ++index) { | |
| 922 write_size += req->fs.info.bufs[index].len; | |
| 923 } | |
| 924 | |
| 925 if (write_size == 0) { | |
| 926 SET_REQ_RESULT(req, 0); | |
| 927 return; | |
| 928 } | |
| 929 | |
| 930 if (force_append) { | |
| 931 pos = fd_info->size; | |
| 932 } else if (req->fs.info.offset == -1) { | |
| 933 pos = fd_info->current_pos; | |
| 934 } else { | |
| 935 pos.QuadPart = req->fs.info.offset; | |
| 936 } | |
| 937 | |
| 938 end_pos.QuadPart = pos.QuadPart + write_size; | |
| 939 | |
| 940 /* Recreate the mapping to enlarge the file if needed */ | |
| 941 if (end_pos.QuadPart > fd_info->size.QuadPart) { | |
| 942 if (fd_info->mapping != INVALID_HANDLE_VALUE) { | |
| 943 CloseHandle(fd_info->mapping); | |
| 944 } | |
| 945 | |
| 946 fd_info->mapping = CreateFileMapping(file, | |
| 947 NULL, | |
| 948 PAGE_READWRITE, | |
| 949 end_pos.HighPart, | |
| 950 end_pos.LowPart, | |
| 951 NULL); | |
| 952 if (fd_info->mapping == NULL) { | |
| 953 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 954 CloseHandle(file); | |
| 955 fd_info->mapping = INVALID_HANDLE_VALUE; | |
| 956 fd_info->size.QuadPart = 0; | |
| 957 fd_info->current_pos.QuadPart = 0; | |
| 958 uv__fd_hash_add(fd, fd_info); | |
| 959 return; | |
| 960 } | |
| 961 | |
| 962 fd_info->size = end_pos; | |
| 963 uv__fd_hash_add(fd, fd_info); | |
| 964 } | |
| 965 | |
| 966 view_offset = pos.QuadPart % uv__allocation_granularity; | |
| 967 view_base.QuadPart = pos.QuadPart - view_offset; | |
| 968 view = MapViewOfFile(fd_info->mapping, | |
| 969 FILE_MAP_WRITE, | |
| 970 view_base.HighPart, | |
| 971 view_base.LowPart, | |
| 972 view_offset + write_size); | |
| 973 if (view == NULL) { | |
| 974 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 975 return; | |
| 976 } | |
| 977 | |
| 978 done_write = 0; | |
| 979 for (index = 0; index < req->fs.info.nbufs; ++index) { | |
| 980 #ifdef _MSC_VER | |
| 981 int err = 0; | |
| 982 __try { | |
| 983 #endif | |
| 984 memcpy((char*)view + view_offset + done_write, | |
| 985 req->fs.info.bufs[index].base, | |
| 986 req->fs.info.bufs[index].len); | |
| 987 #ifdef _MSC_VER | |
| 988 } | |
| 989 __except (fs__filemap_ex_filter(GetExceptionCode(), | |
| 990 GetExceptionInformation(), &err)) { | |
| 991 SET_REQ_WIN32_ERROR(req, err); | |
| 992 UnmapViewOfFile(view); | |
| 993 return; | |
| 994 } | |
| 995 #endif | |
| 996 done_write += req->fs.info.bufs[index].len; | |
| 997 } | |
| 998 assert(done_write == write_size); | |
| 999 | |
| 1000 if (!FlushViewOfFile(view, 0)) { | |
| 1001 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 1002 UnmapViewOfFile(view); | |
| 1003 return; | |
| 1004 } | |
| 1005 if (!UnmapViewOfFile(view)) { | |
| 1006 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 1007 return; | |
| 1008 } | |
| 1009 | |
| 1010 if (req->fs.info.offset == -1) { | |
| 1011 fd_info->current_pos = end_pos; | |
| 1012 uv__fd_hash_add(fd, fd_info); | |
| 1013 } | |
| 1014 | |
| 1015 GetSystemTimeAsFileTime(&ft); | |
| 1016 SetFileTime(file, NULL, NULL, &ft); | |
| 1017 | |
| 1018 SET_REQ_RESULT(req, done_write); | |
| 1019 } | |
| 1020 | |
| 1021 void fs__write(uv_fs_t* req) { | |
| 1022 int fd = req->file.fd; | |
| 1023 int64_t offset = req->fs.info.offset; | |
| 1024 HANDLE handle; | |
| 1025 OVERLAPPED overlapped, *overlapped_ptr; | |
| 1026 LARGE_INTEGER offset_; | |
| 1027 DWORD bytes; | |
| 1028 DWORD error; | |
| 1029 int result; | |
| 1030 unsigned int index; | |
| 1031 LARGE_INTEGER original_position; | |
| 1032 LARGE_INTEGER zero_offset; | |
| 1033 int restore_position; | |
| 1034 struct uv__fd_info_s fd_info; | |
| 1035 | |
| 1036 VERIFY_FD(fd, req); | |
| 1037 | |
| 1038 zero_offset.QuadPart = 0; | |
| 1039 restore_position = 0; | |
| 1040 handle = uv__get_osfhandle(fd); | |
| 1041 if (handle == INVALID_HANDLE_VALUE) { | |
| 1042 SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE); | |
| 1043 return; | |
| 1044 } | |
| 1045 | |
| 1046 if (uv__fd_hash_get(fd, &fd_info)) { | |
| 1047 fs__write_filemap(req, handle, &fd_info); | |
| 1048 return; | |
| 1049 } | |
| 1050 | |
| 1051 if (offset != -1) { | |
| 1052 memset(&overlapped, 0, sizeof overlapped); | |
| 1053 overlapped_ptr = &overlapped; | |
| 1054 if (SetFilePointerEx(handle, zero_offset, &original_position, | |
| 1055 FILE_CURRENT)) { | |
| 1056 restore_position = 1; | |
| 1057 } | |
| 1058 } else { | |
| 1059 overlapped_ptr = NULL; | |
| 1060 } | |
| 1061 | |
| 1062 index = 0; | |
| 1063 bytes = 0; | |
| 1064 do { | |
| 1065 DWORD incremental_bytes; | |
| 1066 | |
| 1067 if (offset != -1) { | |
| 1068 offset_.QuadPart = offset + bytes; | |
| 1069 overlapped.Offset = offset_.LowPart; | |
| 1070 overlapped.OffsetHigh = offset_.HighPart; | |
| 1071 } | |
| 1072 | |
| 1073 result = WriteFile(handle, | |
| 1074 req->fs.info.bufs[index].base, | |
| 1075 req->fs.info.bufs[index].len, | |
| 1076 &incremental_bytes, | |
| 1077 overlapped_ptr); | |
| 1078 bytes += incremental_bytes; | |
| 1079 ++index; | |
| 1080 } while (result && index < req->fs.info.nbufs); | |
| 1081 | |
| 1082 if (restore_position) | |
| 1083 SetFilePointerEx(handle, original_position, NULL, FILE_BEGIN); | |
| 1084 | |
| 1085 if (result || bytes > 0) { | |
| 1086 SET_REQ_RESULT(req, bytes); | |
| 1087 } else { | |
| 1088 error = GetLastError(); | |
| 1089 | |
| 1090 if (error == ERROR_ACCESS_DENIED) { | |
| 1091 error = ERROR_INVALID_FLAGS; | |
| 1092 } | |
| 1093 | |
| 1094 SET_REQ_UV_ERROR(req, uv_translate_write_sys_error(error), error); | |
| 1095 } | |
| 1096 } | |
| 1097 | |
| 1098 | |
| 1099 static void fs__unlink_rmdir(uv_fs_t* req, BOOL isrmdir) { | |
| 1100 const WCHAR* pathw = req->file.pathw; | |
| 1101 HANDLE handle; | |
| 1102 BY_HANDLE_FILE_INFORMATION info; | |
| 1103 FILE_DISPOSITION_INFORMATION disposition; | |
| 1104 FILE_DISPOSITION_INFORMATION_EX disposition_ex; | |
| 1105 IO_STATUS_BLOCK iosb; | |
| 1106 NTSTATUS status; | |
| 1107 DWORD error; | |
| 1108 | |
| 1109 handle = CreateFileW(pathw, | |
| 1110 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE, | |
| 1111 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
| 1112 NULL, | |
| 1113 OPEN_EXISTING, | |
| 1114 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, | |
| 1115 NULL); | |
| 1116 | |
| 1117 if (handle == INVALID_HANDLE_VALUE) { | |
| 1118 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 1119 return; | |
| 1120 } | |
| 1121 | |
| 1122 if (!GetFileInformationByHandle(handle, &info)) { | |
| 1123 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 1124 CloseHandle(handle); | |
| 1125 return; | |
| 1126 } | |
| 1127 | |
| 1128 if (isrmdir && !(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { | |
| 1129 /* Error if we're in rmdir mode but it is not a dir. | |
| 1130 * TODO: change it to UV_NOTDIR in v2. */ | |
| 1131 SET_REQ_UV_ERROR(req, UV_ENOENT, ERROR_DIRECTORY); | |
| 1132 CloseHandle(handle); | |
| 1133 return; | |
| 1134 } | |
| 1135 | |
| 1136 if (!isrmdir && (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { | |
| 1137 /* If not explicitly allowed, do not allow deletion of directories, unless | |
| 1138 * it is a symlink. When the path refers to a non-symlink directory, report | |
| 1139 * EPERM as mandated by POSIX.1. */ | |
| 1140 | |
| 1141 /* Check if it is a reparse point. If it's not, it's a normal directory. */ | |
| 1142 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { | |
| 1143 SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); | |
| 1144 CloseHandle(handle); | |
| 1145 return; | |
| 1146 } | |
| 1147 | |
| 1148 /* Read the reparse point and check if it is a valid symlink. If not, don't | |
| 1149 * unlink. */ | |
| 1150 if (fs__readlink_handle(handle, NULL, NULL) < 0) { | |
| 1151 error = GetLastError(); | |
| 1152 if (error == ERROR_SYMLINK_NOT_SUPPORTED) | |
| 1153 error = ERROR_ACCESS_DENIED; | |
| 1154 SET_REQ_WIN32_ERROR(req, error); | |
| 1155 CloseHandle(handle); | |
| 1156 return; | |
| 1157 } | |
| 1158 } | |
| 1159 | |
| 1160 /* Try posix delete first */ | |
| 1161 disposition_ex.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS | | |
| 1162 FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE; | |
| 1163 | |
| 1164 status = pNtSetInformationFile(handle, | |
| 1165 &iosb, | |
| 1166 &disposition_ex, | |
| 1167 sizeof disposition_ex, | |
| 1168 FileDispositionInformationEx); | |
| 1169 if (NT_SUCCESS(status)) { | |
| 1170 SET_REQ_SUCCESS(req); | |
| 1171 } else { | |
| 1172 /* If status == STATUS_CANNOT_DELETE here, given we set | |
| 1173 * FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE, STATUS_CANNOT_DELETE can only mean | |
| 1174 * that there is an existing mapped view to the file, preventing delete. | |
| 1175 * STATUS_CANNOT_DELETE maps to UV_EACCES so it's not specifically worth handling */ | |
| 1176 error = pRtlNtStatusToDosError(status); | |
| 1177 if (error == ERROR_NOT_SUPPORTED /* filesystem does not support posix deletion */ || | |
| 1178 error == ERROR_INVALID_PARAMETER /* pre Windows 10 error */ || | |
| 1179 error == ERROR_INVALID_FUNCTION /* pre Windows 10 1607 error */) { | |
| 1180 /* posix delete not supported so try fallback */ | |
| 1181 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { | |
| 1182 /* Remove read-only attribute */ | |
| 1183 FILE_BASIC_INFORMATION basic = { 0 }; | |
| 1184 | |
| 1185 basic.FileAttributes = (info.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) | | |
| 1186 FILE_ATTRIBUTE_ARCHIVE; | |
| 1187 | |
| 1188 status = pNtSetInformationFile(handle, | |
| 1189 &iosb, | |
| 1190 &basic, | |
| 1191 sizeof basic, | |
| 1192 FileBasicInformation); | |
| 1193 if (!NT_SUCCESS(status)) { | |
| 1194 SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); | |
| 1195 CloseHandle(handle); | |
| 1196 return; | |
| 1197 } | |
| 1198 } | |
| 1199 | |
| 1200 /* Try to set the delete flag. */ | |
| 1201 disposition.DeleteFile = TRUE; | |
| 1202 status = pNtSetInformationFile(handle, | |
| 1203 &iosb, | |
| 1204 &disposition, | |
| 1205 sizeof disposition, | |
| 1206 FileDispositionInformation); | |
| 1207 if (NT_SUCCESS(status)) { | |
| 1208 SET_REQ_SUCCESS(req); | |
| 1209 } else { | |
| 1210 SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); | |
| 1211 } | |
| 1212 } else { | |
| 1213 SET_REQ_WIN32_ERROR(req, error); | |
| 1214 } | |
| 1215 } | |
| 1216 | |
| 1217 CloseHandle(handle); | |
| 1218 } | |
| 1219 | |
| 1220 | |
| 1221 static void fs__rmdir(uv_fs_t* req) { | |
| 1222 fs__unlink_rmdir(req, /*isrmdir*/1); | |
| 1223 } | |
| 1224 | |
| 1225 | |
| 1226 static void fs__unlink(uv_fs_t* req) { | |
| 1227 fs__unlink_rmdir(req, /*isrmdir*/0); | |
| 1228 } | |
| 1229 | |
| 1230 | |
| 1231 void fs__mkdir(uv_fs_t* req) { | |
| 1232 /* TODO: use req->mode. */ | |
| 1233 if (CreateDirectoryW(req->file.pathw, NULL)) { | |
| 1234 SET_REQ_RESULT(req, 0); | |
| 1235 } else { | |
| 1236 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 1237 if (req->sys_errno_ == ERROR_INVALID_NAME || | |
| 1238 req->sys_errno_ == ERROR_DIRECTORY) | |
| 1239 req->result = UV_EINVAL; | |
| 1240 } | |
| 1241 } | |
| 1242 | |
| 1243 typedef int (*uv__fs_mktemp_func)(uv_fs_t* req); | |
| 1244 | |
| 1245 /* OpenBSD original: lib/libc/stdio/mktemp.c */ | |
| 1246 void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) { | |
| 1247 static const WCHAR *tempchars = | |
| 1248 L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; | |
| 1249 static const size_t num_chars = 62; | |
| 1250 static const size_t num_x = 6; | |
| 1251 WCHAR *cp, *ep; | |
| 1252 unsigned int tries, i; | |
| 1253 size_t len; | |
| 1254 uint64_t v; | |
| 1255 char* path; | |
| 1256 | |
| 1257 path = (char*)req->path; | |
| 1258 len = wcslen(req->file.pathw); | |
| 1259 ep = req->file.pathw + len; | |
| 1260 if (len < num_x || wcsncmp(ep - num_x, L"XXXXXX", num_x)) { | |
| 1261 SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); | |
| 1262 goto clobber; | |
| 1263 } | |
| 1264 | |
| 1265 tries = TMP_MAX; | |
| 1266 do { | |
| 1267 if (uv__random_rtlgenrandom((void *)&v, sizeof(v)) < 0) { | |
| 1268 SET_REQ_UV_ERROR(req, UV_EIO, ERROR_IO_DEVICE); | |
| 1269 goto clobber; | |
| 1270 } | |
| 1271 | |
| 1272 cp = ep - num_x; | |
| 1273 for (i = 0; i < num_x; i++) { | |
| 1274 *cp++ = tempchars[v % num_chars]; | |
| 1275 v /= num_chars; | |
| 1276 } | |
| 1277 | |
| 1278 if (func(req)) { | |
| 1279 if (req->result >= 0) { | |
| 1280 len = strlen(path); | |
| 1281 wcstombs(path + len - num_x, ep - num_x, num_x); | |
| 1282 } | |
| 1283 return; | |
| 1284 } | |
| 1285 } while (--tries); | |
| 1286 | |
| 1287 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 1288 | |
| 1289 clobber: | |
| 1290 path[0] = '\0'; | |
| 1291 } | |
| 1292 | |
| 1293 | |
| 1294 static int fs__mkdtemp_func(uv_fs_t* req) { | |
| 1295 DWORD error; | |
| 1296 if (CreateDirectoryW(req->file.pathw, NULL)) { | |
| 1297 SET_REQ_RESULT(req, 0); | |
| 1298 return 1; | |
| 1299 } | |
| 1300 error = GetLastError(); | |
| 1301 if (error != ERROR_ALREADY_EXISTS) { | |
| 1302 SET_REQ_WIN32_ERROR(req, error); | |
| 1303 return 1; | |
| 1304 } | |
| 1305 | |
| 1306 return 0; | |
| 1307 } | |
| 1308 | |
| 1309 | |
| 1310 void fs__mkdtemp(uv_fs_t* req) { | |
| 1311 fs__mktemp(req, fs__mkdtemp_func); | |
| 1312 } | |
| 1313 | |
| 1314 | |
| 1315 static int fs__mkstemp_func(uv_fs_t* req) { | |
| 1316 HANDLE file; | |
| 1317 int fd; | |
| 1318 | |
| 1319 file = CreateFileW(req->file.pathw, | |
| 1320 GENERIC_READ | GENERIC_WRITE, | |
| 1321 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
| 1322 NULL, | |
| 1323 CREATE_NEW, | |
| 1324 FILE_ATTRIBUTE_NORMAL, | |
| 1325 NULL); | |
| 1326 | |
| 1327 if (file == INVALID_HANDLE_VALUE) { | |
| 1328 DWORD error; | |
| 1329 error = GetLastError(); | |
| 1330 | |
| 1331 /* If the file exists, the main fs__mktemp() function | |
| 1332 will retry. If it's another error, we want to stop. */ | |
| 1333 if (error != ERROR_FILE_EXISTS) { | |
| 1334 SET_REQ_WIN32_ERROR(req, error); | |
| 1335 return 1; | |
| 1336 } | |
| 1337 | |
| 1338 return 0; | |
| 1339 } | |
| 1340 | |
| 1341 fd = _open_osfhandle((intptr_t) file, 0); | |
| 1342 if (fd < 0) { | |
| 1343 /* The only known failure mode for _open_osfhandle() is EMFILE, in which | |
| 1344 * case GetLastError() will return zero. However we'll try to handle other | |
| 1345 * errors as well, should they ever occur. | |
| 1346 */ | |
| 1347 if (errno == EMFILE) | |
| 1348 SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES); | |
| 1349 else if (GetLastError() != ERROR_SUCCESS) | |
| 1350 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 1351 else | |
| 1352 SET_REQ_WIN32_ERROR(req, UV_UNKNOWN); | |
| 1353 CloseHandle(file); | |
| 1354 return 1; | |
| 1355 } | |
| 1356 | |
| 1357 SET_REQ_RESULT(req, fd); | |
| 1358 | |
| 1359 return 1; | |
| 1360 } | |
| 1361 | |
| 1362 | |
| 1363 void fs__mkstemp(uv_fs_t* req) { | |
| 1364 fs__mktemp(req, fs__mkstemp_func); | |
| 1365 } | |
| 1366 | |
| 1367 | |
| 1368 void fs__scandir(uv_fs_t* req) { | |
| 1369 static const size_t dirents_initial_size = 32; | |
| 1370 | |
| 1371 HANDLE dir_handle = INVALID_HANDLE_VALUE; | |
| 1372 | |
| 1373 uv__dirent_t** dirents = NULL; | |
| 1374 size_t dirents_size = 0; | |
| 1375 size_t dirents_used = 0; | |
| 1376 | |
| 1377 IO_STATUS_BLOCK iosb; | |
| 1378 NTSTATUS status; | |
| 1379 | |
| 1380 /* Buffer to hold directory entries returned by NtQueryDirectoryFile. | |
| 1381 * It's important that this buffer can hold at least one entry, regardless | |
| 1382 * of the length of the file names present in the enumerated directory. | |
| 1383 * A file name is at most 256 WCHARs long. | |
| 1384 * According to MSDN, the buffer must be aligned at an 8-byte boundary. | |
| 1385 */ | |
| 1386 #if _MSC_VER | |
| 1387 __declspec(align(8)) char buffer[8192]; | |
| 1388 #else | |
| 1389 __attribute__ ((aligned (8))) char buffer[8192]; | |
| 1390 #endif | |
| 1391 | |
| 1392 STATIC_ASSERT(sizeof buffer >= | |
| 1393 sizeof(FILE_DIRECTORY_INFORMATION) + 256 * sizeof(WCHAR)); | |
| 1394 | |
| 1395 /* Open the directory. */ | |
| 1396 dir_handle = | |
| 1397 CreateFileW(req->file.pathw, | |
| 1398 FILE_LIST_DIRECTORY | SYNCHRONIZE, | |
| 1399 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
| 1400 NULL, | |
| 1401 OPEN_EXISTING, | |
| 1402 FILE_FLAG_BACKUP_SEMANTICS, | |
| 1403 NULL); | |
| 1404 if (dir_handle == INVALID_HANDLE_VALUE) | |
| 1405 goto win32_error; | |
| 1406 | |
| 1407 /* Read the first chunk. */ | |
| 1408 status = pNtQueryDirectoryFile(dir_handle, | |
| 1409 NULL, | |
| 1410 NULL, | |
| 1411 NULL, | |
| 1412 &iosb, | |
| 1413 &buffer, | |
| 1414 sizeof buffer, | |
| 1415 FileDirectoryInformation, | |
| 1416 FALSE, | |
| 1417 NULL, | |
| 1418 TRUE); | |
| 1419 | |
| 1420 /* If the handle is not a directory, we'll get STATUS_INVALID_PARAMETER. | |
| 1421 * This should be reported back as UV_ENOTDIR. | |
| 1422 */ | |
| 1423 if (status == (NTSTATUS)STATUS_INVALID_PARAMETER) | |
| 1424 goto not_a_directory_error; | |
| 1425 | |
| 1426 while (NT_SUCCESS(status)) { | |
| 1427 char* position = buffer; | |
| 1428 size_t next_entry_offset = 0; | |
| 1429 | |
| 1430 do { | |
| 1431 FILE_DIRECTORY_INFORMATION* info; | |
| 1432 uv__dirent_t* dirent; | |
| 1433 | |
| 1434 size_t wchar_len; | |
| 1435 size_t wtf8_len; | |
| 1436 char* wtf8; | |
| 1437 | |
| 1438 /* Obtain a pointer to the current directory entry. */ | |
| 1439 position += next_entry_offset; | |
| 1440 info = (FILE_DIRECTORY_INFORMATION*) position; | |
| 1441 | |
| 1442 /* Fetch the offset to the next directory entry. */ | |
| 1443 next_entry_offset = info->NextEntryOffset; | |
| 1444 | |
| 1445 /* Compute the length of the filename in WCHARs. */ | |
| 1446 wchar_len = info->FileNameLength / sizeof info->FileName[0]; | |
| 1447 | |
| 1448 /* Skip over '.' and '..' entries. It has been reported that | |
| 1449 * the SharePoint driver includes the terminating zero byte in | |
| 1450 * the filename length. Strip those first. | |
| 1451 */ | |
| 1452 while (wchar_len > 0 && info->FileName[wchar_len - 1] == L'\0') | |
| 1453 wchar_len -= 1; | |
| 1454 | |
| 1455 if (wchar_len == 0) | |
| 1456 continue; | |
| 1457 if (wchar_len == 1 && info->FileName[0] == L'.') | |
| 1458 continue; | |
| 1459 if (wchar_len == 2 && info->FileName[0] == L'.' && | |
| 1460 info->FileName[1] == L'.') | |
| 1461 continue; | |
| 1462 | |
| 1463 /* Compute the space required to store the filename as WTF-8. */ | |
| 1464 wtf8_len = uv_utf16_length_as_wtf8(&info->FileName[0], wchar_len); | |
| 1465 | |
| 1466 /* Resize the dirent array if needed. */ | |
| 1467 if (dirents_used >= dirents_size) { | |
| 1468 size_t new_dirents_size = | |
| 1469 dirents_size == 0 ? dirents_initial_size : dirents_size << 1; | |
| 1470 uv__dirent_t** new_dirents = | |
| 1471 uv__realloc(dirents, new_dirents_size * sizeof *dirents); | |
| 1472 | |
| 1473 if (new_dirents == NULL) | |
| 1474 goto out_of_memory_error; | |
| 1475 | |
| 1476 dirents_size = new_dirents_size; | |
| 1477 dirents = new_dirents; | |
| 1478 } | |
| 1479 | |
| 1480 /* Allocate space for the uv dirent structure. The dirent structure | |
| 1481 * includes room for the first character of the filename, but `utf8_len` | |
| 1482 * doesn't count the NULL terminator at this point. | |
| 1483 */ | |
| 1484 dirent = uv__malloc(sizeof *dirent + wtf8_len); | |
| 1485 if (dirent == NULL) | |
| 1486 goto out_of_memory_error; | |
| 1487 | |
| 1488 dirents[dirents_used++] = dirent; | |
| 1489 | |
| 1490 /* Convert file name to UTF-8. */ | |
| 1491 wtf8 = &dirent->d_name[0]; | |
| 1492 if (uv_utf16_to_wtf8(&info->FileName[0], wchar_len, &wtf8, &wtf8_len) != 0) | |
| 1493 goto out_of_memory_error; | |
| 1494 | |
| 1495 /* Fill out the type field. */ | |
| 1496 if (info->FileAttributes & FILE_ATTRIBUTE_DEVICE) | |
| 1497 dirent->d_type = UV__DT_CHAR; | |
| 1498 else if (info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) | |
| 1499 dirent->d_type = UV__DT_LINK; | |
| 1500 else if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) | |
| 1501 dirent->d_type = UV__DT_DIR; | |
| 1502 else | |
| 1503 dirent->d_type = UV__DT_FILE; | |
| 1504 } while (next_entry_offset != 0); | |
| 1505 | |
| 1506 /* Read the next chunk. */ | |
| 1507 status = pNtQueryDirectoryFile(dir_handle, | |
| 1508 NULL, | |
| 1509 NULL, | |
| 1510 NULL, | |
| 1511 &iosb, | |
| 1512 &buffer, | |
| 1513 sizeof buffer, | |
| 1514 FileDirectoryInformation, | |
| 1515 FALSE, | |
| 1516 NULL, | |
| 1517 FALSE); | |
| 1518 | |
| 1519 /* After the first pNtQueryDirectoryFile call, the function may return | |
| 1520 * STATUS_SUCCESS even if the buffer was too small to hold at least one | |
| 1521 * directory entry. | |
| 1522 */ | |
| 1523 if (status == STATUS_SUCCESS && iosb.Information == 0) | |
| 1524 status = STATUS_BUFFER_OVERFLOW; | |
| 1525 } | |
| 1526 | |
| 1527 if (status != STATUS_NO_MORE_FILES) | |
| 1528 goto nt_error; | |
| 1529 | |
| 1530 CloseHandle(dir_handle); | |
| 1531 | |
| 1532 /* Store the result in the request object. */ | |
| 1533 req->ptr = dirents; | |
| 1534 if (dirents != NULL) | |
| 1535 req->flags |= UV_FS_FREE_PTR; | |
| 1536 | |
| 1537 SET_REQ_RESULT(req, dirents_used); | |
| 1538 | |
| 1539 /* `nbufs` will be used as index by uv_fs_scandir_next. */ | |
| 1540 req->fs.info.nbufs = 0; | |
| 1541 | |
| 1542 return; | |
| 1543 | |
| 1544 nt_error: | |
| 1545 SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); | |
| 1546 goto cleanup; | |
| 1547 | |
| 1548 win32_error: | |
| 1549 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 1550 goto cleanup; | |
| 1551 | |
| 1552 not_a_directory_error: | |
| 1553 SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY); | |
| 1554 goto cleanup; | |
| 1555 | |
| 1556 out_of_memory_error: | |
| 1557 SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); | |
| 1558 goto cleanup; | |
| 1559 | |
| 1560 cleanup: | |
| 1561 if (dir_handle != INVALID_HANDLE_VALUE) | |
| 1562 CloseHandle(dir_handle); | |
| 1563 while (dirents_used > 0) | |
| 1564 uv__free(dirents[--dirents_used]); | |
| 1565 if (dirents != NULL) | |
| 1566 uv__free(dirents); | |
| 1567 } | |
| 1568 | |
| 1569 void fs__opendir(uv_fs_t* req) { | |
| 1570 WCHAR* pathw; | |
| 1571 size_t len; | |
| 1572 const WCHAR* fmt; | |
| 1573 WCHAR* find_path; | |
| 1574 uv_dir_t* dir; | |
| 1575 | |
| 1576 pathw = req->file.pathw; | |
| 1577 dir = NULL; | |
| 1578 find_path = NULL; | |
| 1579 | |
| 1580 /* Figure out whether path is a file or a directory. */ | |
| 1581 if (!(GetFileAttributesW(pathw) & FILE_ATTRIBUTE_DIRECTORY)) { | |
| 1582 SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY); | |
| 1583 goto error; | |
| 1584 } | |
| 1585 | |
| 1586 dir = uv__malloc(sizeof(*dir)); | |
| 1587 if (dir == NULL) { | |
| 1588 SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); | |
| 1589 goto error; | |
| 1590 } | |
| 1591 | |
| 1592 len = wcslen(pathw); | |
| 1593 | |
| 1594 if (len == 0) | |
| 1595 fmt = L"./*"; | |
| 1596 else if (IS_SLASH(pathw[len - 1])) | |
| 1597 fmt = L"%s*"; | |
| 1598 else | |
| 1599 fmt = L"%s\\*"; | |
| 1600 | |
| 1601 find_path = uv__malloc(sizeof(WCHAR) * (len + 4)); | |
| 1602 if (find_path == NULL) { | |
| 1603 SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); | |
| 1604 goto error; | |
| 1605 } | |
| 1606 | |
| 1607 _snwprintf(find_path, len + 3, fmt, pathw); | |
| 1608 dir->dir_handle = FindFirstFileW(find_path, &dir->find_data); | |
| 1609 uv__free(find_path); | |
| 1610 find_path = NULL; | |
| 1611 if (dir->dir_handle == INVALID_HANDLE_VALUE && | |
| 1612 GetLastError() != ERROR_FILE_NOT_FOUND) { | |
| 1613 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 1614 goto error; | |
| 1615 } | |
| 1616 | |
| 1617 dir->need_find_call = FALSE; | |
| 1618 req->ptr = dir; | |
| 1619 SET_REQ_RESULT(req, 0); | |
| 1620 return; | |
| 1621 | |
| 1622 error: | |
| 1623 uv__free(dir); | |
| 1624 uv__free(find_path); | |
| 1625 req->ptr = NULL; | |
| 1626 } | |
| 1627 | |
| 1628 void fs__readdir(uv_fs_t* req) { | |
| 1629 uv_dir_t* dir; | |
| 1630 uv_dirent_t* dirents; | |
| 1631 uv__dirent_t dent; | |
| 1632 unsigned int dirent_idx; | |
| 1633 PWIN32_FIND_DATAW find_data; | |
| 1634 unsigned int i; | |
| 1635 int r; | |
| 1636 | |
| 1637 req->flags |= UV_FS_FREE_PTR; | |
| 1638 dir = req->ptr; | |
| 1639 dirents = dir->dirents; | |
| 1640 memset(dirents, 0, dir->nentries * sizeof(*dir->dirents)); | |
| 1641 find_data = &dir->find_data; | |
| 1642 dirent_idx = 0; | |
| 1643 | |
| 1644 while (dirent_idx < dir->nentries) { | |
| 1645 if (dir->need_find_call && FindNextFileW(dir->dir_handle, find_data) == 0) { | |
| 1646 if (GetLastError() == ERROR_NO_MORE_FILES) | |
| 1647 break; | |
| 1648 goto error; | |
| 1649 } | |
| 1650 | |
| 1651 /* Skip "." and ".." entries. */ | |
| 1652 if (find_data->cFileName[0] == L'.' && | |
| 1653 (find_data->cFileName[1] == L'\0' || | |
| 1654 (find_data->cFileName[1] == L'.' && | |
| 1655 find_data->cFileName[2] == L'\0'))) { | |
| 1656 dir->need_find_call = TRUE; | |
| 1657 continue; | |
| 1658 } | |
| 1659 | |
| 1660 r = uv__convert_utf16_to_utf8((const WCHAR*) &find_data->cFileName, | |
| 1661 -1, | |
| 1662 (char**) &dirents[dirent_idx].name); | |
| 1663 if (r != 0) | |
| 1664 goto error; | |
| 1665 | |
| 1666 /* Copy file type. */ | |
| 1667 if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0) | |
| 1668 dent.d_type = UV__DT_CHAR; | |
| 1669 else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) | |
| 1670 dent.d_type = UV__DT_LINK; | |
| 1671 else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) | |
| 1672 dent.d_type = UV__DT_DIR; | |
| 1673 else | |
| 1674 dent.d_type = UV__DT_FILE; | |
| 1675 | |
| 1676 dirents[dirent_idx].type = uv__fs_get_dirent_type(&dent); | |
| 1677 dir->need_find_call = TRUE; | |
| 1678 ++dirent_idx; | |
| 1679 } | |
| 1680 | |
| 1681 SET_REQ_RESULT(req, dirent_idx); | |
| 1682 return; | |
| 1683 | |
| 1684 error: | |
| 1685 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 1686 for (i = 0; i < dirent_idx; ++i) { | |
| 1687 uv__free((char*) dirents[i].name); | |
| 1688 dirents[i].name = NULL; | |
| 1689 } | |
| 1690 } | |
| 1691 | |
| 1692 void fs__closedir(uv_fs_t* req) { | |
| 1693 uv_dir_t* dir; | |
| 1694 | |
| 1695 dir = req->ptr; | |
| 1696 FindClose(dir->dir_handle); | |
| 1697 uv__free(req->ptr); | |
| 1698 SET_REQ_RESULT(req, 0); | |
| 1699 } | |
| 1700 | |
| 1701 INLINE static fs__stat_path_return_t fs__stat_path(WCHAR* path, | |
| 1702 uv_stat_t* statbuf, int do_lstat) { | |
| 1703 FILE_STAT_BASIC_INFORMATION stat_info; | |
| 1704 | |
| 1705 /* Check if the new fast API is available. */ | |
| 1706 if (!pGetFileInformationByName) { | |
| 1707 return FS__STAT_PATH_TRY_SLOW; | |
| 1708 } | |
| 1709 | |
| 1710 /* Check if the API call fails. */ | |
| 1711 if (!pGetFileInformationByName(path, FileStatBasicByNameInfo, &stat_info, | |
| 1712 sizeof(stat_info))) { | |
| 1713 switch(GetLastError()) { | |
| 1714 case ERROR_FILE_NOT_FOUND: | |
| 1715 case ERROR_PATH_NOT_FOUND: | |
| 1716 case ERROR_NOT_READY: | |
| 1717 case ERROR_BAD_NET_NAME: | |
| 1718 /* These errors aren't worth retrying with the slow path. */ | |
| 1719 return FS__STAT_PATH_ERROR; | |
| 1720 } | |
| 1721 return FS__STAT_PATH_TRY_SLOW; | |
| 1722 } | |
| 1723 | |
| 1724 /* A file handle is needed to get st_size for links. */ | |
| 1725 if ((stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { | |
| 1726 return FS__STAT_PATH_TRY_SLOW; | |
| 1727 } | |
| 1728 | |
| 1729 if (stat_info.DeviceType == FILE_DEVICE_NULL) { | |
| 1730 fs__stat_assign_statbuf_null(statbuf); | |
| 1731 return FS__STAT_PATH_SUCCESS; | |
| 1732 } | |
| 1733 | |
| 1734 fs__stat_assign_statbuf(statbuf, stat_info, do_lstat); | |
| 1735 return FS__STAT_PATH_SUCCESS; | |
| 1736 } | |
| 1737 | |
| 1738 INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf, | |
| 1739 int do_lstat) { | |
| 1740 size_t target_length = 0; | |
| 1741 FILE_FS_DEVICE_INFORMATION device_info; | |
| 1742 FILE_ALL_INFORMATION file_info; | |
| 1743 FILE_FS_VOLUME_INFORMATION volume_info; | |
| 1744 NTSTATUS nt_status; | |
| 1745 IO_STATUS_BLOCK io_status; | |
| 1746 FILE_STAT_BASIC_INFORMATION stat_info; | |
| 1747 | |
| 1748 nt_status = pNtQueryVolumeInformationFile(handle, | |
| 1749 &io_status, | |
| 1750 &device_info, | |
| 1751 sizeof device_info, | |
| 1752 FileFsDeviceInformation); | |
| 1753 | |
| 1754 /* Buffer overflow (a warning status code) is expected here. */ | |
| 1755 if (NT_ERROR(nt_status)) { | |
| 1756 SetLastError(pRtlNtStatusToDosError(nt_status)); | |
| 1757 return -1; | |
| 1758 } | |
| 1759 | |
| 1760 /* If it's NUL device set fields as reasonable as possible and return. */ | |
| 1761 if (device_info.DeviceType == FILE_DEVICE_NULL) { | |
| 1762 fs__stat_assign_statbuf_null(statbuf); | |
| 1763 return 0; | |
| 1764 } | |
| 1765 | |
| 1766 nt_status = pNtQueryInformationFile(handle, | |
| 1767 &io_status, | |
| 1768 &file_info, | |
| 1769 sizeof file_info, | |
| 1770 FileAllInformation); | |
| 1771 | |
| 1772 /* Buffer overflow (a warning status code) is expected here. */ | |
| 1773 if (NT_ERROR(nt_status)) { | |
| 1774 SetLastError(pRtlNtStatusToDosError(nt_status)); | |
| 1775 return -1; | |
| 1776 } | |
| 1777 | |
| 1778 nt_status = pNtQueryVolumeInformationFile(handle, | |
| 1779 &io_status, | |
| 1780 &volume_info, | |
| 1781 sizeof volume_info, | |
| 1782 FileFsVolumeInformation); | |
| 1783 | |
| 1784 /* Buffer overflow (a warning status code) is expected here. */ | |
| 1785 if (io_status.Status == STATUS_NOT_IMPLEMENTED) { | |
| 1786 stat_info.VolumeSerialNumber.QuadPart = 0; | |
| 1787 } else if (NT_ERROR(nt_status)) { | |
| 1788 SetLastError(pRtlNtStatusToDosError(nt_status)); | |
| 1789 return -1; | |
| 1790 } else { | |
| 1791 stat_info.VolumeSerialNumber.LowPart = volume_info.VolumeSerialNumber; | |
| 1792 } | |
| 1793 | |
| 1794 stat_info.DeviceType = device_info.DeviceType; | |
| 1795 stat_info.FileAttributes = file_info.BasicInformation.FileAttributes; | |
| 1796 stat_info.NumberOfLinks = file_info.StandardInformation.NumberOfLinks; | |
| 1797 stat_info.FileId.QuadPart = | |
| 1798 file_info.InternalInformation.IndexNumber.QuadPart; | |
| 1799 stat_info.ChangeTime.QuadPart = | |
| 1800 file_info.BasicInformation.ChangeTime.QuadPart; | |
| 1801 stat_info.CreationTime.QuadPart = | |
| 1802 file_info.BasicInformation.CreationTime.QuadPart; | |
| 1803 stat_info.LastAccessTime.QuadPart = | |
| 1804 file_info.BasicInformation.LastAccessTime.QuadPart; | |
| 1805 stat_info.LastWriteTime.QuadPart = | |
| 1806 file_info.BasicInformation.LastWriteTime.QuadPart; | |
| 1807 stat_info.AllocationSize.QuadPart = | |
| 1808 file_info.StandardInformation.AllocationSize.QuadPart; | |
| 1809 | |
| 1810 if (do_lstat && | |
| 1811 (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { | |
| 1812 /* | |
| 1813 * If reading the link fails, the reparse point is not a symlink and needs | |
| 1814 * to be treated as a regular file. The higher level lstat function will | |
| 1815 * detect this failure and retry without do_lstat if appropriate. | |
| 1816 */ | |
| 1817 if (fs__readlink_handle(handle, NULL, &target_length) != 0) { | |
| 1818 return -1; | |
| 1819 } | |
| 1820 stat_info.EndOfFile.QuadPart = target_length; | |
| 1821 } else { | |
| 1822 stat_info.EndOfFile.QuadPart = | |
| 1823 file_info.StandardInformation.EndOfFile.QuadPart; | |
| 1824 } | |
| 1825 | |
| 1826 fs__stat_assign_statbuf(statbuf, stat_info, do_lstat); | |
| 1827 return 0; | |
| 1828 } | |
| 1829 | |
| 1830 INLINE static void fs__stat_assign_statbuf_null(uv_stat_t* statbuf) { | |
| 1831 memset(statbuf, 0, sizeof(uv_stat_t)); | |
| 1832 statbuf->st_mode = _S_IFCHR; | |
| 1833 statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) | | |
| 1834 ((_S_IREAD | _S_IWRITE) >> 6); | |
| 1835 statbuf->st_nlink = 1; | |
| 1836 statbuf->st_blksize = 4096; | |
| 1837 statbuf->st_rdev = FILE_DEVICE_NULL << 16; | |
| 1838 } | |
| 1839 | |
| 1840 INLINE static void fs__stat_assign_statbuf(uv_stat_t* statbuf, | |
| 1841 FILE_STAT_BASIC_INFORMATION stat_info, int do_lstat) { | |
| 1842 statbuf->st_dev = stat_info.VolumeSerialNumber.LowPart; | |
| 1843 | |
| 1844 /* Todo: st_mode should probably always be 0666 for everyone. We might also | |
| 1845 * want to report 0777 if the file is a .exe or a directory. | |
| 1846 * | |
| 1847 * Currently it's based on whether the 'readonly' attribute is set, which | |
| 1848 * makes little sense because the semantics are so different: the 'read-only' | |
| 1849 * flag is just a way for a user to protect against accidental deletion, and | |
| 1850 * serves no security purpose. Windows uses ACLs for that. | |
| 1851 * | |
| 1852 * Also people now use uv_fs_chmod() to take away the writable bit for good | |
| 1853 * reasons. Windows however just makes the file read-only, which makes it | |
| 1854 * impossible to delete the file afterwards, since read-only files can't be | |
| 1855 * deleted. | |
| 1856 * | |
| 1857 * IOW it's all just a clusterfuck and we should think of something that | |
| 1858 * makes slightly more sense. | |
| 1859 * | |
| 1860 * And uv_fs_chmod should probably just fail on windows or be a total no-op. | |
| 1861 * There's nothing sensible it can do anyway. | |
| 1862 */ | |
| 1863 statbuf->st_mode = 0; | |
| 1864 | |
| 1865 /* | |
| 1866 * On Windows, FILE_ATTRIBUTE_REPARSE_POINT is a general purpose mechanism | |
| 1867 * by which filesystem drivers can intercept and alter file system requests. | |
| 1868 * | |
| 1869 * The only reparse points we care about are symlinks and mount points, both | |
| 1870 * of which are treated as POSIX symlinks. Further, we only care when | |
| 1871 * invoked via lstat, which seeks information about the link instead of its | |
| 1872 * target. Otherwise, reparse points must be treated as regular files. | |
| 1873 */ | |
| 1874 if (do_lstat && | |
| 1875 (stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { | |
| 1876 statbuf->st_mode |= S_IFLNK; | |
| 1877 statbuf->st_size = stat_info.EndOfFile.QuadPart; | |
| 1878 } | |
| 1879 | |
| 1880 if (statbuf->st_mode == 0) { | |
| 1881 if (stat_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | |
| 1882 statbuf->st_mode |= _S_IFDIR; | |
| 1883 statbuf->st_size = 0; | |
| 1884 } else { | |
| 1885 statbuf->st_mode |= _S_IFREG; | |
| 1886 statbuf->st_size = stat_info.EndOfFile.QuadPart; | |
| 1887 } | |
| 1888 } | |
| 1889 | |
| 1890 if (stat_info.FileAttributes & FILE_ATTRIBUTE_READONLY) | |
| 1891 statbuf->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6); | |
| 1892 else | |
| 1893 statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) | | |
| 1894 ((_S_IREAD | _S_IWRITE) >> 6); | |
| 1895 | |
| 1896 uv__filetime_to_timespec(&statbuf->st_atim, | |
| 1897 stat_info.LastAccessTime.QuadPart); | |
| 1898 uv__filetime_to_timespec(&statbuf->st_ctim, | |
| 1899 stat_info.ChangeTime.QuadPart); | |
| 1900 uv__filetime_to_timespec(&statbuf->st_mtim, | |
| 1901 stat_info.LastWriteTime.QuadPart); | |
| 1902 uv__filetime_to_timespec(&statbuf->st_birthtim, | |
| 1903 stat_info.CreationTime.QuadPart); | |
| 1904 | |
| 1905 statbuf->st_ino = stat_info.FileId.QuadPart; | |
| 1906 | |
| 1907 /* st_blocks contains the on-disk allocation size in 512-byte units. */ | |
| 1908 statbuf->st_blocks = | |
| 1909 (uint64_t) stat_info.AllocationSize.QuadPart >> 9; | |
| 1910 | |
| 1911 statbuf->st_nlink = stat_info.NumberOfLinks; | |
| 1912 | |
| 1913 /* The st_blksize is supposed to be the 'optimal' number of bytes for reading | |
| 1914 * and writing to the disk. That is, for any definition of 'optimal' - it's | |
| 1915 * supposed to at least avoid read-update-write behavior when writing to the | |
| 1916 * disk. | |
| 1917 * | |
| 1918 * However nobody knows this and even fewer people actually use this value, | |
| 1919 * and in order to fill it out we'd have to make another syscall to query the | |
| 1920 * volume for FILE_FS_SECTOR_SIZE_INFORMATION. | |
| 1921 * | |
| 1922 * Therefore we'll just report a sensible value that's quite commonly okay | |
| 1923 * on modern hardware. | |
| 1924 * | |
| 1925 * 4096 is the minimum required to be compatible with newer Advanced Format | |
| 1926 * drives (which have 4096 bytes per physical sector), and to be backwards | |
| 1927 * compatible with older drives (which have 512 bytes per physical sector). | |
| 1928 */ | |
| 1929 statbuf->st_blksize = 4096; | |
| 1930 | |
| 1931 /* Todo: set st_flags to something meaningful. Also provide a wrapper for | |
| 1932 * chattr(2). | |
| 1933 */ | |
| 1934 statbuf->st_flags = 0; | |
| 1935 | |
| 1936 /* Windows has nothing sensible to say about these values, so they'll just | |
| 1937 * remain empty. | |
| 1938 */ | |
| 1939 statbuf->st_gid = 0; | |
| 1940 statbuf->st_uid = 0; | |
| 1941 statbuf->st_rdev = 0; | |
| 1942 statbuf->st_gen = 0; | |
| 1943 } | |
| 1944 | |
| 1945 | |
| 1946 INLINE static void fs__stat_prepare_path(WCHAR* pathw) { | |
| 1947 size_t len = wcslen(pathw); | |
| 1948 | |
| 1949 /* TODO: ignore namespaced paths. */ | |
| 1950 if (len > 1 && pathw[len - 2] != L':' && | |
| 1951 (pathw[len - 1] == L'\\' || pathw[len - 1] == L'/')) { | |
| 1952 pathw[len - 1] = '\0'; | |
| 1953 } | |
| 1954 } | |
| 1955 | |
| 1956 INLINE static DWORD fs__stat_directory(WCHAR* path, uv_stat_t* statbuf, | |
| 1957 int do_lstat, DWORD ret_error) { | |
| 1958 HANDLE handle = INVALID_HANDLE_VALUE; | |
| 1959 FILE_STAT_BASIC_INFORMATION stat_info; | |
| 1960 FILE_ID_FULL_DIR_INFORMATION dir_info; | |
| 1961 FILE_FS_VOLUME_INFORMATION volume_info; | |
| 1962 FILE_FS_DEVICE_INFORMATION device_info; | |
| 1963 IO_STATUS_BLOCK io_status; | |
| 1964 NTSTATUS nt_status; | |
| 1965 WCHAR* path_dirpath = NULL; | |
| 1966 WCHAR* path_filename = NULL; | |
| 1967 UNICODE_STRING FileMask; | |
| 1968 size_t len; | |
| 1969 size_t split; | |
| 1970 WCHAR splitchar; | |
| 1971 int includes_name; | |
| 1972 | |
| 1973 /* AKA strtok or wcscspn, in reverse. */ | |
| 1974 len = wcslen(path); | |
| 1975 split = len; | |
| 1976 | |
| 1977 includes_name = 0; | |
| 1978 while (split > 0 && path[split - 1] != L'\\' && path[split - 1] != L'/' && | |
| 1979 path[split - 1] != L':') { | |
| 1980 /* check if the path contains a character other than /,\,:,. */ | |
| 1981 if (path[split-1] != '.') { | |
| 1982 includes_name = 1; | |
| 1983 } | |
| 1984 split--; | |
| 1985 } | |
| 1986 /* If the path is a relative path with a file name or a folder name */ | |
| 1987 if (split == 0 && includes_name) { | |
| 1988 path_dirpath = L"."; | |
| 1989 /* If there is a slash or a backslash */ | |
| 1990 } else if (path[split - 1] == L'\\' || path[split - 1] == L'/') { | |
| 1991 path_dirpath = path; | |
| 1992 /* If there is no filename, consider it as a relative folder path */ | |
| 1993 if (!includes_name) { | |
| 1994 split = len; | |
| 1995 /* Else, split it */ | |
| 1996 } else { | |
| 1997 splitchar = path[split - 1]; | |
| 1998 path[split - 1] = L'\0'; | |
| 1999 } | |
| 2000 /* e.g. "..", "c:" */ | |
| 2001 } else { | |
| 2002 path_dirpath = path; | |
| 2003 split = len; | |
| 2004 } | |
| 2005 path_filename = &path[split]; | |
| 2006 | |
| 2007 len = 0; | |
| 2008 while (1) { | |
| 2009 if (path_filename[len] == L'\0') | |
| 2010 break; | |
| 2011 if (path_filename[len] == L'*' || path_filename[len] == L'?' || | |
| 2012 path_filename[len] == L'>' || path_filename[len] == L'<' || | |
| 2013 path_filename[len] == L'"') { | |
| 2014 ret_error = ERROR_INVALID_NAME; | |
| 2015 goto cleanup; | |
| 2016 } | |
| 2017 len++; | |
| 2018 } | |
| 2019 | |
| 2020 /* Get directory handle */ | |
| 2021 handle = CreateFileW(path_dirpath, | |
| 2022 FILE_LIST_DIRECTORY, | |
| 2023 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
| 2024 NULL, | |
| 2025 OPEN_EXISTING, | |
| 2026 FILE_FLAG_BACKUP_SEMANTICS, | |
| 2027 NULL); | |
| 2028 | |
| 2029 if (handle == INVALID_HANDLE_VALUE) { | |
| 2030 ret_error = GetLastError(); | |
| 2031 goto cleanup; | |
| 2032 } | |
| 2033 | |
| 2034 /* Get files in the directory */ | |
| 2035 nt_status = uv__RtlUnicodeStringInit(&FileMask, path_filename, len); | |
| 2036 if (!NT_SUCCESS(nt_status)) { | |
| 2037 ret_error = pRtlNtStatusToDosError(nt_status); | |
| 2038 goto cleanup; | |
| 2039 } | |
| 2040 nt_status = pNtQueryDirectoryFile(handle, | |
| 2041 NULL, | |
| 2042 NULL, | |
| 2043 NULL, | |
| 2044 &io_status, | |
| 2045 &dir_info, | |
| 2046 sizeof(dir_info), | |
| 2047 FileIdFullDirectoryInformation, | |
| 2048 TRUE, | |
| 2049 &FileMask, | |
| 2050 TRUE); | |
| 2051 | |
| 2052 /* Buffer overflow (a warning status code) is expected here since there isn't | |
| 2053 * enough space to store the FileName, and actually indicates success. */ | |
| 2054 if (!NT_SUCCESS(nt_status) && nt_status != STATUS_BUFFER_OVERFLOW) { | |
| 2055 if (nt_status == STATUS_NO_MORE_FILES) | |
| 2056 ret_error = ERROR_PATH_NOT_FOUND; | |
| 2057 else | |
| 2058 ret_error = pRtlNtStatusToDosError(nt_status); | |
| 2059 goto cleanup; | |
| 2060 } | |
| 2061 | |
| 2062 /* Assign values to stat_info */ | |
| 2063 memset(&stat_info, 0, sizeof(FILE_STAT_BASIC_INFORMATION)); | |
| 2064 stat_info.FileAttributes = dir_info.FileAttributes; | |
| 2065 stat_info.CreationTime.QuadPart = dir_info.CreationTime.QuadPart; | |
| 2066 stat_info.LastAccessTime.QuadPart = dir_info.LastAccessTime.QuadPart; | |
| 2067 stat_info.LastWriteTime.QuadPart = dir_info.LastWriteTime.QuadPart; | |
| 2068 if (stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { | |
| 2069 /* A file handle is needed to get st_size for the link (from | |
| 2070 * FSCTL_GET_REPARSE_POINT), which is required by posix, but we are here | |
| 2071 * because getting the file handle failed. We could get just the | |
| 2072 * ReparsePointTag by querying FILE_ID_EXTD_DIR_INFORMATION instead to make | |
| 2073 * sure this really is a link before giving up here on the uv_fs_stat call, | |
| 2074 * but that doesn't seem essential. */ | |
| 2075 if (!do_lstat) | |
| 2076 goto cleanup; | |
| 2077 stat_info.EndOfFile.QuadPart = 0; | |
| 2078 stat_info.AllocationSize.QuadPart = 0; | |
| 2079 } else { | |
| 2080 stat_info.EndOfFile.QuadPart = dir_info.EndOfFile.QuadPart; | |
| 2081 stat_info.AllocationSize.QuadPart = dir_info.AllocationSize.QuadPart; | |
| 2082 } | |
| 2083 stat_info.ChangeTime.QuadPart = dir_info.ChangeTime.QuadPart; | |
| 2084 stat_info.FileId.QuadPart = dir_info.FileId.QuadPart; | |
| 2085 | |
| 2086 /* Finish up by getting device info from the directory handle, | |
| 2087 * since files presumably must live on their device. */ | |
| 2088 nt_status = pNtQueryVolumeInformationFile(handle, | |
| 2089 &io_status, | |
| 2090 &volume_info, | |
| 2091 sizeof volume_info, | |
| 2092 FileFsVolumeInformation); | |
| 2093 | |
| 2094 /* Buffer overflow (a warning status code) is expected here. */ | |
| 2095 if (io_status.Status == STATUS_NOT_IMPLEMENTED) { | |
| 2096 stat_info.VolumeSerialNumber.QuadPart = 0; | |
| 2097 } else if (NT_ERROR(nt_status)) { | |
| 2098 ret_error = pRtlNtStatusToDosError(nt_status); | |
| 2099 goto cleanup; | |
| 2100 } else { | |
| 2101 stat_info.VolumeSerialNumber.QuadPart = volume_info.VolumeSerialNumber; | |
| 2102 } | |
| 2103 | |
| 2104 nt_status = pNtQueryVolumeInformationFile(handle, | |
| 2105 &io_status, | |
| 2106 &device_info, | |
| 2107 sizeof device_info, | |
| 2108 FileFsDeviceInformation); | |
| 2109 | |
| 2110 /* Buffer overflow (a warning status code) is expected here. */ | |
| 2111 if (NT_ERROR(nt_status)) { | |
| 2112 ret_error = pRtlNtStatusToDosError(nt_status); | |
| 2113 goto cleanup; | |
| 2114 } | |
| 2115 | |
| 2116 stat_info.DeviceType = device_info.DeviceType; | |
| 2117 stat_info.NumberOfLinks = 1; /* No way to recover this info. */ | |
| 2118 | |
| 2119 fs__stat_assign_statbuf(statbuf, stat_info, do_lstat); | |
| 2120 ret_error = 0; | |
| 2121 | |
| 2122 cleanup: | |
| 2123 if (split != 0) | |
| 2124 path[split - 1] = splitchar; | |
| 2125 if (handle != INVALID_HANDLE_VALUE) | |
| 2126 CloseHandle(handle); | |
| 2127 return ret_error; | |
| 2128 } | |
| 2129 | |
| 2130 INLINE static DWORD fs__stat_impl_from_path(WCHAR* path, | |
| 2131 int do_lstat, | |
| 2132 uv_stat_t* statbuf) { | |
| 2133 HANDLE handle; | |
| 2134 DWORD flags; | |
| 2135 DWORD ret; | |
| 2136 | |
| 2137 /* If new API exists, try to use it. */ | |
| 2138 switch (fs__stat_path(path, statbuf, do_lstat)) { | |
| 2139 case FS__STAT_PATH_SUCCESS: | |
| 2140 return 0; | |
| 2141 case FS__STAT_PATH_ERROR: | |
| 2142 return GetLastError(); | |
| 2143 case FS__STAT_PATH_TRY_SLOW: | |
| 2144 break; | |
| 2145 } | |
| 2146 | |
| 2147 /* If the new API does not exist, use the old API. */ | |
| 2148 flags = FILE_FLAG_BACKUP_SEMANTICS; | |
| 2149 if (do_lstat) | |
| 2150 flags |= FILE_FLAG_OPEN_REPARSE_POINT; | |
| 2151 | |
| 2152 handle = CreateFileW(path, | |
| 2153 FILE_READ_ATTRIBUTES, | |
| 2154 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
| 2155 NULL, | |
| 2156 OPEN_EXISTING, | |
| 2157 flags, | |
| 2158 NULL); | |
| 2159 | |
| 2160 if (handle == INVALID_HANDLE_VALUE) { | |
| 2161 ret = GetLastError(); | |
| 2162 if (ret != ERROR_ACCESS_DENIED && ret != ERROR_SHARING_VIOLATION) | |
| 2163 return ret; | |
| 2164 return fs__stat_directory(path, statbuf, do_lstat, ret); | |
| 2165 } | |
| 2166 | |
| 2167 if (fs__stat_handle(handle, statbuf, do_lstat) != 0) | |
| 2168 ret = GetLastError(); | |
| 2169 else | |
| 2170 ret = 0; | |
| 2171 | |
| 2172 CloseHandle(handle); | |
| 2173 return ret; | |
| 2174 } | |
| 2175 | |
| 2176 | |
| 2177 INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) { | |
| 2178 DWORD error; | |
| 2179 | |
| 2180 error = fs__stat_impl_from_path(req->file.pathw, do_lstat, &req->statbuf); | |
| 2181 if (error != 0) { | |
| 2182 if (do_lstat && | |
| 2183 (error == ERROR_SYMLINK_NOT_SUPPORTED || | |
| 2184 error == ERROR_NOT_A_REPARSE_POINT)) { | |
| 2185 /* We opened a reparse point but it was not a symlink. Try again. */ | |
| 2186 fs__stat_impl(req, 0); | |
| 2187 } else { | |
| 2188 /* Stat failed. */ | |
| 2189 SET_REQ_WIN32_ERROR(req, error); | |
| 2190 } | |
| 2191 | |
| 2192 return; | |
| 2193 } | |
| 2194 | |
| 2195 req->ptr = &req->statbuf; | |
| 2196 SET_REQ_RESULT(req, 0); | |
| 2197 } | |
| 2198 | |
| 2199 | |
| 2200 INLINE static int fs__fstat_handle(int fd, HANDLE handle, uv_stat_t* statbuf) { | |
| 2201 DWORD file_type; | |
| 2202 | |
| 2203 /* Each file type is processed differently. */ | |
| 2204 file_type = uv_guess_handle(fd); | |
| 2205 switch (file_type) { | |
| 2206 /* Disk files use the existing logic from fs__stat_handle. */ | |
| 2207 case UV_FILE: | |
| 2208 return fs__stat_handle(handle, statbuf, 0); | |
| 2209 | |
| 2210 /* Devices and pipes are processed identically. There is no more information | |
| 2211 * for them from any API. Fields are set as reasonably as possible and the | |
| 2212 * function returns. */ | |
| 2213 case UV_TTY: | |
| 2214 case UV_NAMED_PIPE: | |
| 2215 memset(statbuf, 0, sizeof(uv_stat_t)); | |
| 2216 statbuf->st_mode = file_type == UV_TTY ? _S_IFCHR : _S_IFIFO; | |
| 2217 statbuf->st_nlink = 1; | |
| 2218 statbuf->st_rdev = (file_type == UV_TTY ? FILE_DEVICE_CONSOLE : FILE_DEVICE_NAMED_PIPE) << 16; | |
| 2219 statbuf->st_ino = (uintptr_t) handle; | |
| 2220 return 0; | |
| 2221 | |
| 2222 /* If file type is unknown it is an error. */ | |
| 2223 case UV_UNKNOWN_HANDLE: | |
| 2224 default: | |
| 2225 SetLastError(ERROR_INVALID_HANDLE); | |
| 2226 return -1; | |
| 2227 } | |
| 2228 } | |
| 2229 | |
| 2230 | |
| 2231 static void fs__stat(uv_fs_t* req) { | |
| 2232 fs__stat_prepare_path(req->file.pathw); | |
| 2233 fs__stat_impl(req, 0); | |
| 2234 } | |
| 2235 | |
| 2236 | |
| 2237 static void fs__lstat(uv_fs_t* req) { | |
| 2238 fs__stat_prepare_path(req->file.pathw); | |
| 2239 fs__stat_impl(req, 1); | |
| 2240 } | |
| 2241 | |
| 2242 | |
| 2243 static void fs__fstat(uv_fs_t* req) { | |
| 2244 int fd = req->file.fd; | |
| 2245 HANDLE handle; | |
| 2246 | |
| 2247 VERIFY_FD(fd, req); | |
| 2248 | |
| 2249 handle = uv__get_osfhandle(fd); | |
| 2250 | |
| 2251 if (handle == INVALID_HANDLE_VALUE) { | |
| 2252 SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE); | |
| 2253 return; | |
| 2254 } | |
| 2255 | |
| 2256 if (fs__fstat_handle(fd, handle, &req->statbuf) != 0) { | |
| 2257 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 2258 return; | |
| 2259 } | |
| 2260 | |
| 2261 req->ptr = &req->statbuf; | |
| 2262 SET_REQ_RESULT(req, 0); | |
| 2263 } | |
| 2264 | |
| 2265 | |
| 2266 static void fs__rename(uv_fs_t* req) { | |
| 2267 if (!MoveFileExW(req->file.pathw, req->fs.info.new_pathw, MOVEFILE_REPLACE_EXISTING)) { | |
| 2268 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 2269 return; | |
| 2270 } | |
| 2271 | |
| 2272 SET_REQ_RESULT(req, 0); | |
| 2273 } | |
| 2274 | |
| 2275 | |
| 2276 INLINE static void fs__sync_impl(uv_fs_t* req) { | |
| 2277 int fd = req->file.fd; | |
| 2278 int result; | |
| 2279 | |
| 2280 VERIFY_FD(fd, req); | |
| 2281 | |
| 2282 result = FlushFileBuffers(uv__get_osfhandle(fd)) ? 0 : -1; | |
| 2283 if (result == -1) { | |
| 2284 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 2285 } else { | |
| 2286 SET_REQ_RESULT(req, result); | |
| 2287 } | |
| 2288 } | |
| 2289 | |
| 2290 | |
| 2291 static void fs__fsync(uv_fs_t* req) { | |
| 2292 fs__sync_impl(req); | |
| 2293 } | |
| 2294 | |
| 2295 | |
| 2296 static void fs__fdatasync(uv_fs_t* req) { | |
| 2297 fs__sync_impl(req); | |
| 2298 } | |
| 2299 | |
| 2300 | |
| 2301 static void fs__ftruncate(uv_fs_t* req) { | |
| 2302 int fd = req->file.fd; | |
| 2303 HANDLE handle; | |
| 2304 struct uv__fd_info_s fd_info = { 0 }; | |
| 2305 NTSTATUS status; | |
| 2306 IO_STATUS_BLOCK io_status; | |
| 2307 FILE_END_OF_FILE_INFORMATION eof_info; | |
| 2308 | |
| 2309 VERIFY_FD(fd, req); | |
| 2310 | |
| 2311 handle = uv__get_osfhandle(fd); | |
| 2312 | |
| 2313 if (uv__fd_hash_get(fd, &fd_info)) { | |
| 2314 if (fd_info.is_directory) { | |
| 2315 SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); | |
| 2316 return; | |
| 2317 } | |
| 2318 | |
| 2319 if (fd_info.mapping != INVALID_HANDLE_VALUE) { | |
| 2320 CloseHandle(fd_info.mapping); | |
| 2321 } | |
| 2322 } | |
| 2323 | |
| 2324 eof_info.EndOfFile.QuadPart = req->fs.info.offset; | |
| 2325 | |
| 2326 status = pNtSetInformationFile(handle, | |
| 2327 &io_status, | |
| 2328 &eof_info, | |
| 2329 sizeof eof_info, | |
| 2330 FileEndOfFileInformation); | |
| 2331 | |
| 2332 if (NT_SUCCESS(status)) { | |
| 2333 SET_REQ_RESULT(req, 0); | |
| 2334 } else { | |
| 2335 SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); | |
| 2336 | |
| 2337 if (fd_info.flags) { | |
| 2338 CloseHandle(handle); | |
| 2339 fd_info.mapping = INVALID_HANDLE_VALUE; | |
| 2340 fd_info.size.QuadPart = 0; | |
| 2341 fd_info.current_pos.QuadPart = 0; | |
| 2342 uv__fd_hash_add(fd, &fd_info); | |
| 2343 return; | |
| 2344 } | |
| 2345 } | |
| 2346 | |
| 2347 if (fd_info.flags) { | |
| 2348 fd_info.size = eof_info.EndOfFile; | |
| 2349 | |
| 2350 if (fd_info.size.QuadPart == 0) { | |
| 2351 fd_info.mapping = INVALID_HANDLE_VALUE; | |
| 2352 } else { | |
| 2353 DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | | |
| 2354 UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE; | |
| 2355 fd_info.mapping = CreateFileMapping(handle, | |
| 2356 NULL, | |
| 2357 flProtect, | |
| 2358 fd_info.size.HighPart, | |
| 2359 fd_info.size.LowPart, | |
| 2360 NULL); | |
| 2361 if (fd_info.mapping == NULL) { | |
| 2362 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 2363 CloseHandle(handle); | |
| 2364 fd_info.mapping = INVALID_HANDLE_VALUE; | |
| 2365 fd_info.size.QuadPart = 0; | |
| 2366 fd_info.current_pos.QuadPart = 0; | |
| 2367 uv__fd_hash_add(fd, &fd_info); | |
| 2368 return; | |
| 2369 } | |
| 2370 } | |
| 2371 | |
| 2372 uv__fd_hash_add(fd, &fd_info); | |
| 2373 } | |
| 2374 } | |
| 2375 | |
| 2376 | |
| 2377 static void fs__copyfile(uv_fs_t* req) { | |
| 2378 int flags; | |
| 2379 int overwrite; | |
| 2380 uv_stat_t statbuf; | |
| 2381 uv_stat_t new_statbuf; | |
| 2382 | |
| 2383 flags = req->fs.info.file_flags; | |
| 2384 | |
| 2385 if (flags & UV_FS_COPYFILE_FICLONE_FORCE) { | |
| 2386 SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED); | |
| 2387 return; | |
| 2388 } | |
| 2389 | |
| 2390 overwrite = flags & UV_FS_COPYFILE_EXCL; | |
| 2391 | |
| 2392 if (CopyFileW(req->file.pathw, req->fs.info.new_pathw, overwrite) != 0) { | |
| 2393 SET_REQ_RESULT(req, 0); | |
| 2394 return; | |
| 2395 } | |
| 2396 | |
| 2397 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 2398 if (req->result != UV_EBUSY) | |
| 2399 return; | |
| 2400 | |
| 2401 /* if error UV_EBUSY check if src and dst file are the same */ | |
| 2402 if (fs__stat_impl_from_path(req->file.pathw, 0, &statbuf) != 0 || | |
| 2403 fs__stat_impl_from_path(req->fs.info.new_pathw, 0, &new_statbuf) != 0) { | |
| 2404 return; | |
| 2405 } | |
| 2406 | |
| 2407 if (statbuf.st_dev == new_statbuf.st_dev && | |
| 2408 statbuf.st_ino == new_statbuf.st_ino) { | |
| 2409 SET_REQ_RESULT(req, 0); | |
| 2410 } | |
| 2411 } | |
| 2412 | |
| 2413 | |
| 2414 static void fs__sendfile(uv_fs_t* req) { | |
| 2415 int fd_in = req->file.fd, fd_out = req->fs.info.fd_out; | |
| 2416 size_t length = req->fs.info.bufsml[0].len; | |
| 2417 int64_t offset = req->fs.info.offset; | |
| 2418 const size_t max_buf_size = 65536; | |
| 2419 size_t buf_size = length < max_buf_size ? length : max_buf_size; | |
| 2420 int n, result = 0; | |
| 2421 int64_t result_offset = 0; | |
| 2422 char* buf = (char*) uv__malloc(buf_size); | |
| 2423 if (!buf) { | |
| 2424 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); | |
| 2425 } | |
| 2426 | |
| 2427 if (offset != -1) { | |
| 2428 result_offset = _lseeki64(fd_in, offset, SEEK_SET); | |
| 2429 } | |
| 2430 | |
| 2431 if (result_offset == -1) { | |
| 2432 result = -1; | |
| 2433 } else { | |
| 2434 while (length > 0) { | |
| 2435 n = _read(fd_in, buf, length < buf_size ? length : buf_size); | |
| 2436 if (n == 0) { | |
| 2437 break; | |
| 2438 } else if (n == -1) { | |
| 2439 result = -1; | |
| 2440 break; | |
| 2441 } | |
| 2442 | |
| 2443 length -= n; | |
| 2444 | |
| 2445 n = _write(fd_out, buf, n); | |
| 2446 if (n == -1) { | |
| 2447 result = -1; | |
| 2448 break; | |
| 2449 } | |
| 2450 | |
| 2451 result += n; | |
| 2452 } | |
| 2453 } | |
| 2454 | |
| 2455 uv__free(buf); | |
| 2456 | |
| 2457 SET_REQ_RESULT(req, result); | |
| 2458 } | |
| 2459 | |
| 2460 | |
| 2461 static void fs__access(uv_fs_t* req) { | |
| 2462 DWORD attr = GetFileAttributesW(req->file.pathw); | |
| 2463 | |
| 2464 if (attr == INVALID_FILE_ATTRIBUTES) { | |
| 2465 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 2466 return; | |
| 2467 } | |
| 2468 | |
| 2469 /* | |
| 2470 * Access is possible if | |
| 2471 * - write access wasn't requested, | |
| 2472 * - or the file isn't read-only, | |
| 2473 * - or it's a directory. | |
| 2474 * (Directories cannot be read-only on Windows.) | |
| 2475 */ | |
| 2476 if (!(req->fs.info.mode & W_OK) || | |
| 2477 !(attr & FILE_ATTRIBUTE_READONLY) || | |
| 2478 (attr & FILE_ATTRIBUTE_DIRECTORY)) { | |
| 2479 SET_REQ_RESULT(req, 0); | |
| 2480 } else { | |
| 2481 SET_REQ_WIN32_ERROR(req, UV_EPERM); | |
| 2482 } | |
| 2483 | |
| 2484 } | |
| 2485 | |
| 2486 | |
| 2487 static void fs__chmod(uv_fs_t* req) { | |
| 2488 int result = _wchmod(req->file.pathw, req->fs.info.mode); | |
| 2489 if (result == -1) | |
| 2490 SET_REQ_WIN32_ERROR(req, _doserrno); | |
| 2491 else | |
| 2492 SET_REQ_RESULT(req, 0); | |
| 2493 } | |
| 2494 | |
| 2495 | |
| 2496 static void fs__fchmod(uv_fs_t* req) { | |
| 2497 int fd = req->file.fd; | |
| 2498 int clear_archive_flag; | |
| 2499 HANDLE handle; | |
| 2500 NTSTATUS nt_status; | |
| 2501 IO_STATUS_BLOCK io_status; | |
| 2502 FILE_BASIC_INFORMATION file_info; | |
| 2503 | |
| 2504 VERIFY_FD(fd, req); | |
| 2505 | |
| 2506 handle = ReOpenFile(uv__get_osfhandle(fd), FILE_WRITE_ATTRIBUTES, 0, 0); | |
| 2507 if (handle == INVALID_HANDLE_VALUE) { | |
| 2508 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 2509 return; | |
| 2510 } | |
| 2511 | |
| 2512 nt_status = pNtQueryInformationFile(handle, | |
| 2513 &io_status, | |
| 2514 &file_info, | |
| 2515 sizeof file_info, | |
| 2516 FileBasicInformation); | |
| 2517 | |
| 2518 if (!NT_SUCCESS(nt_status)) { | |
| 2519 SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); | |
| 2520 goto fchmod_cleanup; | |
| 2521 } | |
| 2522 | |
| 2523 /* Test if the Archive attribute is cleared */ | |
| 2524 if ((file_info.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) == 0) { | |
| 2525 /* Set Archive flag, otherwise setting or clearing the read-only | |
| 2526 flag will not work */ | |
| 2527 file_info.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; | |
| 2528 nt_status = pNtSetInformationFile(handle, | |
| 2529 &io_status, | |
| 2530 &file_info, | |
| 2531 sizeof file_info, | |
| 2532 FileBasicInformation); | |
| 2533 if (!NT_SUCCESS(nt_status)) { | |
| 2534 SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); | |
| 2535 goto fchmod_cleanup; | |
| 2536 } | |
| 2537 /* Remember to clear the flag later on */ | |
| 2538 clear_archive_flag = 1; | |
| 2539 } else { | |
| 2540 clear_archive_flag = 0; | |
| 2541 } | |
| 2542 | |
| 2543 if (req->fs.info.mode & _S_IWRITE) { | |
| 2544 file_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY; | |
| 2545 } else { | |
| 2546 file_info.FileAttributes |= FILE_ATTRIBUTE_READONLY; | |
| 2547 } | |
| 2548 | |
| 2549 nt_status = pNtSetInformationFile(handle, | |
| 2550 &io_status, | |
| 2551 &file_info, | |
| 2552 sizeof file_info, | |
| 2553 FileBasicInformation); | |
| 2554 | |
| 2555 if (!NT_SUCCESS(nt_status)) { | |
| 2556 SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); | |
| 2557 goto fchmod_cleanup; | |
| 2558 } | |
| 2559 | |
| 2560 if (clear_archive_flag) { | |
| 2561 file_info.FileAttributes &= ~FILE_ATTRIBUTE_ARCHIVE; | |
| 2562 if (file_info.FileAttributes == 0) { | |
| 2563 file_info.FileAttributes = FILE_ATTRIBUTE_NORMAL; | |
| 2564 } | |
| 2565 nt_status = pNtSetInformationFile(handle, | |
| 2566 &io_status, | |
| 2567 &file_info, | |
| 2568 sizeof file_info, | |
| 2569 FileBasicInformation); | |
| 2570 if (!NT_SUCCESS(nt_status)) { | |
| 2571 SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); | |
| 2572 goto fchmod_cleanup; | |
| 2573 } | |
| 2574 } | |
| 2575 | |
| 2576 SET_REQ_SUCCESS(req); | |
| 2577 fchmod_cleanup: | |
| 2578 CloseHandle(handle); | |
| 2579 } | |
| 2580 | |
| 2581 | |
| 2582 INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) { | |
| 2583 FILETIME filetime_as, *filetime_a = &filetime_as; | |
| 2584 FILETIME filetime_ms, *filetime_m = &filetime_ms; | |
| 2585 FILETIME now; | |
| 2586 | |
| 2587 if (uv__isinf(atime) || uv__isinf(mtime)) | |
| 2588 GetSystemTimeAsFileTime(&now); | |
| 2589 | |
| 2590 if (uv__isinf(atime)) | |
| 2591 filetime_a = &now; | |
| 2592 else if (uv__isnan(atime)) | |
| 2593 filetime_a = NULL; | |
| 2594 else | |
| 2595 TIME_T_TO_FILETIME(atime, filetime_a); | |
| 2596 | |
| 2597 if (uv__isinf(mtime)) | |
| 2598 filetime_m = &now; | |
| 2599 else if (uv__isnan(mtime)) | |
| 2600 filetime_m = NULL; | |
| 2601 else | |
| 2602 TIME_T_TO_FILETIME(mtime, filetime_m); | |
| 2603 | |
| 2604 if (!SetFileTime(handle, NULL, filetime_a, filetime_m)) | |
| 2605 return -1; | |
| 2606 | |
| 2607 return 0; | |
| 2608 } | |
| 2609 | |
| 2610 INLINE static DWORD fs__utime_impl_from_path(WCHAR* path, | |
| 2611 double atime, | |
| 2612 double mtime, | |
| 2613 int do_lutime) { | |
| 2614 HANDLE handle; | |
| 2615 DWORD flags; | |
| 2616 DWORD ret; | |
| 2617 | |
| 2618 flags = FILE_FLAG_BACKUP_SEMANTICS; | |
| 2619 if (do_lutime) { | |
| 2620 flags |= FILE_FLAG_OPEN_REPARSE_POINT; | |
| 2621 } | |
| 2622 | |
| 2623 handle = CreateFileW(path, | |
| 2624 FILE_WRITE_ATTRIBUTES, | |
| 2625 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
| 2626 NULL, | |
| 2627 OPEN_EXISTING, | |
| 2628 flags, | |
| 2629 NULL); | |
| 2630 | |
| 2631 if (handle == INVALID_HANDLE_VALUE) | |
| 2632 return GetLastError(); | |
| 2633 | |
| 2634 if (fs__utime_handle(handle, atime, mtime) != 0) | |
| 2635 ret = GetLastError(); | |
| 2636 else | |
| 2637 ret = 0; | |
| 2638 | |
| 2639 CloseHandle(handle); | |
| 2640 return ret; | |
| 2641 } | |
| 2642 | |
| 2643 INLINE static void fs__utime_impl(uv_fs_t* req, int do_lutime) { | |
| 2644 DWORD error; | |
| 2645 | |
| 2646 error = fs__utime_impl_from_path(req->file.pathw, | |
| 2647 req->fs.time.atime, | |
| 2648 req->fs.time.mtime, | |
| 2649 do_lutime); | |
| 2650 | |
| 2651 if (error != 0) { | |
| 2652 if (do_lutime && | |
| 2653 (error == ERROR_SYMLINK_NOT_SUPPORTED || | |
| 2654 error == ERROR_NOT_A_REPARSE_POINT)) { | |
| 2655 /* Opened file is a reparse point but not a symlink. Try again. */ | |
| 2656 fs__utime_impl(req, 0); | |
| 2657 } else { | |
| 2658 /* utime failed. */ | |
| 2659 SET_REQ_WIN32_ERROR(req, error); | |
| 2660 } | |
| 2661 | |
| 2662 return; | |
| 2663 } | |
| 2664 | |
| 2665 SET_REQ_RESULT(req, 0); | |
| 2666 } | |
| 2667 | |
| 2668 static void fs__utime(uv_fs_t* req) { | |
| 2669 fs__utime_impl(req, /* do_lutime */ 0); | |
| 2670 } | |
| 2671 | |
| 2672 | |
| 2673 static void fs__futime(uv_fs_t* req) { | |
| 2674 int fd = req->file.fd; | |
| 2675 HANDLE handle; | |
| 2676 VERIFY_FD(fd, req); | |
| 2677 | |
| 2678 handle = uv__get_osfhandle(fd); | |
| 2679 | |
| 2680 if (handle == INVALID_HANDLE_VALUE) { | |
| 2681 SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE); | |
| 2682 return; | |
| 2683 } | |
| 2684 | |
| 2685 if (fs__utime_handle(handle, req->fs.time.atime, req->fs.time.mtime) != 0) { | |
| 2686 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 2687 return; | |
| 2688 } | |
| 2689 | |
| 2690 SET_REQ_RESULT(req, 0); | |
| 2691 } | |
| 2692 | |
| 2693 static void fs__lutime(uv_fs_t* req) { | |
| 2694 fs__utime_impl(req, /* do_lutime */ 1); | |
| 2695 } | |
| 2696 | |
| 2697 | |
| 2698 static void fs__link(uv_fs_t* req) { | |
| 2699 DWORD r = CreateHardLinkW(req->fs.info.new_pathw, req->file.pathw, NULL); | |
| 2700 if (r == 0) | |
| 2701 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 2702 else | |
| 2703 SET_REQ_RESULT(req, 0); | |
| 2704 } | |
| 2705 | |
| 2706 | |
| 2707 static void fs__create_junction(uv_fs_t* req, const WCHAR* path, | |
| 2708 const WCHAR* new_path) { | |
| 2709 HANDLE handle = INVALID_HANDLE_VALUE; | |
| 2710 REPARSE_DATA_BUFFER *buffer = NULL; | |
| 2711 int created = 0; | |
| 2712 int target_len; | |
| 2713 int is_absolute, is_long_path; | |
| 2714 int needed_buf_size, used_buf_size, used_data_size, path_buf_len; | |
| 2715 int start, len, i; | |
| 2716 int add_slash; | |
| 2717 DWORD bytes; | |
| 2718 WCHAR* path_buf; | |
| 2719 | |
| 2720 target_len = wcslen(path); | |
| 2721 is_long_path = wcsncmp(path, LONG_PATH_PREFIX, LONG_PATH_PREFIX_LEN) == 0; | |
| 2722 | |
| 2723 if (is_long_path) { | |
| 2724 is_absolute = 1; | |
| 2725 } else { | |
| 2726 is_absolute = target_len >= 3 && IS_LETTER(path[0]) && | |
| 2727 path[1] == L':' && IS_SLASH(path[2]); | |
| 2728 } | |
| 2729 | |
| 2730 if (!is_absolute) { | |
| 2731 /* Not supporting relative paths */ | |
| 2732 SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_NOT_SUPPORTED); | |
| 2733 return; | |
| 2734 } | |
| 2735 | |
| 2736 /* Do a pessimistic calculation of the required buffer size */ | |
| 2737 needed_buf_size = | |
| 2738 FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) + | |
| 2739 JUNCTION_PREFIX_LEN * sizeof(WCHAR) + | |
| 2740 2 * (target_len + 2) * sizeof(WCHAR); | |
| 2741 | |
| 2742 /* Allocate the buffer */ | |
| 2743 buffer = (REPARSE_DATA_BUFFER*)uv__malloc(needed_buf_size); | |
| 2744 if (!buffer) { | |
| 2745 uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); | |
| 2746 } | |
| 2747 | |
| 2748 /* Grab a pointer to the part of the buffer where filenames go */ | |
| 2749 path_buf = (WCHAR*)&(buffer->MountPointReparseBuffer.PathBuffer); | |
| 2750 path_buf_len = 0; | |
| 2751 | |
| 2752 /* Copy the substitute (internal) target path */ | |
| 2753 start = path_buf_len; | |
| 2754 | |
| 2755 wcsncpy((WCHAR*)&path_buf[path_buf_len], JUNCTION_PREFIX, | |
| 2756 JUNCTION_PREFIX_LEN); | |
| 2757 path_buf_len += JUNCTION_PREFIX_LEN; | |
| 2758 | |
| 2759 add_slash = 0; | |
| 2760 for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) { | |
| 2761 if (IS_SLASH(path[i])) { | |
| 2762 add_slash = 1; | |
| 2763 continue; | |
| 2764 } | |
| 2765 | |
| 2766 if (add_slash) { | |
| 2767 path_buf[path_buf_len++] = L'\\'; | |
| 2768 add_slash = 0; | |
| 2769 } | |
| 2770 | |
| 2771 path_buf[path_buf_len++] = path[i]; | |
| 2772 } | |
| 2773 if (add_slash) | |
| 2774 path_buf[path_buf_len++] = L'\\'; | |
| 2775 len = path_buf_len - start; | |
| 2776 | |
| 2777 /* Insert null terminator */ | |
| 2778 path_buf[path_buf_len++] = L'\0'; | |
| 2779 | |
| 2780 /* Set the info about the substitute name */ | |
| 2781 buffer->MountPointReparseBuffer.SubstituteNameOffset = start * sizeof(WCHAR); | |
| 2782 buffer->MountPointReparseBuffer.SubstituteNameLength = len * sizeof(WCHAR); | |
| 2783 | |
| 2784 /* Copy the print name of the target path */ | |
| 2785 start = path_buf_len; | |
| 2786 add_slash = 0; | |
| 2787 for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) { | |
| 2788 if (IS_SLASH(path[i])) { | |
| 2789 add_slash = 1; | |
| 2790 continue; | |
| 2791 } | |
| 2792 | |
| 2793 if (add_slash) { | |
| 2794 path_buf[path_buf_len++] = L'\\'; | |
| 2795 add_slash = 0; | |
| 2796 } | |
| 2797 | |
| 2798 path_buf[path_buf_len++] = path[i]; | |
| 2799 } | |
| 2800 len = path_buf_len - start; | |
| 2801 if (len == 2 || add_slash) { | |
| 2802 path_buf[path_buf_len++] = L'\\'; | |
| 2803 len++; | |
| 2804 } | |
| 2805 | |
| 2806 /* Insert another null terminator */ | |
| 2807 path_buf[path_buf_len++] = L'\0'; | |
| 2808 | |
| 2809 /* Set the info about the print name */ | |
| 2810 buffer->MountPointReparseBuffer.PrintNameOffset = start * sizeof(WCHAR); | |
| 2811 buffer->MountPointReparseBuffer.PrintNameLength = len * sizeof(WCHAR); | |
| 2812 | |
| 2813 /* Calculate how much buffer space was actually used */ | |
| 2814 used_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) + | |
| 2815 path_buf_len * sizeof(WCHAR); | |
| 2816 used_data_size = used_buf_size - | |
| 2817 FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer); | |
| 2818 | |
| 2819 /* Put general info in the data buffer */ | |
| 2820 buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; | |
| 2821 buffer->ReparseDataLength = used_data_size; | |
| 2822 buffer->Reserved = 0; | |
| 2823 | |
| 2824 /* Create a new directory */ | |
| 2825 if (!CreateDirectoryW(new_path, NULL)) { | |
| 2826 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 2827 goto error; | |
| 2828 } | |
| 2829 created = 1; | |
| 2830 | |
| 2831 /* Open the directory */ | |
| 2832 handle = CreateFileW(new_path, | |
| 2833 GENERIC_WRITE, | |
| 2834 0, | |
| 2835 NULL, | |
| 2836 OPEN_EXISTING, | |
| 2837 FILE_FLAG_BACKUP_SEMANTICS | | |
| 2838 FILE_FLAG_OPEN_REPARSE_POINT, | |
| 2839 NULL); | |
| 2840 if (handle == INVALID_HANDLE_VALUE) { | |
| 2841 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 2842 goto error; | |
| 2843 } | |
| 2844 | |
| 2845 /* Create the actual reparse point */ | |
| 2846 if (!DeviceIoControl(handle, | |
| 2847 FSCTL_SET_REPARSE_POINT, | |
| 2848 buffer, | |
| 2849 used_buf_size, | |
| 2850 NULL, | |
| 2851 0, | |
| 2852 &bytes, | |
| 2853 NULL)) { | |
| 2854 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 2855 goto error; | |
| 2856 } | |
| 2857 | |
| 2858 /* Clean up */ | |
| 2859 CloseHandle(handle); | |
| 2860 uv__free(buffer); | |
| 2861 | |
| 2862 SET_REQ_RESULT(req, 0); | |
| 2863 return; | |
| 2864 | |
| 2865 error: | |
| 2866 uv__free(buffer); | |
| 2867 | |
| 2868 if (handle != INVALID_HANDLE_VALUE) { | |
| 2869 CloseHandle(handle); | |
| 2870 } | |
| 2871 | |
| 2872 if (created) { | |
| 2873 RemoveDirectoryW(new_path); | |
| 2874 } | |
| 2875 } | |
| 2876 | |
| 2877 | |
| 2878 static void fs__symlink(uv_fs_t* req) { | |
| 2879 WCHAR* pathw; | |
| 2880 WCHAR* new_pathw; | |
| 2881 int flags; | |
| 2882 int err; | |
| 2883 | |
| 2884 pathw = req->file.pathw; | |
| 2885 new_pathw = req->fs.info.new_pathw; | |
| 2886 | |
| 2887 if (req->fs.info.file_flags & UV_FS_SYMLINK_JUNCTION) { | |
| 2888 fs__create_junction(req, pathw, new_pathw); | |
| 2889 return; | |
| 2890 } | |
| 2891 | |
| 2892 if (req->fs.info.file_flags & UV_FS_SYMLINK_DIR) | |
| 2893 flags = SYMBOLIC_LINK_FLAG_DIRECTORY | uv__file_symlink_usermode_flag; | |
| 2894 else | |
| 2895 flags = uv__file_symlink_usermode_flag; | |
| 2896 | |
| 2897 if (CreateSymbolicLinkW(new_pathw, pathw, flags)) { | |
| 2898 SET_REQ_RESULT(req, 0); | |
| 2899 return; | |
| 2900 } | |
| 2901 | |
| 2902 /* Something went wrong. We will test if it is because of user-mode | |
| 2903 * symlinks. | |
| 2904 */ | |
| 2905 err = GetLastError(); | |
| 2906 if (err == ERROR_INVALID_PARAMETER && | |
| 2907 flags & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) { | |
| 2908 /* This system does not support user-mode symlinks. We will clear the | |
| 2909 * unsupported flag and retry. | |
| 2910 */ | |
| 2911 uv__file_symlink_usermode_flag = 0; | |
| 2912 fs__symlink(req); | |
| 2913 } else { | |
| 2914 SET_REQ_WIN32_ERROR(req, err); | |
| 2915 } | |
| 2916 } | |
| 2917 | |
| 2918 | |
| 2919 static void fs__readlink(uv_fs_t* req) { | |
| 2920 HANDLE handle; | |
| 2921 | |
| 2922 handle = CreateFileW(req->file.pathw, | |
| 2923 0, | |
| 2924 0, | |
| 2925 NULL, | |
| 2926 OPEN_EXISTING, | |
| 2927 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, | |
| 2928 NULL); | |
| 2929 | |
| 2930 if (handle == INVALID_HANDLE_VALUE) { | |
| 2931 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 2932 return; | |
| 2933 } | |
| 2934 | |
| 2935 assert(req->ptr == NULL); | |
| 2936 if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) { | |
| 2937 DWORD error = GetLastError(); | |
| 2938 SET_REQ_WIN32_ERROR(req, error); | |
| 2939 if (error == ERROR_NOT_A_REPARSE_POINT) | |
| 2940 req->result = UV_EINVAL; | |
| 2941 CloseHandle(handle); | |
| 2942 return; | |
| 2943 } | |
| 2944 | |
| 2945 req->flags |= UV_FS_FREE_PTR; | |
| 2946 SET_REQ_RESULT(req, 0); | |
| 2947 | |
| 2948 CloseHandle(handle); | |
| 2949 } | |
| 2950 | |
| 2951 | |
| 2952 static ssize_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) { | |
| 2953 int r; | |
| 2954 DWORD w_realpath_len; | |
| 2955 WCHAR* w_realpath_ptr = NULL; | |
| 2956 WCHAR* w_realpath_buf; | |
| 2957 | |
| 2958 w_realpath_len = GetFinalPathNameByHandleW(handle, NULL, 0, VOLUME_NAME_DOS); | |
| 2959 if (w_realpath_len == 0) { | |
| 2960 return -1; | |
| 2961 } | |
| 2962 | |
| 2963 w_realpath_buf = uv__malloc((w_realpath_len + 1) * sizeof(WCHAR)); | |
| 2964 if (w_realpath_buf == NULL) { | |
| 2965 SetLastError(ERROR_OUTOFMEMORY); | |
| 2966 return -1; | |
| 2967 } | |
| 2968 w_realpath_ptr = w_realpath_buf; | |
| 2969 | |
| 2970 if (GetFinalPathNameByHandleW( | |
| 2971 handle, w_realpath_ptr, w_realpath_len, VOLUME_NAME_DOS) == 0) { | |
| 2972 uv__free(w_realpath_buf); | |
| 2973 SetLastError(ERROR_INVALID_HANDLE); | |
| 2974 return -1; | |
| 2975 } | |
| 2976 | |
| 2977 /* convert UNC path to long path */ | |
| 2978 if (wcsncmp(w_realpath_ptr, | |
| 2979 UNC_PATH_PREFIX, | |
| 2980 UNC_PATH_PREFIX_LEN) == 0) { | |
| 2981 w_realpath_ptr += 6; | |
| 2982 *w_realpath_ptr = L'\\'; | |
| 2983 w_realpath_len -= 6; | |
| 2984 } else if (wcsncmp(w_realpath_ptr, | |
| 2985 LONG_PATH_PREFIX, | |
| 2986 LONG_PATH_PREFIX_LEN) == 0) { | |
| 2987 w_realpath_ptr += 4; | |
| 2988 w_realpath_len -= 4; | |
| 2989 } else { | |
| 2990 uv__free(w_realpath_buf); | |
| 2991 SetLastError(ERROR_INVALID_HANDLE); | |
| 2992 return -1; | |
| 2993 } | |
| 2994 | |
| 2995 assert(*realpath_ptr == NULL); | |
| 2996 r = uv_utf16_to_wtf8(w_realpath_ptr, w_realpath_len, realpath_ptr, NULL); | |
| 2997 uv__free(w_realpath_buf); | |
| 2998 return r; | |
| 2999 } | |
| 3000 | |
| 3001 static void fs__realpath(uv_fs_t* req) { | |
| 3002 HANDLE handle; | |
| 3003 | |
| 3004 handle = CreateFileW(req->file.pathw, | |
| 3005 0, | |
| 3006 0, | |
| 3007 NULL, | |
| 3008 OPEN_EXISTING, | |
| 3009 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, | |
| 3010 NULL); | |
| 3011 if (handle == INVALID_HANDLE_VALUE) { | |
| 3012 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 3013 return; | |
| 3014 } | |
| 3015 | |
| 3016 assert(req->ptr == NULL); | |
| 3017 if (fs__realpath_handle(handle, (char**) &req->ptr) == -1) { | |
| 3018 CloseHandle(handle); | |
| 3019 SET_REQ_WIN32_ERROR(req, GetLastError()); | |
| 3020 return; | |
| 3021 } | |
| 3022 | |
| 3023 CloseHandle(handle); | |
| 3024 req->flags |= UV_FS_FREE_PTR; | |
| 3025 SET_REQ_RESULT(req, 0); | |
| 3026 } | |
| 3027 | |
| 3028 | |
| 3029 static void fs__chown(uv_fs_t* req) { | |
| 3030 SET_REQ_RESULT(req, 0); | |
| 3031 } | |
| 3032 | |
| 3033 | |
| 3034 static void fs__fchown(uv_fs_t* req) { | |
| 3035 SET_REQ_RESULT(req, 0); | |
| 3036 } | |
| 3037 | |
| 3038 | |
| 3039 static void fs__lchown(uv_fs_t* req) { | |
| 3040 SET_REQ_RESULT(req, 0); | |
| 3041 } | |
| 3042 | |
| 3043 | |
| 3044 static void fs__statfs(uv_fs_t* req) { | |
| 3045 uv_statfs_t* stat_fs; | |
| 3046 DWORD sectors_per_cluster; | |
| 3047 DWORD bytes_per_sector; | |
| 3048 DWORD free_clusters; | |
| 3049 DWORD total_clusters; | |
| 3050 WCHAR* pathw; | |
| 3051 | |
| 3052 pathw = req->file.pathw; | |
| 3053 retry_get_disk_free_space: | |
| 3054 if (0 == GetDiskFreeSpaceW(pathw, | |
| 3055 §ors_per_cluster, | |
| 3056 &bytes_per_sector, | |
| 3057 &free_clusters, | |
| 3058 &total_clusters)) { | |
| 3059 DWORD err; | |
| 3060 WCHAR* fpart; | |
| 3061 size_t len; | |
| 3062 DWORD ret; | |
| 3063 BOOL is_second; | |
| 3064 | |
| 3065 err = GetLastError(); | |
| 3066 is_second = pathw != req->file.pathw; | |
| 3067 if (err != ERROR_DIRECTORY || is_second) { | |
| 3068 if (is_second) | |
| 3069 uv__free(pathw); | |
| 3070 | |
| 3071 SET_REQ_WIN32_ERROR(req, err); | |
| 3072 return; | |
| 3073 } | |
| 3074 | |
| 3075 len = MAX_PATH + 1; | |
| 3076 pathw = uv__malloc(len * sizeof(*pathw)); | |
| 3077 if (pathw == NULL) { | |
| 3078 SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); | |
| 3079 return; | |
| 3080 } | |
| 3081 retry_get_full_path_name: | |
| 3082 ret = GetFullPathNameW(req->file.pathw, | |
| 3083 len, | |
| 3084 pathw, | |
| 3085 &fpart); | |
| 3086 if (ret == 0) { | |
| 3087 uv__free(pathw); | |
| 3088 SET_REQ_WIN32_ERROR(req, err); | |
| 3089 return; | |
| 3090 } else if (ret > len) { | |
| 3091 len = ret; | |
| 3092 pathw = uv__reallocf(pathw, len * sizeof(*pathw)); | |
| 3093 if (pathw == NULL) { | |
| 3094 SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); | |
| 3095 return; | |
| 3096 } | |
| 3097 goto retry_get_full_path_name; | |
| 3098 } | |
| 3099 if (fpart != 0) | |
| 3100 *fpart = L'\0'; | |
| 3101 | |
| 3102 goto retry_get_disk_free_space; | |
| 3103 } | |
| 3104 if (pathw != req->file.pathw) { | |
| 3105 uv__free(pathw); | |
| 3106 } | |
| 3107 | |
| 3108 stat_fs = uv__malloc(sizeof(*stat_fs)); | |
| 3109 if (stat_fs == NULL) { | |
| 3110 SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); | |
| 3111 return; | |
| 3112 } | |
| 3113 | |
| 3114 stat_fs->f_type = 0; | |
| 3115 stat_fs->f_bsize = bytes_per_sector * sectors_per_cluster; | |
| 3116 stat_fs->f_blocks = total_clusters; | |
| 3117 stat_fs->f_bfree = free_clusters; | |
| 3118 stat_fs->f_bavail = free_clusters; | |
| 3119 stat_fs->f_files = 0; | |
| 3120 stat_fs->f_ffree = 0; | |
| 3121 req->ptr = stat_fs; | |
| 3122 req->flags |= UV_FS_FREE_PTR; | |
| 3123 SET_REQ_RESULT(req, 0); | |
| 3124 } | |
| 3125 | |
| 3126 | |
| 3127 static void uv__fs_work(struct uv__work* w) { | |
| 3128 uv_fs_t* req; | |
| 3129 | |
| 3130 req = container_of(w, uv_fs_t, work_req); | |
| 3131 assert(req->type == UV_FS); | |
| 3132 | |
| 3133 #define XX(uc, lc) case UV_FS_##uc: fs__##lc(req); break; | |
| 3134 switch (req->fs_type) { | |
| 3135 XX(OPEN, open) | |
| 3136 XX(CLOSE, close) | |
| 3137 XX(READ, read) | |
| 3138 XX(WRITE, write) | |
| 3139 XX(COPYFILE, copyfile) | |
| 3140 XX(SENDFILE, sendfile) | |
| 3141 XX(STAT, stat) | |
| 3142 XX(LSTAT, lstat) | |
| 3143 XX(FSTAT, fstat) | |
| 3144 XX(FTRUNCATE, ftruncate) | |
| 3145 XX(UTIME, utime) | |
| 3146 XX(FUTIME, futime) | |
| 3147 XX(LUTIME, lutime) | |
| 3148 XX(ACCESS, access) | |
| 3149 XX(CHMOD, chmod) | |
| 3150 XX(FCHMOD, fchmod) | |
| 3151 XX(FSYNC, fsync) | |
| 3152 XX(FDATASYNC, fdatasync) | |
| 3153 XX(UNLINK, unlink) | |
| 3154 XX(RMDIR, rmdir) | |
| 3155 XX(MKDIR, mkdir) | |
| 3156 XX(MKDTEMP, mkdtemp) | |
| 3157 XX(MKSTEMP, mkstemp) | |
| 3158 XX(RENAME, rename) | |
| 3159 XX(SCANDIR, scandir) | |
| 3160 XX(READDIR, readdir) | |
| 3161 XX(OPENDIR, opendir) | |
| 3162 XX(CLOSEDIR, closedir) | |
| 3163 XX(LINK, link) | |
| 3164 XX(SYMLINK, symlink) | |
| 3165 XX(READLINK, readlink) | |
| 3166 XX(REALPATH, realpath) | |
| 3167 XX(CHOWN, chown) | |
| 3168 XX(FCHOWN, fchown) | |
| 3169 XX(LCHOWN, lchown) | |
| 3170 XX(STATFS, statfs) | |
| 3171 default: | |
| 3172 assert(!"bad uv_fs_type"); | |
| 3173 } | |
| 3174 } | |
| 3175 | |
| 3176 | |
| 3177 static void uv__fs_done(struct uv__work* w, int status) { | |
| 3178 uv_fs_t* req; | |
| 3179 | |
| 3180 req = container_of(w, uv_fs_t, work_req); | |
| 3181 uv__req_unregister(req->loop); | |
| 3182 | |
| 3183 if (status == UV_ECANCELED) { | |
| 3184 assert(req->result == 0); | |
| 3185 SET_REQ_UV_ERROR(req, UV_ECANCELED, 0); | |
| 3186 } | |
| 3187 | |
| 3188 req->cb(req); | |
| 3189 } | |
| 3190 | |
| 3191 | |
| 3192 void uv_fs_req_cleanup(uv_fs_t* req) { | |
| 3193 if (req == NULL) | |
| 3194 return; | |
| 3195 | |
| 3196 if (req->flags & UV_FS_CLEANEDUP) | |
| 3197 return; | |
| 3198 | |
| 3199 if (req->flags & UV_FS_FREE_PATHS) | |
| 3200 uv__free(req->file.pathw); | |
| 3201 | |
| 3202 if (req->flags & UV_FS_FREE_PTR) { | |
| 3203 if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL) | |
| 3204 uv__fs_scandir_cleanup(req); | |
| 3205 else if (req->fs_type == UV_FS_READDIR) | |
| 3206 uv__fs_readdir_cleanup(req); | |
| 3207 else | |
| 3208 uv__free(req->ptr); | |
| 3209 } | |
| 3210 | |
| 3211 if (req->fs.info.bufs != req->fs.info.bufsml) | |
| 3212 uv__free(req->fs.info.bufs); | |
| 3213 | |
| 3214 req->path = NULL; | |
| 3215 req->file.pathw = NULL; | |
| 3216 req->fs.info.new_pathw = NULL; | |
| 3217 req->fs.info.bufs = NULL; | |
| 3218 req->ptr = NULL; | |
| 3219 | |
| 3220 req->flags |= UV_FS_CLEANEDUP; | |
| 3221 } | |
| 3222 | |
| 3223 | |
| 3224 int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, | |
| 3225 int mode, uv_fs_cb cb) { | |
| 3226 int err; | |
| 3227 | |
| 3228 INIT(UV_FS_OPEN); | |
| 3229 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3230 if (err) { | |
| 3231 SET_REQ_WIN32_ERROR(req, err); | |
| 3232 return req->result; | |
| 3233 } | |
| 3234 | |
| 3235 req->fs.info.file_flags = flags; | |
| 3236 req->fs.info.mode = mode; | |
| 3237 POST; | |
| 3238 } | |
| 3239 | |
| 3240 | |
| 3241 int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { | |
| 3242 INIT(UV_FS_CLOSE); | |
| 3243 req->file.fd = fd; | |
| 3244 POST; | |
| 3245 } | |
| 3246 | |
| 3247 | |
| 3248 int uv_fs_read(uv_loop_t* loop, | |
| 3249 uv_fs_t* req, | |
| 3250 uv_file fd, | |
| 3251 const uv_buf_t bufs[], | |
| 3252 unsigned int nbufs, | |
| 3253 int64_t offset, | |
| 3254 uv_fs_cb cb) { | |
| 3255 INIT(UV_FS_READ); | |
| 3256 | |
| 3257 if (bufs == NULL || nbufs == 0) { | |
| 3258 SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); | |
| 3259 return UV_EINVAL; | |
| 3260 } | |
| 3261 | |
| 3262 req->file.fd = fd; | |
| 3263 | |
| 3264 req->fs.info.nbufs = nbufs; | |
| 3265 req->fs.info.bufs = req->fs.info.bufsml; | |
| 3266 if (nbufs > ARRAY_SIZE(req->fs.info.bufsml)) | |
| 3267 req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs)); | |
| 3268 | |
| 3269 if (req->fs.info.bufs == NULL) { | |
| 3270 SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); | |
| 3271 return UV_ENOMEM; | |
| 3272 } | |
| 3273 | |
| 3274 memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs)); | |
| 3275 | |
| 3276 req->fs.info.offset = offset; | |
| 3277 POST; | |
| 3278 } | |
| 3279 | |
| 3280 | |
| 3281 int uv_fs_write(uv_loop_t* loop, | |
| 3282 uv_fs_t* req, | |
| 3283 uv_file fd, | |
| 3284 const uv_buf_t bufs[], | |
| 3285 unsigned int nbufs, | |
| 3286 int64_t offset, | |
| 3287 uv_fs_cb cb) { | |
| 3288 INIT(UV_FS_WRITE); | |
| 3289 | |
| 3290 if (bufs == NULL || nbufs == 0) { | |
| 3291 SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); | |
| 3292 return UV_EINVAL; | |
| 3293 } | |
| 3294 | |
| 3295 req->file.fd = fd; | |
| 3296 | |
| 3297 req->fs.info.nbufs = nbufs; | |
| 3298 req->fs.info.bufs = req->fs.info.bufsml; | |
| 3299 if (nbufs > ARRAY_SIZE(req->fs.info.bufsml)) | |
| 3300 req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs)); | |
| 3301 | |
| 3302 if (req->fs.info.bufs == NULL) { | |
| 3303 SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); | |
| 3304 return UV_ENOMEM; | |
| 3305 } | |
| 3306 | |
| 3307 memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs)); | |
| 3308 | |
| 3309 req->fs.info.offset = offset; | |
| 3310 POST; | |
| 3311 } | |
| 3312 | |
| 3313 | |
| 3314 int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, | |
| 3315 uv_fs_cb cb) { | |
| 3316 int err; | |
| 3317 | |
| 3318 INIT(UV_FS_UNLINK); | |
| 3319 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3320 if (err) { | |
| 3321 SET_REQ_WIN32_ERROR(req, err); | |
| 3322 return req->result; | |
| 3323 } | |
| 3324 | |
| 3325 POST; | |
| 3326 } | |
| 3327 | |
| 3328 | |
| 3329 int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, | |
| 3330 uv_fs_cb cb) { | |
| 3331 int err; | |
| 3332 | |
| 3333 INIT(UV_FS_MKDIR); | |
| 3334 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3335 if (err) { | |
| 3336 SET_REQ_WIN32_ERROR(req, err); | |
| 3337 return req->result; | |
| 3338 } | |
| 3339 | |
| 3340 req->fs.info.mode = mode; | |
| 3341 POST; | |
| 3342 } | |
| 3343 | |
| 3344 | |
| 3345 int uv_fs_mkdtemp(uv_loop_t* loop, | |
| 3346 uv_fs_t* req, | |
| 3347 const char* tpl, | |
| 3348 uv_fs_cb cb) { | |
| 3349 int err; | |
| 3350 | |
| 3351 INIT(UV_FS_MKDTEMP); | |
| 3352 err = fs__capture_path(req, tpl, NULL, TRUE); | |
| 3353 if (err) { | |
| 3354 SET_REQ_WIN32_ERROR(req, err); | |
| 3355 return req->result; | |
| 3356 } | |
| 3357 | |
| 3358 POST; | |
| 3359 } | |
| 3360 | |
| 3361 | |
| 3362 int uv_fs_mkstemp(uv_loop_t* loop, | |
| 3363 uv_fs_t* req, | |
| 3364 const char* tpl, | |
| 3365 uv_fs_cb cb) { | |
| 3366 int err; | |
| 3367 | |
| 3368 INIT(UV_FS_MKSTEMP); | |
| 3369 err = fs__capture_path(req, tpl, NULL, TRUE); | |
| 3370 if (err) { | |
| 3371 SET_REQ_WIN32_ERROR(req, err); | |
| 3372 return req->result; | |
| 3373 } | |
| 3374 | |
| 3375 POST; | |
| 3376 } | |
| 3377 | |
| 3378 | |
| 3379 int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { | |
| 3380 int err; | |
| 3381 | |
| 3382 INIT(UV_FS_RMDIR); | |
| 3383 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3384 if (err) { | |
| 3385 SET_REQ_WIN32_ERROR(req, err); | |
| 3386 return req->result; | |
| 3387 } | |
| 3388 | |
| 3389 POST; | |
| 3390 } | |
| 3391 | |
| 3392 | |
| 3393 int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, | |
| 3394 uv_fs_cb cb) { | |
| 3395 int err; | |
| 3396 | |
| 3397 INIT(UV_FS_SCANDIR); | |
| 3398 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3399 if (err) { | |
| 3400 SET_REQ_WIN32_ERROR(req, err); | |
| 3401 return req->result; | |
| 3402 } | |
| 3403 | |
| 3404 req->fs.info.file_flags = flags; | |
| 3405 POST; | |
| 3406 } | |
| 3407 | |
| 3408 int uv_fs_opendir(uv_loop_t* loop, | |
| 3409 uv_fs_t* req, | |
| 3410 const char* path, | |
| 3411 uv_fs_cb cb) { | |
| 3412 int err; | |
| 3413 | |
| 3414 INIT(UV_FS_OPENDIR); | |
| 3415 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3416 if (err) { | |
| 3417 SET_REQ_WIN32_ERROR(req, err); | |
| 3418 return req->result; | |
| 3419 } | |
| 3420 POST; | |
| 3421 } | |
| 3422 | |
| 3423 int uv_fs_readdir(uv_loop_t* loop, | |
| 3424 uv_fs_t* req, | |
| 3425 uv_dir_t* dir, | |
| 3426 uv_fs_cb cb) { | |
| 3427 INIT(UV_FS_READDIR); | |
| 3428 | |
| 3429 if (dir == NULL || | |
| 3430 dir->dirents == NULL || | |
| 3431 dir->dir_handle == INVALID_HANDLE_VALUE) { | |
| 3432 SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); | |
| 3433 return UV_EINVAL; | |
| 3434 } | |
| 3435 | |
| 3436 req->ptr = dir; | |
| 3437 POST; | |
| 3438 } | |
| 3439 | |
| 3440 int uv_fs_closedir(uv_loop_t* loop, | |
| 3441 uv_fs_t* req, | |
| 3442 uv_dir_t* dir, | |
| 3443 uv_fs_cb cb) { | |
| 3444 INIT(UV_FS_CLOSEDIR); | |
| 3445 if (dir == NULL) { | |
| 3446 SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); | |
| 3447 return UV_EINVAL; | |
| 3448 } | |
| 3449 req->ptr = dir; | |
| 3450 POST; | |
| 3451 } | |
| 3452 | |
| 3453 int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, | |
| 3454 const char* new_path, uv_fs_cb cb) { | |
| 3455 int err; | |
| 3456 | |
| 3457 INIT(UV_FS_LINK); | |
| 3458 err = fs__capture_path(req, path, new_path, cb != NULL); | |
| 3459 if (err) { | |
| 3460 SET_REQ_WIN32_ERROR(req, err); | |
| 3461 return req->result; | |
| 3462 } | |
| 3463 | |
| 3464 POST; | |
| 3465 } | |
| 3466 | |
| 3467 | |
| 3468 int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, | |
| 3469 const char* new_path, int flags, uv_fs_cb cb) { | |
| 3470 int err; | |
| 3471 | |
| 3472 INIT(UV_FS_SYMLINK); | |
| 3473 err = fs__capture_path(req, path, new_path, cb != NULL); | |
| 3474 if (err) { | |
| 3475 SET_REQ_WIN32_ERROR(req, err); | |
| 3476 return req->result; | |
| 3477 } | |
| 3478 | |
| 3479 req->fs.info.file_flags = flags; | |
| 3480 POST; | |
| 3481 } | |
| 3482 | |
| 3483 | |
| 3484 int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, | |
| 3485 uv_fs_cb cb) { | |
| 3486 int err; | |
| 3487 | |
| 3488 INIT(UV_FS_READLINK); | |
| 3489 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3490 if (err) { | |
| 3491 SET_REQ_WIN32_ERROR(req, err); | |
| 3492 return req->result; | |
| 3493 } | |
| 3494 | |
| 3495 POST; | |
| 3496 } | |
| 3497 | |
| 3498 | |
| 3499 int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, | |
| 3500 uv_fs_cb cb) { | |
| 3501 int err; | |
| 3502 | |
| 3503 INIT(UV_FS_REALPATH); | |
| 3504 | |
| 3505 if (!path) { | |
| 3506 SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); | |
| 3507 return UV_EINVAL; | |
| 3508 } | |
| 3509 | |
| 3510 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3511 if (err) { | |
| 3512 SET_REQ_WIN32_ERROR(req, err); | |
| 3513 return req->result; | |
| 3514 } | |
| 3515 | |
| 3516 POST; | |
| 3517 } | |
| 3518 | |
| 3519 | |
| 3520 int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, | |
| 3521 uv_gid_t gid, uv_fs_cb cb) { | |
| 3522 int err; | |
| 3523 | |
| 3524 INIT(UV_FS_CHOWN); | |
| 3525 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3526 if (err) { | |
| 3527 SET_REQ_WIN32_ERROR(req, err); | |
| 3528 return req->result; | |
| 3529 } | |
| 3530 | |
| 3531 POST; | |
| 3532 } | |
| 3533 | |
| 3534 | |
| 3535 int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_uid_t uid, | |
| 3536 uv_gid_t gid, uv_fs_cb cb) { | |
| 3537 INIT(UV_FS_FCHOWN); | |
| 3538 POST; | |
| 3539 } | |
| 3540 | |
| 3541 | |
| 3542 int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, | |
| 3543 uv_gid_t gid, uv_fs_cb cb) { | |
| 3544 int err; | |
| 3545 | |
| 3546 INIT(UV_FS_LCHOWN); | |
| 3547 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3548 if (err) { | |
| 3549 SET_REQ_WIN32_ERROR(req, err); | |
| 3550 return req->result; | |
| 3551 } | |
| 3552 | |
| 3553 POST; | |
| 3554 } | |
| 3555 | |
| 3556 | |
| 3557 int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { | |
| 3558 int err; | |
| 3559 | |
| 3560 INIT(UV_FS_STAT); | |
| 3561 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3562 if (err) { | |
| 3563 SET_REQ_WIN32_ERROR(req, err); | |
| 3564 return req->result; | |
| 3565 } | |
| 3566 | |
| 3567 POST; | |
| 3568 } | |
| 3569 | |
| 3570 | |
| 3571 int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { | |
| 3572 int err; | |
| 3573 | |
| 3574 INIT(UV_FS_LSTAT); | |
| 3575 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3576 if (err) { | |
| 3577 SET_REQ_WIN32_ERROR(req, err); | |
| 3578 return req->result; | |
| 3579 } | |
| 3580 | |
| 3581 POST; | |
| 3582 } | |
| 3583 | |
| 3584 | |
| 3585 int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { | |
| 3586 INIT(UV_FS_FSTAT); | |
| 3587 req->file.fd = fd; | |
| 3588 POST; | |
| 3589 } | |
| 3590 | |
| 3591 | |
| 3592 int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, | |
| 3593 const char* new_path, uv_fs_cb cb) { | |
| 3594 int err; | |
| 3595 | |
| 3596 INIT(UV_FS_RENAME); | |
| 3597 err = fs__capture_path(req, path, new_path, cb != NULL); | |
| 3598 if (err) { | |
| 3599 SET_REQ_WIN32_ERROR(req, err); | |
| 3600 return req->result; | |
| 3601 } | |
| 3602 | |
| 3603 POST; | |
| 3604 } | |
| 3605 | |
| 3606 | |
| 3607 int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { | |
| 3608 INIT(UV_FS_FSYNC); | |
| 3609 req->file.fd = fd; | |
| 3610 POST; | |
| 3611 } | |
| 3612 | |
| 3613 | |
| 3614 int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { | |
| 3615 INIT(UV_FS_FDATASYNC); | |
| 3616 req->file.fd = fd; | |
| 3617 POST; | |
| 3618 } | |
| 3619 | |
| 3620 | |
| 3621 int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file fd, | |
| 3622 int64_t offset, uv_fs_cb cb) { | |
| 3623 INIT(UV_FS_FTRUNCATE); | |
| 3624 req->file.fd = fd; | |
| 3625 req->fs.info.offset = offset; | |
| 3626 POST; | |
| 3627 } | |
| 3628 | |
| 3629 | |
| 3630 int uv_fs_copyfile(uv_loop_t* loop, | |
| 3631 uv_fs_t* req, | |
| 3632 const char* path, | |
| 3633 const char* new_path, | |
| 3634 int flags, | |
| 3635 uv_fs_cb cb) { | |
| 3636 int err; | |
| 3637 | |
| 3638 INIT(UV_FS_COPYFILE); | |
| 3639 | |
| 3640 if (flags & ~(UV_FS_COPYFILE_EXCL | | |
| 3641 UV_FS_COPYFILE_FICLONE | | |
| 3642 UV_FS_COPYFILE_FICLONE_FORCE)) { | |
| 3643 SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); | |
| 3644 return UV_EINVAL; | |
| 3645 } | |
| 3646 | |
| 3647 err = fs__capture_path(req, path, new_path, cb != NULL); | |
| 3648 if (err) { | |
| 3649 SET_REQ_WIN32_ERROR(req, err); | |
| 3650 return req->result; | |
| 3651 } | |
| 3652 | |
| 3653 req->fs.info.file_flags = flags; | |
| 3654 POST; | |
| 3655 } | |
| 3656 | |
| 3657 | |
| 3658 int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file fd_out, | |
| 3659 uv_file fd_in, int64_t in_offset, size_t length, uv_fs_cb cb) { | |
| 3660 INIT(UV_FS_SENDFILE); | |
| 3661 req->file.fd = fd_in; | |
| 3662 req->fs.info.fd_out = fd_out; | |
| 3663 req->fs.info.offset = in_offset; | |
| 3664 req->fs.info.bufsml[0].len = length; | |
| 3665 POST; | |
| 3666 } | |
| 3667 | |
| 3668 | |
| 3669 int uv_fs_access(uv_loop_t* loop, | |
| 3670 uv_fs_t* req, | |
| 3671 const char* path, | |
| 3672 int flags, | |
| 3673 uv_fs_cb cb) { | |
| 3674 int err; | |
| 3675 | |
| 3676 INIT(UV_FS_ACCESS); | |
| 3677 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3678 if (err) { | |
| 3679 SET_REQ_WIN32_ERROR(req, err); | |
| 3680 return req->result; | |
| 3681 } | |
| 3682 | |
| 3683 req->fs.info.mode = flags; | |
| 3684 POST; | |
| 3685 } | |
| 3686 | |
| 3687 | |
| 3688 int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, | |
| 3689 uv_fs_cb cb) { | |
| 3690 int err; | |
| 3691 | |
| 3692 INIT(UV_FS_CHMOD); | |
| 3693 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3694 if (err) { | |
| 3695 SET_REQ_WIN32_ERROR(req, err); | |
| 3696 return req->result; | |
| 3697 } | |
| 3698 | |
| 3699 req->fs.info.mode = mode; | |
| 3700 POST; | |
| 3701 } | |
| 3702 | |
| 3703 | |
| 3704 int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int mode, | |
| 3705 uv_fs_cb cb) { | |
| 3706 INIT(UV_FS_FCHMOD); | |
| 3707 req->file.fd = fd; | |
| 3708 req->fs.info.mode = mode; | |
| 3709 POST; | |
| 3710 } | |
| 3711 | |
| 3712 | |
| 3713 int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, | |
| 3714 double mtime, uv_fs_cb cb) { | |
| 3715 int err; | |
| 3716 | |
| 3717 INIT(UV_FS_UTIME); | |
| 3718 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3719 if (err) { | |
| 3720 SET_REQ_WIN32_ERROR(req, err); | |
| 3721 return req->result; | |
| 3722 } | |
| 3723 | |
| 3724 req->fs.time.atime = atime; | |
| 3725 req->fs.time.mtime = mtime; | |
| 3726 POST; | |
| 3727 } | |
| 3728 | |
| 3729 | |
| 3730 int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime, | |
| 3731 double mtime, uv_fs_cb cb) { | |
| 3732 INIT(UV_FS_FUTIME); | |
| 3733 req->file.fd = fd; | |
| 3734 req->fs.time.atime = atime; | |
| 3735 req->fs.time.mtime = mtime; | |
| 3736 POST; | |
| 3737 } | |
| 3738 | |
| 3739 int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, | |
| 3740 double mtime, uv_fs_cb cb) { | |
| 3741 int err; | |
| 3742 | |
| 3743 INIT(UV_FS_LUTIME); | |
| 3744 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3745 if (err) { | |
| 3746 SET_REQ_WIN32_ERROR(req, err); | |
| 3747 return req->result; | |
| 3748 } | |
| 3749 | |
| 3750 req->fs.time.atime = atime; | |
| 3751 req->fs.time.mtime = mtime; | |
| 3752 POST; | |
| 3753 } | |
| 3754 | |
| 3755 | |
| 3756 int uv_fs_statfs(uv_loop_t* loop, | |
| 3757 uv_fs_t* req, | |
| 3758 const char* path, | |
| 3759 uv_fs_cb cb) { | |
| 3760 int err; | |
| 3761 | |
| 3762 INIT(UV_FS_STATFS); | |
| 3763 err = fs__capture_path(req, path, NULL, cb != NULL); | |
| 3764 if (err) { | |
| 3765 SET_REQ_WIN32_ERROR(req, err); | |
| 3766 return req->result; | |
| 3767 } | |
| 3768 | |
| 3769 POST; | |
| 3770 } | |
| 3771 | |
| 3772 int uv_fs_get_system_error(const uv_fs_t* req) { | |
| 3773 return req->sys_errno_; | |
| 3774 } |