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