Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Kitware
GitHub Repository: Kitware/CMake
Path: blob/master/Utilities/cmlibuv/src/win/fs.c
3153 views
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 */
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
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
1305
void fs__mkdtemp(uv_fs_t* req) {
1306
fs__mktemp(req, fs__mkdtemp_func);
1307
}
1308
1309
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
1358
void fs__mkstemp(uv_fs_t* req) {
1359
fs__mktemp(req, fs__mkstemp_func);
1360
}
1361
1362
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
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
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
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
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
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
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
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
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
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
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
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
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
1978
static void fs__fsync(uv_fs_t* req) {
1979
fs__sync_impl(req);
1980
}
1981
1982
1983
static void fs__fdatasync(uv_fs_t* req) {
1984
fs__sync_impl(req);
1985
}
1986
1987
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
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
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
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
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
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
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
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
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
2340
static void fs__utime(uv_fs_t* req) {
2341
fs__utime_impl(req, /* do_lutime */ 0);
2342
}
2343
2344
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
2365
static void fs__lutime(uv_fs_t* req) {
2366
fs__utime_impl(req, /* do_lutime */ 1);
2367
}
2368
2369
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
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
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
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
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
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
2694
static void fs__chown(uv_fs_t* req) {
2695
SET_REQ_RESULT(req, 0);
2696
}
2697
2698
2699
static void fs__fchown(uv_fs_t* req) {
2700
SET_REQ_RESULT(req, 0);
2701
}
2702
2703
2704
static void fs__lchown(uv_fs_t* req) {
2705
SET_REQ_RESULT(req, 0);
2706
}
2707
2708
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
&sectors_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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
3437
int uv_fs_get_system_error(const uv_fs_t* req) {
3438
return req->sys_errno_;
3439
}
3440
3441