Example #1
0
static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop,
    uv_fs_event_t* handle) {
  assert(handle->dir_handle != INVALID_HANDLE_VALUE);
  assert(!handle->req_pending);

  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,
                             (handle->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)) {
    /* Make this req pending reporting an error. */
    SET_REQ_ERROR(&handle->req, GetLastError());
    uv_insert_pending_req(loop, (uv_req_t*)&handle->req);
  }

  handle->req_pending = 1;
}
Example #2
0
unsigned int CFileWatcher::Worker(void* arg)
{
	CFileWatcher* watcher = (CFileWatcher*)arg;
	DWORD error;
	DWORD bytes;
	ULONG_PTR key;
	LPOVERLAPPED overlapped;

	while (TRUE)
	{
		error = S_OK;
		if (!GetQueuedCompletionStatus(
			watcher->completionPort, &bytes, &key, &overlapped, watcher->uncFileSharePollingInterval))
		{
			error = GetLastError();
		}

		if (-1L == key) // terminate thread
		{
			break;
		}
		else if (S_OK == error) // a change in registered directory detected (local file system)
		{
			WatchedDirectory* directory = (WatchedDirectory*)key;
			
			watcher->ScanDirectory(directory, FALSE);

			RtlZeroMemory(&directory->overlapped, sizeof directory->overlapped);
			ReadDirectoryChangesW(
				directory->watchHandle,
				&directory->info,
				sizeof directory->info,
				FALSE,
				FILE_NOTIFY_CHANGE_LAST_WRITE,
				NULL,
				&directory->overlapped,
				NULL);
		}
		else // timeout - scan all registered UNC files for changes
		{
			WatchedDirectory* current = watcher->directories;
			while (current)
			{
				watcher->ScanDirectory(current, TRUE);
				current = current->next;
			}
		}		
	}

	return 0;
}
bool CCheckDirChange::WatchDirChanges(PReadDirChangeInfo pChangeInfo)
{
	pChangeInfo->pFileNotification = (FILE_NOTIFY_INFORMATION*)(new char[pChangeInfo->dwMaxBufferLen]);
	memset(pChangeInfo->pFileNotification,0,pChangeInfo->dwMaxBufferLen);

	return ReadDirectoryChangesW(m_hDirectory,
		pChangeInfo->pFileNotification,
		pChangeInfo->dwMaxBufferLen,
		TRUE,
		FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_LAST_WRITE|FILE_NOTIFY_CHANGE_DIR_NAME, 
		&pChangeInfo->dwReturnLen,
		&pChangeInfo->overlapped,
		NULL)?true:false;
}
Example #4
0
static GObject *
g_win32_directory_monitor_constructor (GType                  type,
				       guint                  n_construct_properties,
				       GObjectConstructParam *construct_properties) {
  GObject *obj;
  GWin32DirectoryMonitorClass *klass;
  GObjectClass *parent_class;
  GWin32DirectoryMonitor *self;
  wchar_t *wdirname;
  gboolean result;

  klass = G_WIN32_DIRECTORY_MONITOR_CLASS (g_type_class_peek (G_TYPE_WIN32_DIRECTORY_MONITOR));
  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
  obj = parent_class->constructor (type, n_construct_properties, construct_properties);
  self = G_WIN32_DIRECTORY_MONITOR (obj);
  wdirname = g_utf8_to_utf16 (G_LOCAL_DIRECTORY_MONITOR (obj)->dirname, -1, NULL, NULL, NULL);

  self->priv->hDirectory = CreateFileW (wdirname,
					FILE_LIST_DIRECTORY,
					FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 

					NULL,
					OPEN_EXISTING,
					FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
					NULL); 
  g_free (wdirname);
  if (self->priv->hDirectory == INVALID_HANDLE_VALUE)
    {
      /* Ignore errors */
      return obj;
    }

  result = ReadDirectoryChangesW (self->priv->hDirectory,
				  (gpointer)self->priv->file_notify_buffer,
				  self->priv->buffer_allocated_bytes,
				  FALSE, 
				  FILE_NOTIFY_CHANGE_FILE_NAME |
				  FILE_NOTIFY_CHANGE_DIR_NAME |
				  FILE_NOTIFY_CHANGE_ATTRIBUTES |
				  FILE_NOTIFY_CHANGE_SIZE,
				  &self->priv->buffer_filled_bytes,
				  &self->priv->overlapped,
				  g_win32_directory_monitor_callback);
  /* Ignore errors */

  return obj;
}
void file_system_watcher::path_watcher::read_changes()
{
	DWORD bytes_returned;
	if (!ReadDirectoryChangesW(
		directory_or_file_handle,
		buffer,
		buffer_size,
		TRUE,
		win_filters,
		&bytes_returned,
		&over,
		&completion))
		throw std::system_error(
			GetLastError(), 
			std::system_category(), 
			error_message{last});
}
Example #6
0
bool watchDirectory(const char* path, const std::function<void (const char* name)>& callback)
{
	HANDLE h = CreateFileW(fromUtf8(path).c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

	if (h == INVALID_HANDLE_VALUE)
		return false;

	char buf[65536];
	DWORD bufsize = 0;

	unsigned int filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION;

	while (ReadDirectoryChangesW(h, buf, sizeof(buf), true, filter, &bufsize, NULL, NULL))
	{
		if (bufsize == 0)
			continue;

		size_t offset = 0;

		for (;;)
		{
			assert(offset < bufsize);

			FILE_NOTIFY_INFORMATION* file = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(buf + offset);

			if (file->Action == FILE_ACTION_ADDED || file->Action == FILE_ACTION_MODIFIED || file->Action == FILE_ACTION_REMOVED || file->Action == FILE_ACTION_RENAMED_NEW_NAME)
			{
				std::string fp = toUtf8(file->FileName, file->FileNameLength / sizeof(WCHAR));

				std::replace(fp.begin(), fp.end(), '\\', '/');

				callback(fp.c_str());
			}

			if (!file->NextEntryOffset)
				break;

			offset += file->NextEntryOffset;
		}
	}

	CloseHandle(h);

	return true;
}
bool CCheckDirChange::WatchDirChanges(PReadDirChangeInfo pChangeInfos[] ,int length)
{
	for (int i=0;i<length;++i)
	{
		pChangeInfos[i]->pFileNotification = (FILE_NOTIFY_INFORMATION*)(new char[pChangeInfos[i]->dwMaxBufferLen]);
		memset(pChangeInfos[i]->pFileNotification,0,pChangeInfos[i]->dwMaxBufferLen);
		bool ret = ReadDirectoryChangesW(m_hDirectory,
			pChangeInfos[i]->pFileNotification,
			pChangeInfos[i]->dwMaxBufferLen,
			TRUE,
			FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_LAST_WRITE|FILE_NOTIFY_CHANGE_DIR_NAME, 
			&pChangeInfos[i]->dwReturnLen,
			&pChangeInfos[i]->overlapped,
			NULL)?true:false;
		if(ret == false)
			return false;
	}
	return true;
}
	bool directory_monitor_read_changes(DirectoryMonitorRecord* record)
	{
		DWORD notify_filter = FILE_NOTIFY_CHANGE_FILE_NAME | \
			FILE_NOTIFY_CHANGE_SIZE | \
			FILE_NOTIFY_CHANGE_CREATION;
		BOOL result = ReadDirectoryChangesW(record->handle,
			record->buffer + (DIRECTORY_MONITOR_BUFFER_SIZE * record->buffer_index),
			DIRECTORY_MONITOR_BUFFER_SIZE,
			FALSE,
			notify_filter,
			NULL, /* only needed for synchronous call */
			&record->async_data,
			directory_monitor_completion_routine);
		if (!result)
		{
			LOGV("ReadyDirectoryChangesW failed with error = %i\n", GetLastError());
		}

		return (result > 0);
	} // directory_monitor_read_changes
Example #9
0
/* Worker routine for the watch thread.  */
static DWORD WINAPI
watch_worker (LPVOID arg)
{
  struct notification *dirwatch = (struct notification *)arg;
  BOOL bErr;
  DWORD _bytes = 0;
  DWORD status;

  if (dirwatch->dir)
    {
      bErr = ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf,
				    DIRWATCH_BUFFER_SIZE, dirwatch->subtree,
				    dirwatch->filter, &_bytes,
				    dirwatch->io_info, watch_completion);
      if (!bErr)
	{
	  DebPrint (("ReadDirectoryChangesW: %lu\n", GetLastError ()));
	  /* We cannot remove the dirwatch object from watch_list,
	     because we are in a separate thread.  For the same
	     reason, we also cannot free memory consumed by the
	     buffers allocated for the dirwatch object.  So we close
	     the directory handle, but do not free the object itself
	     or its buffers.  We also don't touch the signature.  This
	     way, remove_watch can still identify the object, remove
	     it, and free its memory.  */
	  CloseHandle (dirwatch->dir);
	  dirwatch->dir = NULL;
	  return 1;
	}
    }

  do {
    status = WaitForSingleObjectEx(dirwatch->terminate, INFINITE, TRUE);
  } while (status == WAIT_IO_COMPLETION);

  /* The thread is about to terminate, so we clean up the dir handle.  */
  CloseHandle (dirwatch->dir);
  dirwatch->dir = NULL;

  return 0;
}
Example #10
0
static void CALLBACK StartMonitoringDirForChangesAPC(ULONG_PTR arg) {
    WatchedDir* wd = (WatchedDir*)arg;
    ZeroMemory(&wd->overlapped, sizeof(wd->overlapped));

    OVERLAPPED* overlapped = (OVERLAPPED*)&(wd->overlapped);
    wd->overlapped.data = (HANDLE)wd;

    lf(L"StartMonitoringDirForChangesAPC() %s", wd->dirPath);

    CrashIf(g_threadId != GetCurrentThreadId());

    DWORD dwNotifyFilter = FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME;
    ReadDirectoryChangesW(wd->hDir,
                          wd->buf,                           // read results buffer
                          sizeof(wd->buf),                   // length of buffer
                          FALSE,                             // bWatchSubtree
                          dwNotifyFilter,                    // filter conditions
                          nullptr,                           // bytes returned
                          overlapped,                        // overlapped buffer
                          ReadDirectoryChangesNotification); // completion routine
}
Example #11
0
bool wxFSWatcherImplMSW::DoSetUpWatch(wxFSWatchEntryMSW& watch)
{
    BOOL bWatchSubtree = FALSE;

    switch ( watch.GetType() )
    {
        case wxFSWPath_File:
            wxLogError(_("Monitoring individual files for changes is not "
                         "supported currently."));
            return false;

        case wxFSWPath_Dir:
            bWatchSubtree = FALSE;
            break;

        case wxFSWPath_Tree:
            bWatchSubtree = TRUE;
            break;

        case wxFSWPath_None:
            wxFAIL_MSG( "Invalid watch type." );
            return false;
    }

    int flags = Watcher2NativeFlags(watch.GetFlags());
    int ret = ReadDirectoryChangesW(watch.GetHandle(), watch.GetBuffer(),
                                    wxFSWatchEntryMSW::BUFFER_SIZE,
                                    bWatchSubtree,
                                    flags, NULL,
                                    watch.GetOverlapped(), NULL);
    if (!ret)
    {
        wxLogSysError(_("Unable to set up watch for '%s'"),
                        watch.GetPath());
    }

    return ret != 0;
}
Example #12
0
void CDirectoryWatcher::WorkerThread()
{
    DWORD numBytes;
    CDirWatchInfo * pdi = NULL;
    LPOVERLAPPED lpOverlapped;
    WCHAR buf[READ_DIR_CHANGE_BUFFER_SIZE] = {0};
    WCHAR * pFound = NULL;
    while (m_bRunning)
    {
        CleanupWatchInfo();
        if (watchedPaths.GetCount())
        {
            // Any incoming notifications?

            pdi = NULL;
            numBytes = 0;
            InterlockedExchange(&m_bCleaned, FALSE);
            if (   (!m_hCompPort)
                || !GetQueuedCompletionStatus(m_hCompPort,
                                              &numBytes,
                                              (PULONG_PTR) &pdi,
                                              &lpOverlapped,
                                              600000 /*10 minutes*/))
            {
                // No. Still trying?

                if (!m_bRunning)
                    return;

                CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": restarting watcher\n");
                m_hCompPort.CloseHandle();

                // We must sync the whole section because other threads may
                // receive "AddPath" calls that will delete the completion
                // port *while* we are adding references to it .

                AutoLocker lock(m_critSec);

                // Clear the list of watched objects and recreate that list.
                // This will also delete the old completion port

                ClearInfoMap();
                CleanupWatchInfo();

                for (int i=0; i<watchedPaths.GetCount(); ++i)
                {
                    CTSVNPath watchedPath = watchedPaths[i];

                    CAutoFile hDir = CreateFile(watchedPath.GetWinPath(),
                                            FILE_LIST_DIRECTORY,
                                            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                                            NULL, //security attributes
                                            OPEN_EXISTING,
                                            FILE_FLAG_BACKUP_SEMANTICS | //required privileges: SE_BACKUP_NAME and SE_RESTORE_NAME.
                                            FILE_FLAG_OVERLAPPED,
                                            NULL);
                    if (!hDir)
                    {
                        // this could happen if a watched folder has been removed/renamed
                        CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": CreateFile failed. Can't watch directory %s\n", watchedPaths[i].GetWinPath());
                        watchedPaths.RemovePath(watchedPath);
                        break;
                    }

                    DEV_BROADCAST_HANDLE NotificationFilter;
                    SecureZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
                    NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
                    NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
                    NotificationFilter.dbch_handle = hDir;
                    // RegisterDeviceNotification sends a message to the UI thread:
                    // make sure we *can* send it and that the UI thread isn't waiting on a lock
                    int numPaths = watchedPaths.GetCount();
                    size_t numWatch = watchInfoMap.size();
                    lock.Unlock();
                    NotificationFilter.dbch_hdevnotify = RegisterDeviceNotification(hWnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
                    lock.Lock();
                    // since we released the lock to prevent a deadlock with the UI thread,
                    // it could happen that new paths were added to watch, or another thread
                    // could have cleared our info map.
                    // if that happened, we have to restart watching all paths again.
                    if ((numPaths != watchedPaths.GetCount()) || (numWatch != watchInfoMap.size()))
                    {
                        ClearInfoMap();
                        CleanupWatchInfo();
                        Sleep(200);
                        break;
                    }

                    CDirWatchInfo * pDirInfo = new CDirWatchInfo(hDir, watchedPath);
                    hDir.Detach();  // the new CDirWatchInfo object owns the handle now
                    pDirInfo->m_hDevNotify = NotificationFilter.dbch_hdevnotify;


                    HANDLE port = CreateIoCompletionPort(pDirInfo->m_hDir, m_hCompPort, (ULONG_PTR)pDirInfo, 0);
                    if (port == NULL)
                    {
                        CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": CreateIoCompletionPort failed. Can't watch directory %s\n", watchedPath.GetWinPath());

                        // we must close the directory handle to allow ClearInfoMap()
                        // to close the completion port properly
                        pDirInfo->CloseDirectoryHandle();

                        ClearInfoMap();
                        CleanupWatchInfo();
                        delete pDirInfo;
                        pDirInfo = NULL;

                        watchedPaths.RemovePath(watchedPath);
                        break;
                    }
                    m_hCompPort = port;

                    if (!ReadDirectoryChangesW(pDirInfo->m_hDir,
                                                pDirInfo->m_Buffer,
                                                READ_DIR_CHANGE_BUFFER_SIZE,
                                                TRUE,
                                                FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
                                                &numBytes,// not used
                                                &pDirInfo->m_Overlapped,
                                                NULL))  //no completion routine!
                    {
                        CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": ReadDirectoryChangesW failed. Can't watch directory %s\n", watchedPath.GetWinPath());

                        // we must close the directory handle to allow ClearInfoMap()
                        // to close the completion port properly
                        pDirInfo->CloseDirectoryHandle();

                        ClearInfoMap();
                        CleanupWatchInfo();
                        delete pDirInfo;
                        pDirInfo = NULL;
                        watchedPaths.RemovePath(watchedPath);
                        break;
                    }

                    CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": watching path %s\n", pDirInfo->m_DirName.GetWinPath());
                    watchInfoMap[pDirInfo->m_hDir] = pDirInfo;
                }
            }
            else
            {
                if (!m_bRunning)
                    return;
                if (watchInfoMap.empty())
                    continue;

                // NOTE: the longer this code takes to execute until ReadDirectoryChangesW
                // is called again, the higher the chance that we miss some
                // changes in the file system!
                if (pdi)
                {
                    BOOL bRet = false;
                    std::list<CTSVNPath> notifyPaths;
                    {
                        AutoLocker lock(m_critSec);
                        // in case the CDirectoryWatcher objects have been cleaned,
                        // the m_bCleaned variable will be set to true here. If the
                        // objects haven't been cleared, we can access them here.
                        if (InterlockedExchange(&m_bCleaned, FALSE))
                            continue;
                        if (   (!pdi->m_hDir) || watchInfoMap.empty()
                            || (watchInfoMap.find(pdi->m_hDir) == watchInfoMap.end()))
                        {
                            continue;
                        }
                        PFILE_NOTIFY_INFORMATION pnotify = (PFILE_NOTIFY_INFORMATION)pdi->m_Buffer;
                        DWORD nOffset = 0;

                        do
                        {
                            pnotify = (PFILE_NOTIFY_INFORMATION)((LPBYTE)pnotify + nOffset);

                            if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE)
                                break;

                            nOffset = pnotify->NextEntryOffset;

                            if (pnotify->FileNameLength >= (READ_DIR_CHANGE_BUFFER_SIZE*sizeof(TCHAR)))
                                continue;

                            SecureZeroMemory(buf, READ_DIR_CHANGE_BUFFER_SIZE*sizeof(TCHAR));
                            wcsncpy_s(buf, pdi->m_DirPath, _countof(buf)-1);
                            errno_t err = wcsncat_s(buf+pdi->m_DirPath.GetLength(), READ_DIR_CHANGE_BUFFER_SIZE-pdi->m_DirPath.GetLength(), pnotify->FileName, min(READ_DIR_CHANGE_BUFFER_SIZE-pdi->m_DirPath.GetLength(), int(pnotify->FileNameLength/sizeof(TCHAR))));
                            if (err == STRUNCATE)
                            {
                                continue;
                            }
                            buf[(pnotify->FileNameLength/sizeof(TCHAR))+pdi->m_DirPath.GetLength()] = 0;

                            if (m_FolderCrawler)
                            {
                                if ((pFound = StrStrI(buf, L"\\tmp"))!=NULL)
                                {
                                    pFound += 4;
                                    if (((*pFound)=='\\')||((*pFound)=='\0'))
                                    {
                                        continue;
                                    }
                                }
                                if ((pFound = StrStrI(buf, L":\\RECYCLER"))!=NULL)
                                {
                                    if ((*(pFound + 10) == '\0') || (*(pFound + 10) == '\\'))
                                        continue;
                                }
                                if ((pFound = StrStrI(buf, L":\\$Recycle.Bin"))!=NULL)
                                {
                                    if ((*(pFound + 14) == '\0') || (*(pFound + 14) == '\\'))
                                        continue;
                                }

                                if (StrStrI(buf, L".tmp")!=NULL)
                                {
                                    // assume files with a .tmp extension are not versioned and interesting,
                                    // so ignore them.
                                    continue;
                                }
                                CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": change notification for %s\n", buf);
                                notifyPaths.push_back(CTSVNPath(buf));
                            }
                        } while ((nOffset > 0)&&(nOffset < READ_DIR_CHANGE_BUFFER_SIZE));

                        // setup next notification cycle

                        SecureZeroMemory (pdi->m_Buffer, sizeof(pdi->m_Buffer));
                        SecureZeroMemory (&pdi->m_Overlapped, sizeof(OVERLAPPED));
                        bRet = ReadDirectoryChangesW (pdi->m_hDir,
                            pdi->m_Buffer,
                            READ_DIR_CHANGE_BUFFER_SIZE,
                            TRUE,
                            FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
                            &numBytes,// not used
                            &pdi->m_Overlapped,
                            NULL);  //no completion routine!
                    }
                    if (!notifyPaths.empty())
                    {
                        for (auto nit = notifyPaths.cbegin(); nit != notifyPaths.cend(); ++nit)
                        {
                            m_FolderCrawler->AddPathForUpdate(*nit);
                        }
                    }

                    // any clean-up to do?

                    CleanupWatchInfo();

                    if (!bRet)
                    {
                        // Since the call to ReadDirectoryChangesW failed, just
                        // wait a while. We don't want to have this thread
                        // running using 100% CPU if something goes completely
                        // wrong.
                        Sleep(200);
                    }
                }
            }
        }// if (watchedPaths.GetCount())
        else
            Sleep(200);
    }// while (m_bRunning)
}
Example #13
0
static void CALLBACK 
g_win32_directory_monitor_callback (DWORD        error,
				    DWORD        nBytes,
				    LPOVERLAPPED lpOverlapped)
{
  gulong offset;
  PFILE_NOTIFY_INFORMATION pfile_notify_walker;
  glong file_name_len;
  gchar *file_name;
  gchar *path;
  GFile *file;
  GWin32DirectoryMonitorPrivate *priv = (GWin32DirectoryMonitorPrivate *) lpOverlapped;

  static GFileMonitorEvent events[] =
    {
      0, 
      G_FILE_MONITOR_EVENT_CREATED, /* FILE_ACTION_ADDED            */
      G_FILE_MONITOR_EVENT_DELETED, /* FILE_ACTION_REMOVED          */
      G_FILE_MONITOR_EVENT_CHANGED, /* FILE_ACTION_MODIFIED         */
      G_FILE_MONITOR_EVENT_DELETED, /* FILE_ACTION_RENAMED_OLD_NAME */
      G_FILE_MONITOR_EVENT_CREATED, /* FILE_ACTION_RENAMED_NEW_NAME */
    };

  /* If priv->self is NULL the GWin32DirectoryMonitor object has been destroyed. */
  if (priv->self == NULL ||
      g_file_monitor_is_cancelled (priv->self) ||
      priv->file_notify_buffer == NULL)
    {
      g_free (priv->file_notify_buffer);
      g_free (priv);
      return;
    }

  offset = 0;
  do {
    pfile_notify_walker = (PFILE_NOTIFY_INFORMATION)(priv->file_notify_buffer + offset);
    if (pfile_notify_walker->Action > 0)
      {
	file_name = g_utf16_to_utf8 (pfile_notify_walker->FileName, pfile_notify_walker->FileNameLength / sizeof(WCHAR), NULL, &file_name_len, NULL);
	path = g_build_filename(G_LOCAL_DIRECTORY_MONITOR (priv->self)->dirname, file_name, NULL);
	file = g_file_new_for_path (path);
	g_file_monitor_emit_event (priv->self, file, NULL, events [pfile_notify_walker->Action]);
	g_object_unref (file);
	g_free (path);
	g_free (file_name);
      }
    offset += pfile_notify_walker->NextEntryOffset;
  } while (pfile_notify_walker->NextEntryOffset);

  ReadDirectoryChangesW (priv->hDirectory,
			 (gpointer)priv->file_notify_buffer,
			 priv->buffer_allocated_bytes,
			 FALSE, 
			 FILE_NOTIFY_CHANGE_FILE_NAME |
			 FILE_NOTIFY_CHANGE_DIR_NAME |
			 FILE_NOTIFY_CHANGE_ATTRIBUTES |
			 FILE_NOTIFY_CHANGE_SIZE,
			 &priv->buffer_filled_bytes,
			 &priv->overlapped,
			 g_win32_directory_monitor_callback);
}
Example #14
0
 /// Refreshes the directory monitoring.
 bool RefreshWatch(WatchStruct* pWatch, bool _clear)
 {
     return ReadDirectoryChangesW(
         pWatch->mDirHandle, pWatch->mBuffer, sizeof(pWatch->mBuffer), pWatch->mIsRecursive,
         pWatch->mNotifyFilter, NULL, &pWatch->mOverlapped, _clear ? 0 : WatchCallback) != 0;
 }
