/** * Opens file based on parameters and options, returning a FileDescriptor encapsulating the handle * to the open file. */ private static FileDescriptor open( String pathForWindows, String pathToCheck, Flags flags, long pSecurityDescriptor) throws WindowsException { // set to true if file must be truncated after open boolean truncateAfterOpen = false; // map options int dwDesiredAccess = 0; if (flags.read) dwDesiredAccess |= GENERIC_READ; if (flags.write) dwDesiredAccess |= GENERIC_WRITE; int dwShareMode = 0; if (flags.shareRead) dwShareMode |= FILE_SHARE_READ; if (flags.shareWrite) dwShareMode |= FILE_SHARE_WRITE; if (flags.shareDelete) dwShareMode |= FILE_SHARE_DELETE; int dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; int dwCreationDisposition = OPEN_EXISTING; if (flags.write) { if (flags.createNew) { dwCreationDisposition = CREATE_NEW; // force create to fail if file is orphaned reparse point dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT; } else { if (flags.create) dwCreationDisposition = OPEN_ALWAYS; if (flags.truncateExisting) { // Windows doesn't have a creation disposition that exactly // corresponds to CREATE + TRUNCATE_EXISTING so we use // the OPEN_ALWAYS mode and then truncate the file. if (dwCreationDisposition == OPEN_ALWAYS) { truncateAfterOpen = true; } else { dwCreationDisposition = TRUNCATE_EXISTING; } } } } if (flags.dsync || flags.sync) dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH; if (flags.overlapped) dwFlagsAndAttributes |= FILE_FLAG_OVERLAPPED; if (flags.deleteOnClose) dwFlagsAndAttributes |= FILE_FLAG_DELETE_ON_CLOSE; // NOFOLLOW_LINKS and NOFOLLOW_REPARSEPOINT mean open reparse point boolean okayToFollowLinks = true; if (dwCreationDisposition != CREATE_NEW && (flags.noFollowLinks || flags.openReparsePoint || flags.deleteOnClose)) { if (flags.noFollowLinks || flags.deleteOnClose) okayToFollowLinks = false; dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT; } // permission check if (pathToCheck != null) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { if (flags.read) sm.checkRead(pathToCheck); if (flags.write) sm.checkWrite(pathToCheck); if (flags.deleteOnClose) sm.checkDelete(pathToCheck); } } // open file long handle = CreateFile( pathForWindows, dwDesiredAccess, dwShareMode, pSecurityDescriptor, dwCreationDisposition, dwFlagsAndAttributes); // make sure this isn't a symbolic link. if (!okayToFollowLinks) { try { if (WindowsFileAttributes.readAttributes(handle).isSymbolicLink()) throw new WindowsException("File is symbolic link"); } catch (WindowsException x) { CloseHandle(handle); throw x; } } // truncate file (for CREATE + TRUNCATE_EXISTING case) if (truncateAfterOpen) { try { SetEndOfFile(handle); } catch (WindowsException x) { CloseHandle(handle); throw x; } } // make the file sparse if needed if (dwCreationDisposition == CREATE_NEW && flags.sparse) { try { DeviceIoControlSetSparse(handle); } catch (WindowsException x) { // ignore as sparse option is hint } } // create FileDescriptor and return FileDescriptor fdObj = new FileDescriptor(); fdAccess.setHandle(fdObj, handle); return fdObj; }
/** * 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); } }