// process events (list of FILE_NOTIFY_INFORMATION structures) private void processEvents(WindowsWatchKey key, int size) { long address = key.buffer().address(); int nextOffset; do { int action = UNSAFE.getInt(address + OFFSETOF_ACTION); // map action to event WatchEvent.Kind<?> kind = translateActionToEvent(action); if (key.events().contains(kind)) { // copy the name int nameLengthInBytes = UNSAFE.getInt(address + OFFSETOF_FILENAMELENGTH); if ((nameLengthInBytes % 2) != 0) { throw new AssertionError("FileNameLength is not a multiple of 2"); } char[] nameAsArray = new char[nameLengthInBytes / 2]; UNSAFE.copyMemory( null, address + OFFSETOF_FILENAME, nameAsArray, Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes); // create FileName and queue event WindowsPath name = WindowsPath.createFromNormalizedPath(fs, new String(nameAsArray)); key.signalEvent(kind, name); } // next event nextOffset = UNSAFE.getInt(address + OFFSETOF_NEXTENTRYOFFSET); address += (long) nextOffset; } while (nextOffset != 0); }
/** * Register a directory for changes as follows: * * <p>1. Open directory 2. Read its attributes (and check it really is a directory) 3. Assign * completion key and associated handle with completion port 4. Call ReadDirectoryChangesW to * start (async) read of changes 5. Create or return existing key representing registration */ @Override Object implRegister( Path obj, Set<? extends WatchEvent.Kind<?>> events, WatchEvent.Modifier... modifiers) { WindowsPath dir = (WindowsPath) obj; boolean watchSubtree = false; // FILE_TREE modifier allowed for (WatchEvent.Modifier modifier : modifiers) { if (modifier == ExtendedWatchEventModifier.FILE_TREE) { watchSubtree = true; } else { if (modifier == null) return new NullPointerException(); if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier) continue; // ignore return new UnsupportedOperationException("Modifier not supported"); } } // open directory long handle; try { handle = CreateFile( dir.getPathForWin32Calls(), FILE_LIST_DIRECTORY, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED); } catch (WindowsException x) { return x.asIOException(dir); } boolean registered = false; try { // read attributes and check file is a directory WindowsFileAttributes attrs; try { attrs = WindowsFileAttributes.readAttributes(handle); } catch (WindowsException x) { return x.asIOException(dir); } if (!attrs.isDirectory()) { return new NotDirectoryException(dir.getPathForExceptionMessage()); } // check if this directory is already registered FileKey fk = new FileKey(attrs.volSerialNumber(), attrs.fileIndexHigh(), attrs.fileIndexLow()); WindowsWatchKey existing = fk2key.get(fk); // if already registered and we're not changing the subtree // modifier then simply update the event and return the key. if (existing != null && watchSubtree == existing.watchSubtree()) { existing.setEvents(events); return existing; } // Can overflow the int type capacity. // Skip WAKEUP_COMPLETION_KEY value. int completionKey = ++lastCompletionKey; if (completionKey == WAKEUP_COMPLETION_KEY) completionKey = ++lastCompletionKey; // associate handle with completion port try { CreateIoCompletionPort(handle, port, completionKey); } catch (WindowsException x) { return new IOException(x.getMessage()); } // allocate memory for events, including space for other structures // needed to do overlapped I/O int size = CHANGES_BUFFER_SIZE + SIZEOF_DWORD + SIZEOF_OVERLAPPED; NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); long bufferAddress = buffer.address(); long overlappedAddress = bufferAddress + size - SIZEOF_OVERLAPPED; long countAddress = overlappedAddress - SIZEOF_DWORD; // zero the overlapped structure UNSAFE.setMemory(overlappedAddress, SIZEOF_OVERLAPPED, (byte) 0); // start async read of changes to directory try { createAndAttachEvent(overlappedAddress); ReadDirectoryChangesW( handle, bufferAddress, CHANGES_BUFFER_SIZE, watchSubtree, ALL_FILE_NOTIFY_EVENTS, countAddress, overlappedAddress); } catch (WindowsException x) { closeAttachedEvent(overlappedAddress); buffer.release(); return new IOException(x.getMessage()); } WindowsWatchKey watchKey; if (existing == null) { // not registered so create new watch key watchKey = new WindowsWatchKey(dir, watcher, fk) .init( handle, events, watchSubtree, buffer, countAddress, overlappedAddress, completionKey); // map file key to watch key fk2key.put(fk, watchKey); } else { // directory already registered so need to: // 1. remove mapping from old completion key to existing watch key // 2. release existing key's resources (handle/buffer) // 3. re-initialize key with new handle/buffer ck2key.remove(existing.completionKey()); releaseResources(existing); watchKey = existing.init( handle, events, watchSubtree, buffer, countAddress, overlappedAddress, completionKey); } // map completion map to watch key ck2key.put(completionKey, watchKey); registered = true; return watchKey; } finally { if (!registered) CloseHandle(handle); } }