void WatchDirectory(void *thread_data) {
  // Copy thread inputs to local variables
  MediaScan *s = ((thread_data_type *)thread_data)->s;
  LPTSTR lpDir = ((thread_data_type *)thread_data)->lpDir;

  // Data variables for the windows directory change notification
  OVERLAPPED oOverlap;
  FILE_NOTIFY_INFORMATION Buffer[FILE_BUFFER_SZ];
  char buf[256];
  char full_path[MAX_PATH_STR_LEN];
  DWORD BytesReturned;

  // Overlapped I/O variables
  WSAOVERLAPPED RecvOverlapped;
  HANDLE hDir;
  DWORD dwWaitStatus;
  DWORD dwBytesRead;
  BOOL bResult;
  WSABUF DataBuf;
  DWORD RecvBytes, Flags;
  char buffer[DATA_BUFSIZE];
  int rc = 0;
  int err = 0;
  char *pBase = 0;

  // Thread state variables
  int ThreadRunning = TRUE;

  // Initialize the cache database
  if (!init_bdb(s)) {
    MediaScanError *e = error_create("", MS_ERROR_CACHE, "Unable to initialize libmediascan cache");
    send_error(s, e);
  }

  SecureZeroMemory(buffer, DATA_BUFSIZE);

  // Make sure the RecvOverlapped struct is zeroed out
  SecureZeroMemory((PVOID) & oOverlap, sizeof(OVERLAPPED));

  // Create the event that will get fired when a directory changes
  oOverlap.hEvent = CreateEvent(NULL, // default security attributes
                                TRUE, // manual-reset event
                                FALSE,  // initial state is nonsignaled
                                TEXT("MyFileChangeEvent")); // "FileChangeEvent" name

  // Make sure the RecvOverlapped struct is zeroed out
  SecureZeroMemory((PVOID) & RecvOverlapped, sizeof(WSAOVERLAPPED));

  // Create an event handle and setup an overlapped structure.
  RecvOverlapped.hEvent = WSACreateEvent();
  if (RecvOverlapped.hEvent == NULL) {
    LOG_ERROR("WSACreateEvent failed: %d\n", WSAGetLastError());
    return;
  }

  DataBuf.len = DATA_BUFSIZE;
  DataBuf.buf = buffer;

  // Open the directory that we are going to have windows monitor, note the share modes
  hDir = CreateFile(lpDir,      // pointer to the file name
                    FILE_LIST_DIRECTORY,  // access (read/write) mode
                    FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, // share mode
                    NULL,       // security descriptor
                    OPEN_EXISTING,  // how to create
                    FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,  // file attributes
                    NULL);      // file with attributes to copy

  // Tell windows to monitor the folder asyncroniously using overlapped I/O
  ReadDirectoryChangesW(hDir,   // handle to directory
                        &Buffer,  // read results buffer
                        FILE_BUFFER_SZ * sizeof(FILE_NOTIFY_INFORMATION), // length of buffer
                        TRUE,   // Monitor sub-directories = TRUE
                        DETECTION_FILTER, // filter conditions
                        &BytesReturned, // bytes returned
                        &oOverlap,  // We are using overlapped I/O
                        NULL);  // Not using completion routine

  Flags = 0;
/* XXX FIXME, reqpipe is gone
  rc = WSARecv(s->thread->reqpipe[0], &DataBuf, 1, &RecvBytes, &Flags, &RecvOverlapped, NULL);
  if ((rc == SOCKET_ERROR) && (WSA_IO_PENDING != (err = WSAGetLastError()))) {
    LOG_ERROR("WSARecv failed with error: %d\n", err);
    return;
  }
*/

  // Run until we are told to stop. It is important to let the thread clean up after itself so
  // there is shutdown code at the bottom of this function.
  while (ThreadRunning) {

    // Set up the events are going to wait for
    HANDLE event_list[2] = { oOverlap.hEvent, RecvOverlapped.hEvent };
    FILE_NOTIFY_INFORMATION *fni;
    LOG_LEVEL(1, "\nWaiting for notification...\n");

    // Wait forever for notification.
    dwWaitStatus = WaitForMultipleObjects(2,  // number of objects in array
                                          event_list, // array of objects
                                          FALSE,  // wait for any object
                                          INFINITE);  // infinite wait

    switch (dwWaitStatus) {
        // This is for the event oOverlap.hEvent
      case WAIT_OBJECT_0:
        bResult = GetOverlappedResult(hDir, &oOverlap, &dwBytesRead, TRUE);

        pBase = (char *)Buffer;
        do {

          fni = (FILE_NOTIFY_INFORMATION *) pBase;

          // Note pRecord->FileName is in UTF-16, have to convert it to 8 bits
          WideCharToMultiByte(CP_UTF8, 0, fni->FileName,  // the string you have
                              fni->FileNameLength / 2,  // length of the string
                              buf,  // output
                              _countof(buf),  // size of the buffer in bytes - if you leave it zero the return value is the length required for the output buffer
                              NULL, NULL);
          buf[fni->FileNameLength / 2] = 0;


          // Set up a full path to the file to be scanned and call ms_scan_fileCall scan here with the file changed
          strcpy(full_path, lpDir);
          strcat(full_path, "\\");
          strcat(full_path, buf);
          LOG_INFO("Found File Changed: %s", full_path);

          switch (fni->Action) {

            case FILE_ACTION_ADDED:
              LOG_INFO("  file was added\n");

              // We get notifications even while a file is being copied. This will wait 60 seconds for the file to finish.
              if (WaitForFile(full_path, 10)) {
                ms_scan_file(s, full_path, TYPE_UNKNOWN);
              }
              else {
                LOG_ERROR("A file found by the background scanner never finished copying");
              }

              break;
            case FILE_ACTION_REMOVED:
              LOG_INFO("  file was removed\n");
              HandleRemovedFile(s, full_path);
              break;

            case FILE_ACTION_MODIFIED:
              LOG_INFO("	file was modified\n");  // This can be a change in the time stamp or attributes.";
              // We get notifications even while a file is being copied. This will wait 60 seconds for the file to finish.
              if (WaitForFile(full_path, 10)) {
                ms_scan_file(s, full_path, TYPE_UNKNOWN);
              }
              else {
                LOG_ERROR("A file found by the background scanner never finished copying");
              }

              break;
/*            case FILE_ACTION_RENAMED_OLD_NAME:
								LOG_INFO("	file was renamed and this is the old name\n");
              break;
            case FILE_ACTION_RENAMED_NEW_NAME:
								LOG_INFO("	file was renamed and this is the new name\n");
              break;
*/ }

          if (!fni->NextEntryOffset)
            break;
          pBase += fni->NextEntryOffset;
        } while (TRUE);

        SecureZeroMemory((PVOID) Buffer, sizeof(Buffer));
        ReadDirectoryChangesW(hDir, // handle to directory
                              &Buffer,  // read results buffer
                              sizeof(Buffer), // length of buffer
                              TRUE, // Monitor sub-directories = TRUE
                              DETECTION_FILTER, // filter conditions
                              &BytesReturned, // bytes returned
                              &oOverlap,  // We are using overlapped I/O
                              NULL);  // Not using completion routine

        break;                  /* WAIT_OBJECT_0: */

        // This is for the event s->ghSignalEvent
      case WAIT_OBJECT_0 + 1:

        ThreadRunning = FALSE;

        break;                  /* WAIT_OBJECT_0 + 1: */

      case WAIT_TIMEOUT:

        // A timeout occurred, this would happen if some value other
        // than INFINITE is used in the Wait call and no changes occur.
        // In a single-threaded environment you might not want an
        // INFINITE wait.

        LOG_INFO("\nNo changes in the timeout period.\n");
        break;

      default:
        LOG_ERROR("\n ERROR: Unhandled dwWaitStatus.\n");
        ExitThread(GetLastError());
        break;
    }
  }

  // Free the data that was passed to this thread on the heap
  if (thread_data != NULL) {
    free(thread_data);
    thread_data = NULL;         // Ensure address is not reused.
  }

  CancelIo(hDir);

  if (!HasOverlappedIoCompleted(&oOverlap)) {
    SleepEx(5, TRUE);
  }

  WSACloseEvent(RecvOverlapped.hEvent);
  CloseHandle(oOverlap.hEvent);


  ExitThread(GetLastError());

}                               /* WatchDirectory() */
Example #16
0
VOID CALLBACK
watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
{
  struct notification *dirwatch;
  DWORD _bytes;
  struct notifications_set *ns = NULL;
  BOOL terminate = FALSE;

  /* Who knows what happened?  Perhaps the OVERLAPPED structure was
     freed by someone already?  In any case, we cannot do anything
     with this request, so just punt and skip it.  FIXME: should we
     raise the 'terminate' flag in this case?  */
  if (!io_info)
    {
      DebPrint(("watch_completion: io_info is null.\n"));
      return;
    }

  /* We have a pointer to our dirwatch structure conveniently stashed
     away in the hEvent member of the OVERLAPPED struct.  According to
     MSDN documentation of ReadDirectoryChangesW: "The hEvent member
     of the OVERLAPPED structure is not used by the system, so you can
     use it yourself."  */
  dirwatch = (struct notification *)io_info->hEvent;

  if (status == ERROR_OPERATION_ABORTED)
    {
      /* We've been called because the main thread told us to issue
	 CancelIo on the directory we watch, and watch_end did so.
         We must exit, without issuing another call to
         ReadDirectoryChangesW. */
      return;
    }

  /* We allocate a new set of notifications to be linked to the linked
     list of notifications set.  This will be processed by Emacs event
     loop in the main thread.  We need to duplicate the notifications
     buffer, but not the dirwatch structure.  */

  /* Implementation note: In general, allocating memory in non-main
     threads is a no-no in Emacs.  We certainly cannot call xmalloc
     and friends, because it can longjmp when allocation fails, which
     will crash Emacs because the jmp_buf is set up to a location on
     the main thread's stack.  However, we can call 'malloc' directly,
     since that is redirected to HeapAlloc that uses our private heap,
     see w32heap.c, and that is thread-safe.  */
  ns = malloc (sizeof(struct notifications_set));
  if (ns)
    {
      memset (ns, 0, sizeof(struct notifications_set));
      ns->notifications = malloc (bytes_ret);
      if (ns->notifications)
	{
	  memcpy (ns->notifications, dirwatch->buf, bytes_ret);
	  ns->size = bytes_ret;
	  ns->desc = dirwatch;
	}
      else
	{
	  free (ns);
	  ns = NULL;
	}
    }
  if (ns == NULL)
    DebPrint(("Out of memory.  Notifications lost."));

  /* Calling ReadDirectoryChangesW quickly to watch again for new
     notifications.  */
  if (!ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf,
			      DIRWATCH_BUFFER_SIZE, dirwatch->subtree,
			      dirwatch->filter, &_bytes, dirwatch->io_info,
			      watch_completion))
    {
      DebPrint (("ReadDirectoryChangesW error: %lu\n", GetLastError ()));
      /* If this call fails, it means that the directory is not
         watchable any more.  We need to terminate the worker thread.
         Still, we will wait until the current notifications have been
         sent to the main thread.  */
      terminate = TRUE;
    }

  if (ns)
    send_notifications(ns);

  /* If we were asked to terminate the thread, then fire the event. */
  if (terminate)
    SetEvent(dirwatch->terminate);
}
Example #17
0
HRESULT CFileWatcher::WatchFile(PCWSTR fileName, FileModifiedCallback callback, void* data)
{
	HRESULT hr;
	WatchedFile* file;
	WatchedDirectory* directory;
	WatchedDirectory* newDirectory;
	WCHAR fileOnly[_MAX_FNAME];
	WCHAR ext[_MAX_EXT];
	WCHAR* directoryName = NULL;
	DWORD fileNameLength;
	DWORD fileNameOnlyLength;
	DWORD directoryLength;	
	WIN32_FILE_ATTRIBUTE_DATA attributes;

	CheckNull(callback);
	CheckNull(fileName);
	fileNameLength = wcslen(fileName);

	// allocate new WatchedFile, get snapshot of the last write time

	ErrorIf(NULL == (file = new WatchedFile), ERROR_NOT_ENOUGH_MEMORY);
	RtlZeroMemory(file, sizeof WatchedFile);
	file->callback = callback;
	file->data = data;
	ErrorIf(!GetFileAttributesExW(fileName, GetFileExInfoStandard, &attributes), GetLastError());
	memcpy(&file->lastWrite, &attributes.ftLastWriteTime, sizeof attributes.ftLastWriteTime);

	// create and normalize a copy of directory name and file name

	ErrorIf(0 != _wsplitpath_s(fileName, NULL, 0, NULL, 0, fileOnly, _MAX_FNAME, ext, _MAX_EXT), ERROR_INVALID_PARAMETER);	
	fileNameOnlyLength = wcslen(fileOnly) + wcslen(ext);	
	directoryLength = fileNameLength - fileNameOnlyLength; 
	ErrorIf(NULL == (directoryName = new WCHAR[directoryLength + 8]), ERROR_NOT_ENOUGH_MEMORY); // pessimistic length after normalization with prefix \\?\UNC\ 
	ErrorIf(NULL == (file->fileName = new WCHAR[fileNameLength + 1]), ERROR_NOT_ENOUGH_MEMORY);
	wcscpy(file->fileName, fileName);

	if (fileNameLength > 8 && 0 == memcmp(fileName, L"\\\\?\\UNC\\", 8 * sizeof WCHAR))
	{
		// normalized UNC path
		file->unc = TRUE;
		memcpy(directoryName, fileName, directoryLength * sizeof WCHAR);
		directoryName[directoryLength] = L'\0';
	}
	else if (fileNameLength > 4 && 0 == memcmp(fileName, L"\\\\?\\", 4 * sizeof WCHAR))
	{
		// normalized local file
		file->unc = FALSE;
		memcpy(directoryName, fileName, directoryLength * sizeof WCHAR);
		directoryName[directoryLength] = L'\0';
	}
	else if (fileNameLength > 2 && 0 == memcmp(fileName, L"\\\\", 2 * sizeof(WCHAR)))
	{
		// not normalized UNC path
		file->unc = TRUE;
		wcscpy(directoryName, L"\\\\?\\UNC\\");
		memcpy(directoryName + 8, fileName + 2, (directoryLength - 2) * sizeof WCHAR);
		directoryName[8 + directoryLength - 2] = L'\0';
	}
	else
	{
		// not normalized local file
		file->unc = FALSE;
		wcscpy(directoryName, L"\\\\?\\");
		memcpy(directoryName + 4, fileName, directoryLength * sizeof WCHAR);
		directoryName[4 + directoryLength] = L'\0';	
	}

	// find matching directory watcher entry

	directory = this->directories;
	while (NULL != directory)
	{
		if (0 == wcscmp(directory->directoryName, directoryName))
		{
			delete [] directoryName;
			directoryName = NULL;
			break;
		}

		directory = directory->next;
	}

	// if directory watcher not found, create one

	if (NULL == directory)
	{
		ErrorIf(NULL == (newDirectory = new WatchedDirectory), ERROR_NOT_ENOUGH_MEMORY);	
		RtlZeroMemory(newDirectory, sizeof WatchedDirectory);
		newDirectory->directoryName = directoryName;
		directoryName = NULL;
		newDirectory->files = file;

		ErrorIf(INVALID_HANDLE_VALUE == (newDirectory->watchHandle = CreateFileW(
			newDirectory->directoryName,           
			FILE_LIST_DIRECTORY,    
			FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
			NULL, 
			OPEN_EXISTING,         
			FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
			NULL)),
			GetLastError());

		ErrorIf(NULL == CreateIoCompletionPort(newDirectory->watchHandle, this->completionPort, (ULONG_PTR)newDirectory, 0), 
			GetLastError());

		ErrorIf(0 == ReadDirectoryChangesW(
			newDirectory->watchHandle,
			&newDirectory->info,
			sizeof newDirectory->info,
			FALSE,
			FILE_NOTIFY_CHANGE_LAST_WRITE,
			NULL,
			&newDirectory->overlapped,
			NULL),
			GetLastError());

		if (NULL == this->directories)
		{
			// no watchers exist yet, start the watcher thread

			ErrorIf(NULL == (this->worker = (HANDLE)_beginthreadex(
				NULL, 
				4096, 
				CFileWatcher::Worker, 
				this, 
				0, 
				NULL)), 
				ERROR_NOT_ENOUGH_MEMORY);
		}

		newDirectory->next = this->directories;
		this->directories = newDirectory;
		newDirectory = NULL;
	}
	else
	{
		file->next = directory->files;
		directory->files = file;
		file = NULL;
	}

	return S_OK;
Error:

	if (NULL != newDirectory)
	{
		if (NULL != newDirectory->directoryName)
		{
			delete [] newDirectory->directoryName;
		}

		if (NULL != newDirectory->watchHandle)
		{
			CloseHandle(newDirectory->watchHandle);
		}

		delete newDirectory;
	}

	if (NULL != file)
	{
		delete [] file->fileName;
		delete file;
	}

	if (NULL != directoryName)
	{
		delete [] directoryName;
	}

	return hr;
}
Example #18
0
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);
}