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