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