libuv-svace-build
598 строк · 17.2 Кб
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 <errno.h>24#include <stdio.h>25#include <string.h>26
27#include "uv.h"28#include "internal.h"29#include "handle-inl.h"30#include "req-inl.h"31
32
33const unsigned int uv_directory_watcher_buffer_size = 4096;34
35
36static void uv__fs_event_queue_readdirchanges(uv_loop_t* loop,37uv_fs_event_t* handle) {38assert(handle->dir_handle != INVALID_HANDLE_VALUE);39assert(!handle->req_pending);40
41memset(&(handle->req.u.io.overlapped), 0,42sizeof(handle->req.u.io.overlapped));43if (!ReadDirectoryChangesW(handle->dir_handle,44handle->buffer,45uv_directory_watcher_buffer_size,46(handle->flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,47FILE_NOTIFY_CHANGE_FILE_NAME |48FILE_NOTIFY_CHANGE_DIR_NAME |49FILE_NOTIFY_CHANGE_ATTRIBUTES |50FILE_NOTIFY_CHANGE_SIZE |51FILE_NOTIFY_CHANGE_LAST_WRITE |52FILE_NOTIFY_CHANGE_LAST_ACCESS |53FILE_NOTIFY_CHANGE_CREATION |54FILE_NOTIFY_CHANGE_SECURITY,55NULL,56&handle->req.u.io.overlapped,57NULL)) {58/* Make this req pending reporting an error. */59SET_REQ_ERROR(&handle->req, GetLastError());60uv__insert_pending_req(loop, (uv_req_t*)&handle->req);61}62
63handle->req_pending = 1;64}
65
66static void uv__relative_path(const WCHAR* filename,67const WCHAR* dir,68WCHAR** relpath) {69size_t relpathlen;70size_t filenamelen = wcslen(filename);71size_t dirlen = wcslen(dir);72assert(!_wcsnicmp(filename, dir, dirlen));73if (dirlen > 0 && dir[dirlen - 1] == '\\')74dirlen--;75relpathlen = filenamelen - dirlen - 1;76*relpath = uv__malloc((relpathlen + 1) * sizeof(WCHAR));77if (!*relpath)78uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");79wcsncpy(*relpath, filename + dirlen + 1, relpathlen);80(*relpath)[relpathlen] = L'\0';81}
82
83static int uv__split_path(const WCHAR* filename, WCHAR** dir,84WCHAR** file) {85size_t len, i;86DWORD dir_len;87
88if (filename == NULL) {89if (dir != NULL)90*dir = NULL;91*file = NULL;92return 0;93}94
95len = wcslen(filename);96i = len;97while (i > 0 && filename[--i] != '\\' && filename[i] != '/');98
99if (i == 0) {100if (dir) {101dir_len = GetCurrentDirectoryW(0, NULL);102if (dir_len == 0) {103return -1;104}105*dir = (WCHAR*)uv__malloc(dir_len * sizeof(WCHAR));106if (!*dir) {107uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");108}109
110if (!GetCurrentDirectoryW(dir_len, *dir)) {111uv__free(*dir);112*dir = NULL;113return -1;114}115}116
117*file = _wcsdup(filename);118} else {119if (dir) {120*dir = (WCHAR*)uv__malloc((i + 2) * sizeof(WCHAR));121if (!*dir) {122uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");123}124wcsncpy(*dir, filename, i + 1);125(*dir)[i + 1] = L'\0';126}127
128*file = (WCHAR*)uv__malloc((len - i) * sizeof(WCHAR));129if (!*file) {130uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");131}132wcsncpy(*file, filename + i + 1, len - i - 1);133(*file)[len - i - 1] = L'\0';134}135
136return 0;137}
138
139
140int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {141uv__handle_init(loop, (uv_handle_t*) handle, UV_FS_EVENT);142handle->dir_handle = INVALID_HANDLE_VALUE;143handle->buffer = NULL;144handle->req_pending = 0;145handle->filew = NULL;146handle->short_filew = NULL;147handle->dirw = NULL;148
149UV_REQ_INIT(&handle->req, UV_FS_EVENT_REQ);150handle->req.data = handle;151
152return 0;153}
154
155
156int uv_fs_event_start(uv_fs_event_t* handle,157uv_fs_event_cb cb,158const char* path,159unsigned int flags) {160int is_path_dir;161size_t size;162DWORD attr, last_error;163WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL;164DWORD short_path_buffer_len;165WCHAR *short_path_buffer;166WCHAR* short_path, *long_path;167
168short_path = NULL;169if (uv__is_active(handle))170return UV_EINVAL;171
172handle->cb = cb;173handle->path = uv__strdup(path);174if (!handle->path) {175uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");176}177
178uv__handle_start(handle);179
180last_error = uv__convert_utf8_to_utf16(path, &pathw);181if (last_error)182goto error_uv;183
184/* Determine whether path is a file or a directory. */185attr = GetFileAttributesW(pathw);186if (attr == INVALID_FILE_ATTRIBUTES) {187last_error = GetLastError();188goto error;189}190
191is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;192
193if (is_path_dir) {194/* path is a directory, so that's the directory that we will watch. */195
196/* Convert to long path. */197size = GetLongPathNameW(pathw, NULL, 0);198
199if (size) {200long_path = (WCHAR*)uv__malloc(size * sizeof(WCHAR));201if (!long_path) {202uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");203}204
205size = GetLongPathNameW(pathw, long_path, size);206if (size) {207long_path[size] = '\0';208} else {209uv__free(long_path);210long_path = NULL;211}212
213if (long_path) {214uv__free(pathw);215pathw = long_path;216}217}218
219dir_to_watch = pathw;220} else {221/*222* path is a file. So we split path into dir & file parts, and
223* watch the dir directory.
224*/
225
226/* Convert to short path. */227short_path_buffer = NULL;228short_path_buffer_len = GetShortPathNameW(pathw, NULL, 0);229if (short_path_buffer_len == 0) {230goto short_path_done;231}232short_path_buffer = uv__malloc(short_path_buffer_len * sizeof(WCHAR));233if (short_path_buffer == NULL) {234goto short_path_done;235}236if (GetShortPathNameW(pathw,237short_path_buffer,238short_path_buffer_len) == 0) {239uv__free(short_path_buffer);240short_path_buffer = NULL;241}242short_path_done:243short_path = short_path_buffer;244
245if (uv__split_path(pathw, &dir, &handle->filew) != 0) {246last_error = GetLastError();247goto error;248}249
250if (uv__split_path(short_path, NULL, &handle->short_filew) != 0) {251last_error = GetLastError();252goto error;253}254
255dir_to_watch = dir;256uv__free(pathw);257pathw = NULL;258}259
260handle->dir_handle = CreateFileW(dir_to_watch,261FILE_LIST_DIRECTORY,262FILE_SHARE_READ | FILE_SHARE_DELETE |263FILE_SHARE_WRITE,264NULL,265OPEN_EXISTING,266FILE_FLAG_BACKUP_SEMANTICS |267FILE_FLAG_OVERLAPPED,268NULL);269
270if (dir) {271uv__free(dir);272dir = NULL;273}274
275if (handle->dir_handle == INVALID_HANDLE_VALUE) {276last_error = GetLastError();277goto error;278}279
280if (CreateIoCompletionPort(handle->dir_handle,281handle->loop->iocp,282(ULONG_PTR)handle,2830) == NULL) {284last_error = GetLastError();285goto error;286}287
288if (!handle->buffer) {289handle->buffer = (char*)uv__malloc(uv_directory_watcher_buffer_size);290}291if (!handle->buffer) {292uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");293}294
295memset(&(handle->req.u.io.overlapped), 0,296sizeof(handle->req.u.io.overlapped));297
298if (!ReadDirectoryChangesW(handle->dir_handle,299handle->buffer,300uv_directory_watcher_buffer_size,301(flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,302FILE_NOTIFY_CHANGE_FILE_NAME |303FILE_NOTIFY_CHANGE_DIR_NAME |304FILE_NOTIFY_CHANGE_ATTRIBUTES |305FILE_NOTIFY_CHANGE_SIZE |306FILE_NOTIFY_CHANGE_LAST_WRITE |307FILE_NOTIFY_CHANGE_LAST_ACCESS |308FILE_NOTIFY_CHANGE_CREATION |309FILE_NOTIFY_CHANGE_SECURITY,310NULL,311&handle->req.u.io.overlapped,312NULL)) {313last_error = GetLastError();314goto error;315}316
317assert(is_path_dir ? pathw != NULL : pathw == NULL);318handle->dirw = pathw;319handle->req_pending = 1;320return 0;321
322error:323last_error = uv_translate_sys_error(last_error);324
325error_uv:326if (handle->path) {327uv__free(handle->path);328handle->path = NULL;329}330
331if (handle->filew) {332uv__free(handle->filew);333handle->filew = NULL;334}335
336if (handle->short_filew) {337uv__free(handle->short_filew);338handle->short_filew = NULL;339}340
341uv__free(pathw);342
343if (handle->dir_handle != INVALID_HANDLE_VALUE) {344CloseHandle(handle->dir_handle);345handle->dir_handle = INVALID_HANDLE_VALUE;346}347
348if (handle->buffer) {349uv__free(handle->buffer);350handle->buffer = NULL;351}352
353if (uv__is_active(handle))354uv__handle_stop(handle);355
356uv__free(short_path);357
358return last_error;359}
360
361
362int uv_fs_event_stop(uv_fs_event_t* handle) {363if (!uv__is_active(handle))364return 0;365
366if (handle->dir_handle != INVALID_HANDLE_VALUE) {367CloseHandle(handle->dir_handle);368handle->dir_handle = INVALID_HANDLE_VALUE;369}370
371uv__handle_stop(handle);372
373if (handle->filew) {374uv__free(handle->filew);375handle->filew = NULL;376}377
378if (handle->short_filew) {379uv__free(handle->short_filew);380handle->short_filew = NULL;381}382
383if (handle->path) {384uv__free(handle->path);385handle->path = NULL;386}387
388if (handle->dirw) {389uv__free(handle->dirw);390handle->dirw = NULL;391}392
393return 0;394}
395
396
397static int file_info_cmp(WCHAR* str, WCHAR* file_name, size_t file_name_len) {398size_t str_len;399
400if (str == NULL)401return -1;402
403str_len = wcslen(str);404
405/*406Since we only care about equality, return early if the strings
407aren't the same length
408*/
409if (str_len != (file_name_len / sizeof(WCHAR)))410return -1;411
412return _wcsnicmp(str, file_name, str_len);413}
414
415
416void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req,417uv_fs_event_t* handle) {418FILE_NOTIFY_INFORMATION* file_info;419int err, sizew, size;420char* filename = NULL;421WCHAR* filenamew = NULL;422WCHAR* long_filenamew = NULL;423DWORD offset = 0;424
425assert(req->type == UV_FS_EVENT_REQ);426assert(handle->req_pending);427handle->req_pending = 0;428
429/* Don't report any callbacks if:430* - We're closing, just push the handle onto the endgame queue
431* - We are not active, just ignore the callback
432*/
433if (!uv__is_active(handle)) {434if (handle->flags & UV_HANDLE_CLOSING) {435uv__want_endgame(loop, (uv_handle_t*) handle);436}437return;438}439
440file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset);441
442if (REQ_SUCCESS(req)) {443if (req->u.io.overlapped.InternalHigh > 0) {444do {445file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset);446assert(!filename);447assert(!filenamew);448assert(!long_filenamew);449
450/*451* Fire the event only if we were asked to watch a directory,
452* or if the filename filter matches.
453*/
454if (handle->dirw ||455file_info_cmp(handle->filew,456file_info->FileName,457file_info->FileNameLength) == 0 ||458file_info_cmp(handle->short_filew,459file_info->FileName,460file_info->FileNameLength) == 0) {461
462if (handle->dirw) {463/*464* We attempt to resolve the long form of the file name explicitly.
465* We only do this for file names that might still exist on disk.
466* If this fails, we use the name given by ReadDirectoryChangesW.
467* This may be the long form or the 8.3 short name in some cases.
468*/
469if (file_info->Action != FILE_ACTION_REMOVED &&470file_info->Action != FILE_ACTION_RENAMED_OLD_NAME) {471/* Construct a full path to the file. */472size = wcslen(handle->dirw) +473file_info->FileNameLength / sizeof(WCHAR) + 2;474
475filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));476if (!filenamew) {477uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");478}479
480_snwprintf(filenamew, size, L"%s\\%.*s", handle->dirw,481file_info->FileNameLength / (DWORD)sizeof(WCHAR),482file_info->FileName);483
484filenamew[size - 1] = L'\0';485
486/* Convert to long name. */487size = GetLongPathNameW(filenamew, NULL, 0);488
489if (size) {490long_filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));491if (!long_filenamew) {492uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");493}494
495size = GetLongPathNameW(filenamew, long_filenamew, size);496if (size) {497long_filenamew[size] = '\0';498} else {499uv__free(long_filenamew);500long_filenamew = NULL;501}502}503
504uv__free(filenamew);505
506if (long_filenamew) {507/* Get the file name out of the long path. */508uv__relative_path(long_filenamew,509handle->dirw,510&filenamew);511uv__free(long_filenamew);512long_filenamew = filenamew;513sizew = -1;514} else {515/* We couldn't get the long filename, use the one reported. */516filenamew = file_info->FileName;517sizew = file_info->FileNameLength / sizeof(WCHAR);518}519} else {520/*521* Removed or renamed events cannot be resolved to the long form.
522* We therefore use the name given by ReadDirectoryChangesW.
523* This may be the long form or the 8.3 short name in some cases.
524*/
525filenamew = file_info->FileName;526sizew = file_info->FileNameLength / sizeof(WCHAR);527}528} else {529/* We already have the long name of the file, so just use it. */530filenamew = handle->filew;531sizew = -1;532}533
534/* Convert the filename to utf8. */535uv__convert_utf16_to_utf8(filenamew, sizew, &filename);536
537switch (file_info->Action) {538case FILE_ACTION_ADDED:539case FILE_ACTION_REMOVED:540case FILE_ACTION_RENAMED_OLD_NAME:541case FILE_ACTION_RENAMED_NEW_NAME:542handle->cb(handle, filename, UV_RENAME, 0);543break;544
545case FILE_ACTION_MODIFIED:546handle->cb(handle, filename, UV_CHANGE, 0);547break;548}549
550uv__free(filename);551filename = NULL;552uv__free(long_filenamew);553long_filenamew = NULL;554filenamew = NULL;555}556
557offset = file_info->NextEntryOffset;558} while (offset && !(handle->flags & UV_HANDLE_CLOSING));559} else {560handle->cb(handle, NULL, UV_CHANGE, 0);561}562} else {563err = GET_REQ_ERROR(req);564handle->cb(handle, NULL, 0, uv_translate_sys_error(err));565}566
567if (handle->flags & UV_HANDLE_CLOSING) {568uv__want_endgame(loop, (uv_handle_t*)handle);569} else if (uv__is_active(handle)) {570uv__fs_event_queue_readdirchanges(loop, handle);571}572}
573
574
575void uv__fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) {576uv_fs_event_stop(handle);577
578uv__handle_closing(handle);579
580if (!handle->req_pending) {581uv__want_endgame(loop, (uv_handle_t*)handle);582}583
584}
585
586
587void uv__fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {588if ((handle->flags & UV_HANDLE_CLOSING) && !handle->req_pending) {589assert(!(handle->flags & UV_HANDLE_CLOSED));590
591if (handle->buffer) {592uv__free(handle->buffer);593handle->buffer = NULL;594}595
596uv__handle_close(handle);597}598}
599