Example #1
0
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle,
    const char* filename, uv_fs_event_cb cb) {
  int name_size;
  DWORD attr, last_error;
  wchar_t* dir = NULL, *dir_to_watch, *filenamew;
  wchar_t short_path[MAX_PATH];

  uv_fs_event_init_handle(loop, handle, filename, cb);

  /* Convert name to UTF16. */
  name_size = uv_utf8_to_utf16(filename, NULL, 0) * sizeof(wchar_t);
  filenamew = (wchar_t*)malloc(name_size);
  if (!filenamew) {
    uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
  }

  if (!uv_utf8_to_utf16(filename, filenamew, 
      name_size / sizeof(wchar_t))) {
    uv__set_sys_error(loop, GetLastError());
    return -1;
  }

  /* Determine whether filename is a file or a directory. */
  attr = GetFileAttributesW(filenamew);
  if (attr == INVALID_FILE_ATTRIBUTES) {
    last_error = GetLastError();
    goto error;
  }

  handle->is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;

  if (handle->is_path_dir) {
     /* filename is a directory, so that's the directory that we will watch. */
    dir_to_watch = filenamew;
  } else {
    /* 
     * filename is a file.  So we split filename into dir & file parts, and
     * watch the dir directory.
     */
    
    /* Convert to short path. */
    if (!GetShortPathNameW(filenamew, short_path, COUNTOF(short_path))) {
      last_error = GetLastError();
      goto error;
    }

    if (uv_split_path(filenamew, &dir, &handle->filew) != 0) {
      last_error = GetLastError();
      goto error;
    }

    if (uv_split_path(short_path, NULL, &handle->short_filew) != 0) {
      last_error = GetLastError();
      goto error;
    }

    dir_to_watch = dir;
  }

  handle->dir_handle = CreateFileW(dir_to_watch,
                                   FILE_LIST_DIRECTORY,
                                   FILE_SHARE_READ | FILE_SHARE_DELETE |
                                     FILE_SHARE_WRITE,
                                   NULL,
                                   OPEN_EXISTING,
                                   FILE_FLAG_BACKUP_SEMANTICS |
                                     FILE_FLAG_OVERLAPPED,
                                   NULL);

  if (dir) {
    free(dir);
    dir = NULL;
  }

  if (handle->dir_handle == INVALID_HANDLE_VALUE) {
    last_error = GetLastError();
    goto error;
  }

  if (CreateIoCompletionPort(handle->dir_handle,
                             loop->iocp,
                             (ULONG_PTR)handle,
                             0) == NULL) {
    last_error = GetLastError();
    goto error;
  }

  handle->buffer = (char*)_aligned_malloc(uv_directory_watcher_buffer_size, 
    sizeof(DWORD));
  if (!handle->buffer) {
    uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
  }

  memset(&(handle->req.overlapped), 0, sizeof(handle->req.overlapped));

  if (!ReadDirectoryChangesW(handle->dir_handle,
                             handle->buffer,
                             uv_directory_watcher_buffer_size,
                             FALSE,
                             FILE_NOTIFY_CHANGE_FILE_NAME      |
                               FILE_NOTIFY_CHANGE_DIR_NAME     |
                               FILE_NOTIFY_CHANGE_ATTRIBUTES   |
                               FILE_NOTIFY_CHANGE_SIZE         |
                               FILE_NOTIFY_CHANGE_LAST_WRITE   |
                               FILE_NOTIFY_CHANGE_LAST_ACCESS  |
                               FILE_NOTIFY_CHANGE_CREATION     |
                               FILE_NOTIFY_CHANGE_SECURITY,
                             NULL,
                             &handle->req.overlapped,
                             NULL)) {
    last_error = GetLastError();
    goto error;
  }

  handle->req_pending = 1;
  return 0;

error:
  if (handle->filename) {
    free(handle->filename);
    handle->filename = NULL;
  }

  if (handle->filew) {
    free(handle->filew);
    handle->filew = NULL;
  }

  if (handle->short_filew) {
    free(handle->short_filew);
    handle->short_filew = NULL;
  }

  if (handle->dir_handle != INVALID_HANDLE_VALUE) {
    CloseHandle(handle->dir_handle);
    handle->dir_handle = INVALID_HANDLE_VALUE;
  }

  if (handle->buffer) {
    _aligned_free(handle->buffer);
    handle->buffer = NULL;
  }

  uv__set_sys_error(loop, last_error);
  return -1;
}
int uv_fs_event_start(uv_fs_event_t* handle,
                      uv_fs_event_cb cb,
                      const char* path,
                      unsigned int flags) {
  int name_size, is_path_dir;
  DWORD attr, last_error;
  WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL;
  WCHAR short_path[MAX_PATH];

  if (uv__is_active(handle))
    return UV_EINVAL;

  handle->cb = cb;
  handle->path = uv__strdup(path);
  if (!handle->path) {
    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
  }

  uv__handle_start(handle);

  /* Convert name to UTF16. */
  name_size = uv_utf8_to_utf16(path, NULL, 0) * sizeof(WCHAR);
  pathw = (WCHAR*)uv__malloc(name_size);
  if (!pathw) {
    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
  }

  if (!uv_utf8_to_utf16(path, pathw,
      name_size / sizeof(WCHAR))) {
    return uv_translate_sys_error(GetLastError());
  }

  /* Determine whether path is a file or a directory. */
  attr = GetFileAttributesW(pathw);
  if (attr == INVALID_FILE_ATTRIBUTES) {
    last_error = GetLastError();
    goto error;
  }

  is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;

  if (is_path_dir) {
     /* path is a directory, so that's the directory that we will watch. */
    handle->dirw = pathw;
    dir_to_watch = pathw;
  } else {
    /*
     * path is a file.  So we split path into dir & file parts, and
     * watch the dir directory.
     */

    /* Convert to short path. */
    if (!GetShortPathNameW(pathw, short_path, ARRAY_SIZE(short_path))) {
      last_error = GetLastError();
      goto error;
    }

    if (uv_split_path(pathw, &dir, &handle->filew) != 0) {
      last_error = GetLastError();
      goto error;
    }

    if (uv_split_path(short_path, NULL, &handle->short_filew) != 0) {
      last_error = GetLastError();
      goto error;
    }

    dir_to_watch = dir;
    uv__free(pathw);
    pathw = NULL;
  }

  handle->dir_handle = CreateFileW(dir_to_watch,
                                   FILE_LIST_DIRECTORY,
                                   FILE_SHARE_READ | FILE_SHARE_DELETE |
                                     FILE_SHARE_WRITE,
                                   NULL,
                                   OPEN_EXISTING,
                                   FILE_FLAG_BACKUP_SEMANTICS |
                                     FILE_FLAG_OVERLAPPED,
                                   NULL);

  if (dir) {
    uv__free(dir);
    dir = NULL;
  }

  if (handle->dir_handle == INVALID_HANDLE_VALUE) {
    last_error = GetLastError();
    goto error;
  }

  if (CreateIoCompletionPort(handle->dir_handle,
                             handle->loop->iocp,
                             (ULONG_PTR)handle,
                             0) == NULL) {
    last_error = GetLastError();
    goto error;
  }

  if (!handle->buffer) {
    handle->buffer = (char*)uv__malloc(uv_directory_watcher_buffer_size);
  }
  if (!handle->buffer) {
    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
  }

  memset(&(handle->req.u.io.overlapped), 0,
         sizeof(handle->req.u.io.overlapped));

  if (!ReadDirectoryChangesW(handle->dir_handle,
                             handle->buffer,
                             uv_directory_watcher_buffer_size,
                             (flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
                             FILE_NOTIFY_CHANGE_FILE_NAME      |
                               FILE_NOTIFY_CHANGE_DIR_NAME     |
                               FILE_NOTIFY_CHANGE_ATTRIBUTES   |
                               FILE_NOTIFY_CHANGE_SIZE         |
                               FILE_NOTIFY_CHANGE_LAST_WRITE   |
                               FILE_NOTIFY_CHANGE_LAST_ACCESS  |
                               FILE_NOTIFY_CHANGE_CREATION     |
                               FILE_NOTIFY_CHANGE_SECURITY,
                             NULL,
                             &handle->req.u.io.overlapped,
                             NULL)) {
    last_error = GetLastError();
    goto error;
  }

  handle->req_pending = 1;
  return 0;

error:
  if (handle->path) {
    uv__free(handle->path);
    handle->path = NULL;
  }

  if (handle->filew) {
    uv__free(handle->filew);
    handle->filew = NULL;
  }

  if (handle->short_filew) {
    uv__free(handle->short_filew);
    handle->short_filew = NULL;
  }

  uv__free(pathw);

  if (handle->dir_handle != INVALID_HANDLE_VALUE) {
    CloseHandle(handle->dir_handle);
    handle->dir_handle = INVALID_HANDLE_VALUE;
  }

  if (handle->buffer) {
    uv__free(handle->buffer);
    handle->buffer = NULL;
  }

  return uv_translate_sys_error(last_error);
}
Example #3
0
void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
    uv_fs_event_t* handle) {
  FILE_NOTIFY_INFORMATION* file_info;
  int sizew, size, result;
  char* filename = NULL;
  wchar_t* filenamew, *long_filenamew = NULL;
  DWORD offset = 0;

  assert(req->type == UV_FS_EVENT_REQ);
  assert(handle->req_pending);
  handle->req_pending = 0;

  /* If we're closing, don't report any callbacks, and just push the handle */
  /* onto the endgame queue. */
  if (handle->flags & UV_HANDLE_CLOSING) {
    uv_want_endgame(loop, (uv_handle_t*) handle);
    return;
  };

  file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset);

  if (REQ_SUCCESS(req)) {
    if (req->overlapped.InternalHigh > 0) {
      do {
        file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset);
        assert(!filename);
        assert(!long_filenamew);

        /*
         * Fire the event only if we were asked to watch a directory,
         * or if the filename filter matches.
         */
        if (handle->dirw ||
          _wcsnicmp(handle->filew, file_info->FileName,
            file_info->FileNameLength / sizeof(wchar_t)) == 0 ||
          _wcsnicmp(handle->short_filew, file_info->FileName,
            file_info->FileNameLength / sizeof(wchar_t)) == 0) {

          if (handle->dirw) {
            /*
             * We attempt to convert the file name to its long form for
             * events that still point to valid files on disk.
             * For removed and renamed events, we do not provide the file name.
             */
            if (file_info->Action != FILE_ACTION_REMOVED &&
              file_info->Action != FILE_ACTION_RENAMED_OLD_NAME) {
              /* Construct a full path to the file. */
              size = wcslen(handle->dirw) +
                file_info->FileNameLength / sizeof(wchar_t) + 2;

              filenamew = (wchar_t*)malloc(size * sizeof(wchar_t));
              if (!filenamew) {
                uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
              }

              _snwprintf(filenamew, size, L"%s\\%s", handle->dirw,
                file_info->FileName);

              filenamew[size - 1] = L'\0';

              /* Convert to long name. */
              size = GetLongPathNameW(filenamew, NULL, 0);

              if (size) {
                long_filenamew = (wchar_t*)malloc(size * sizeof(wchar_t));
                if (!long_filenamew) {
                  uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
                }

                size = GetLongPathNameW(filenamew, long_filenamew, size);
                if (size) {
                  long_filenamew[size] = '\0';
                } else {
                  free(long_filenamew);
                  long_filenamew = NULL;
                }
              }

              free(filenamew);

              if (long_filenamew) {
                /* Get the file name out of the long path. */
                result = uv_split_path(long_filenamew, NULL, &filenamew);
                free(long_filenamew);

                if (result == 0) {
                  long_filenamew = filenamew;
                  sizew = -1;
                } else {
                  long_filenamew = NULL;
                }
              }

              /*
               * If we couldn't get the long name - just use the name
               * provided by ReadDirectoryChangesW.
               */
              if (!long_filenamew) {
                filenamew = file_info->FileName;
                sizew = file_info->FileNameLength / sizeof(wchar_t);
              }
            } else {
              /* Removed or renamed callbacks don't provide filename. */
              filenamew = NULL;
            }
          } else {
            /* We already have the long name of the file, so just use it. */
            filenamew = handle->filew;
            sizew = -1;
          }

          if (filenamew) {
            /* Convert the filename to utf8. */
            size = uv_utf16_to_utf8(filenamew,
                                    sizew,
                                    NULL,
                                    0);
            if (size) {
              filename = (char*)malloc(size + 1);
              if (!filename) {
                uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
              }

              size = uv_utf16_to_utf8(filenamew,
                                      sizew,
                                      filename,
                                      size);
              if (size) {
                filename[size] = '\0';
              } else {
                free(filename);
                filename = NULL;
              }
            }
          }

          switch (file_info->Action) {
            case FILE_ACTION_ADDED:
            case FILE_ACTION_REMOVED:
            case FILE_ACTION_RENAMED_OLD_NAME:
            case FILE_ACTION_RENAMED_NEW_NAME:
              handle->cb(handle, filename, UV_RENAME, 0);
              break;

            case FILE_ACTION_MODIFIED:
              handle->cb(handle, filename, UV_CHANGE, 0);
              break;
          }

          free(filename);
          filename = NULL;
          free(long_filenamew);
          long_filenamew = NULL;
        }

        offset = file_info->NextEntryOffset;
      } while (offset && !(handle->flags & UV_HANDLE_CLOSING));
    } else {
      handle->cb(handle, NULL, UV_CHANGE, 0);
    }
  } else {
    uv__set_sys_error(loop, GET_REQ_ERROR(req));
    handle->cb(handle, NULL, 0, -1);
  }

  if (!(handle->flags & UV_HANDLE_CLOSING)) {
    uv_fs_event_queue_readdirchanges(loop, handle);
  } else {
    uv_want_endgame(loop, (uv_handle_t*)handle);
  }
}