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