feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
94
jdkSrc/jdk8/sun/nio/fs/AbstractAclFileAttributeView.java
Normal file
94
jdkSrc/jdk8/sun/nio/fs/AbstractAclFileAttributeView.java
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.attribute.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Base implementation of AclFileAttributeView
|
||||
*/
|
||||
|
||||
abstract class AbstractAclFileAttributeView
|
||||
implements AclFileAttributeView, DynamicFileAttributeView
|
||||
{
|
||||
private static final String OWNER_NAME = "owner";
|
||||
private static final String ACL_NAME = "acl";
|
||||
|
||||
@Override
|
||||
public final String name() {
|
||||
return "acl";
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public final void setAttribute(String attribute, Object value)
|
||||
throws IOException
|
||||
{
|
||||
if (attribute.equals(OWNER_NAME)) {
|
||||
setOwner((UserPrincipal)value);
|
||||
return;
|
||||
}
|
||||
if (attribute.equals(ACL_NAME)) {
|
||||
setAcl((List<AclEntry>)value);
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException("'" + name() + ":" +
|
||||
attribute + "' not recognized");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map<String,Object> readAttributes(String[] attributes)
|
||||
throws IOException
|
||||
{
|
||||
boolean acl = false;
|
||||
boolean owner = false;
|
||||
for (String attribute: attributes) {
|
||||
if (attribute.equals("*")) {
|
||||
owner = true;
|
||||
acl = true;
|
||||
continue;
|
||||
}
|
||||
if (attribute.equals(ACL_NAME)) {
|
||||
acl = true;
|
||||
continue;
|
||||
}
|
||||
if (attribute.equals(OWNER_NAME)) {
|
||||
owner = true;
|
||||
continue;
|
||||
}
|
||||
throw new IllegalArgumentException("'" + name() + ":" +
|
||||
attribute + "' not recognized");
|
||||
}
|
||||
Map<String,Object> result = new HashMap<>(2);
|
||||
if (acl)
|
||||
result.put(ACL_NAME, getAcl());
|
||||
if (owner)
|
||||
result.put(OWNER_NAME, getOwner());
|
||||
return Collections.unmodifiableMap(result);
|
||||
}
|
||||
}
|
||||
171
jdkSrc/jdk8/sun/nio/fs/AbstractBasicFileAttributeView.java
Normal file
171
jdkSrc/jdk8/sun/nio/fs/AbstractBasicFileAttributeView.java
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.attribute.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Base implementation of BasicFileAttributeView
|
||||
*/
|
||||
|
||||
abstract class AbstractBasicFileAttributeView
|
||||
implements BasicFileAttributeView, DynamicFileAttributeView
|
||||
{
|
||||
private static final String SIZE_NAME = "size";
|
||||
private static final String CREATION_TIME_NAME = "creationTime";
|
||||
private static final String LAST_ACCESS_TIME_NAME = "lastAccessTime";
|
||||
private static final String LAST_MODIFIED_TIME_NAME = "lastModifiedTime";
|
||||
private static final String FILE_KEY_NAME = "fileKey";
|
||||
private static final String IS_DIRECTORY_NAME = "isDirectory";
|
||||
private static final String IS_REGULAR_FILE_NAME = "isRegularFile";
|
||||
private static final String IS_SYMBOLIC_LINK_NAME = "isSymbolicLink";
|
||||
private static final String IS_OTHER_NAME = "isOther";
|
||||
|
||||
// the names of the basic attributes
|
||||
static final Set<String> basicAttributeNames =
|
||||
Util.newSet(SIZE_NAME,
|
||||
CREATION_TIME_NAME,
|
||||
LAST_ACCESS_TIME_NAME,
|
||||
LAST_MODIFIED_TIME_NAME,
|
||||
FILE_KEY_NAME,
|
||||
IS_DIRECTORY_NAME,
|
||||
IS_REGULAR_FILE_NAME,
|
||||
IS_SYMBOLIC_LINK_NAME,
|
||||
IS_OTHER_NAME);
|
||||
|
||||
protected AbstractBasicFileAttributeView() { }
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "basic";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String attribute, Object value)
|
||||
throws IOException
|
||||
{
|
||||
if (attribute.equals(LAST_MODIFIED_TIME_NAME)) {
|
||||
setTimes((FileTime)value, null, null);
|
||||
return;
|
||||
}
|
||||
if (attribute.equals(LAST_ACCESS_TIME_NAME)) {
|
||||
setTimes(null, (FileTime)value, null);
|
||||
return;
|
||||
}
|
||||
if (attribute.equals(CREATION_TIME_NAME)) {
|
||||
setTimes(null, null, (FileTime)value);
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException("'" + name() + ":" +
|
||||
attribute + "' not recognized");
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to build a map of attribute name/values.
|
||||
*/
|
||||
static class AttributesBuilder {
|
||||
private Set<String> names = new HashSet<>();
|
||||
private Map<String,Object> map = new HashMap<>();
|
||||
private boolean copyAll;
|
||||
|
||||
private AttributesBuilder(Set<String> allowed, String[] requested) {
|
||||
for (String name: requested) {
|
||||
if (name.equals("*")) {
|
||||
copyAll = true;
|
||||
} else {
|
||||
if (!allowed.contains(name))
|
||||
throw new IllegalArgumentException("'" + name + "' not recognized");
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates builder to build up a map of the matching attributes
|
||||
*/
|
||||
static AttributesBuilder create(Set<String> allowed, String[] requested) {
|
||||
return new AttributesBuilder(allowed, requested);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attribute should be returned in the map
|
||||
*/
|
||||
boolean match(String name) {
|
||||
return copyAll || names.contains(name);
|
||||
}
|
||||
|
||||
void add(String name, Object value) {
|
||||
map.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map. Discard all references to the AttributesBuilder
|
||||
* after invoking this method.
|
||||
*/
|
||||
Map<String,Object> unmodifiableMap() {
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by readAttributes or sub-classes to add all matching basic
|
||||
* attributes to the builder
|
||||
*/
|
||||
final void addRequestedBasicAttributes(BasicFileAttributes attrs,
|
||||
AttributesBuilder builder)
|
||||
{
|
||||
if (builder.match(SIZE_NAME))
|
||||
builder.add(SIZE_NAME, attrs.size());
|
||||
if (builder.match(CREATION_TIME_NAME))
|
||||
builder.add(CREATION_TIME_NAME, attrs.creationTime());
|
||||
if (builder.match(LAST_ACCESS_TIME_NAME))
|
||||
builder.add(LAST_ACCESS_TIME_NAME, attrs.lastAccessTime());
|
||||
if (builder.match(LAST_MODIFIED_TIME_NAME))
|
||||
builder.add(LAST_MODIFIED_TIME_NAME, attrs.lastModifiedTime());
|
||||
if (builder.match(FILE_KEY_NAME))
|
||||
builder.add(FILE_KEY_NAME, attrs.fileKey());
|
||||
if (builder.match(IS_DIRECTORY_NAME))
|
||||
builder.add(IS_DIRECTORY_NAME, attrs.isDirectory());
|
||||
if (builder.match(IS_REGULAR_FILE_NAME))
|
||||
builder.add(IS_REGULAR_FILE_NAME, attrs.isRegularFile());
|
||||
if (builder.match(IS_SYMBOLIC_LINK_NAME))
|
||||
builder.add(IS_SYMBOLIC_LINK_NAME, attrs.isSymbolicLink());
|
||||
if (builder.match(IS_OTHER_NAME))
|
||||
builder.add(IS_OTHER_NAME, attrs.isOther());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String,Object> readAttributes(String[] requested)
|
||||
throws IOException
|
||||
{
|
||||
AttributesBuilder builder =
|
||||
AttributesBuilder.create(basicAttributeNames, requested);
|
||||
addRequestedBasicAttributes(readAttributes(), builder);
|
||||
return builder.unmodifiableMap();
|
||||
}
|
||||
}
|
||||
110
jdkSrc/jdk8/sun/nio/fs/AbstractFileSystemProvider.java
Normal file
110
jdkSrc/jdk8/sun/nio/fs/AbstractFileSystemProvider.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base implementation class of FileSystemProvider
|
||||
*/
|
||||
|
||||
abstract class AbstractFileSystemProvider extends FileSystemProvider {
|
||||
protected AbstractFileSystemProvider() { }
|
||||
|
||||
/**
|
||||
* Splits the given attribute name into the name of an attribute view and
|
||||
* the attribute. If the attribute view is not identified then it assumed
|
||||
* to be "basic".
|
||||
*/
|
||||
private static String[] split(String attribute) {
|
||||
String[] s = new String[2];
|
||||
int pos = attribute.indexOf(':');
|
||||
if (pos == -1) {
|
||||
s[0] = "basic";
|
||||
s[1] = attribute;
|
||||
} else {
|
||||
s[0] = attribute.substring(0, pos++);
|
||||
s[1] = (pos == attribute.length()) ? "" : attribute.substring(pos);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a DynamicFileAttributeView by name. Returns {@code null} if the
|
||||
* view is not available.
|
||||
*/
|
||||
abstract DynamicFileAttributeView getFileAttributeView(Path file,
|
||||
String name,
|
||||
LinkOption... options);
|
||||
|
||||
@Override
|
||||
public final void setAttribute(Path file,
|
||||
String attribute,
|
||||
Object value,
|
||||
LinkOption... options)
|
||||
throws IOException
|
||||
{
|
||||
String[] s = split(attribute);
|
||||
if (s[0].length() == 0)
|
||||
throw new IllegalArgumentException(attribute);
|
||||
DynamicFileAttributeView view = getFileAttributeView(file, s[0], options);
|
||||
if (view == null)
|
||||
throw new UnsupportedOperationException("View '" + s[0] + "' not available");
|
||||
view.setAttribute(s[1], value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map<String,Object> readAttributes(Path file, String attributes, LinkOption... options)
|
||||
throws IOException
|
||||
{
|
||||
String[] s = split(attributes);
|
||||
if (s[0].length() == 0)
|
||||
throw new IllegalArgumentException(attributes);
|
||||
DynamicFileAttributeView view = getFileAttributeView(file, s[0], options);
|
||||
if (view == null)
|
||||
throw new UnsupportedOperationException("View '" + s[0] + "' not available");
|
||||
return view.readAttributes(s[1].split(","));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file. The {@code failIfNotExists} parameters determines if an
|
||||
* {@code IOException} is thrown when the file does not exist.
|
||||
*/
|
||||
abstract boolean implDelete(Path file, boolean failIfNotExists) throws IOException;
|
||||
|
||||
@Override
|
||||
public final void delete(Path file) throws IOException {
|
||||
implDelete(file, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean deleteIfExists(Path file) throws IOException {
|
||||
return implDelete(file, false);
|
||||
}
|
||||
}
|
||||
111
jdkSrc/jdk8/sun/nio/fs/AbstractFileTypeDetector.java
Normal file
111
jdkSrc/jdk8/sun/nio/fs/AbstractFileTypeDetector.java
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.spi.FileTypeDetector;
|
||||
import java.util.Locale;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Base implementation of FileTypeDetector
|
||||
*/
|
||||
|
||||
public abstract class AbstractFileTypeDetector
|
||||
extends FileTypeDetector
|
||||
{
|
||||
protected AbstractFileTypeDetector() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the appropriate probe method to guess a file's content type,
|
||||
* and checks that the content type's syntax is valid.
|
||||
*/
|
||||
@Override
|
||||
public final String probeContentType(Path file) throws IOException {
|
||||
if (file == null)
|
||||
throw new NullPointerException("'file' is null");
|
||||
String result = implProbeContentType(file);
|
||||
return (result == null) ? null : parse(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Probes the given file to guess its content type.
|
||||
*/
|
||||
protected abstract String implProbeContentType(Path file)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Parses a candidate content type into its type and subtype, returning
|
||||
* null if either token is invalid.
|
||||
*/
|
||||
private static String parse(String s) {
|
||||
int slash = s.indexOf('/');
|
||||
int semicolon = s.indexOf(';');
|
||||
if (slash < 0)
|
||||
return null; // no subtype
|
||||
String type = s.substring(0, slash).trim().toLowerCase(Locale.ENGLISH);
|
||||
if (!isValidToken(type))
|
||||
return null; // invalid type
|
||||
String subtype = (semicolon < 0) ? s.substring(slash + 1) :
|
||||
s.substring(slash + 1, semicolon);
|
||||
subtype = subtype.trim().toLowerCase(Locale.ENGLISH);
|
||||
if (!isValidToken(subtype))
|
||||
return null; // invalid subtype
|
||||
StringBuilder sb = new StringBuilder(type.length() + subtype.length() + 1);
|
||||
sb.append(type);
|
||||
sb.append('/');
|
||||
sb.append(subtype);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Special characters
|
||||
*/
|
||||
private static final String TSPECIALS = "()<>@,;:/[]?=\\\"";
|
||||
|
||||
/**
|
||||
* Returns true if the character is a valid token character.
|
||||
*/
|
||||
private static boolean isTokenChar(char c) {
|
||||
return (c > 040) && (c < 0177) && (TSPECIALS.indexOf(c) < 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given string is a legal type or subtype.
|
||||
*/
|
||||
private static boolean isValidToken(String s) {
|
||||
int len = s.length();
|
||||
if (len == 0)
|
||||
return false;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (!isTokenChar(s.charAt(i)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
106
jdkSrc/jdk8/sun/nio/fs/AbstractPath.java
Normal file
106
jdkSrc/jdk8/sun/nio/fs/AbstractPath.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Base implementation class of {@code Path}.
|
||||
*/
|
||||
|
||||
abstract class AbstractPath implements Path {
|
||||
protected AbstractPath() { }
|
||||
|
||||
@Override
|
||||
public final boolean startsWith(String other) {
|
||||
return startsWith(getFileSystem().getPath(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean endsWith(String other) {
|
||||
return endsWith(getFileSystem().getPath(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Path resolve(String other) {
|
||||
return resolve(getFileSystem().getPath(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Path resolveSibling(Path other) {
|
||||
if (other == null)
|
||||
throw new NullPointerException();
|
||||
Path parent = getParent();
|
||||
return (parent == null) ? other : parent.resolve(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Path resolveSibling(String other) {
|
||||
return resolveSibling(getFileSystem().getPath(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Iterator<Path> iterator() {
|
||||
return new Iterator<Path>() {
|
||||
private int i = 0;
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return (i < getNameCount());
|
||||
}
|
||||
@Override
|
||||
public Path next() {
|
||||
if (i < getNameCount()) {
|
||||
Path result = getName(i);
|
||||
i++;
|
||||
return result;
|
||||
} else {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public final File toFile() {
|
||||
return new File(toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey register(WatchService watcher,
|
||||
WatchEvent.Kind<?>... events)
|
||||
throws IOException
|
||||
{
|
||||
return register(watcher, events, new WatchEvent.Modifier[0]);
|
||||
}
|
||||
}
|
||||
290
jdkSrc/jdk8/sun/nio/fs/AbstractPoller.java
Normal file
290
jdkSrc/jdk8/sun/nio/fs/AbstractPoller.java
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Base implementation of background poller thread used in watch service
|
||||
* implementations. A poller thread waits on events from the file system and
|
||||
* also services "requests" from clients to register for new events or cancel
|
||||
* existing registrations.
|
||||
*/
|
||||
|
||||
abstract class AbstractPoller implements Runnable {
|
||||
|
||||
// list of requests pending to the poller thread
|
||||
private final LinkedList<Request> requestList;
|
||||
|
||||
// set to true when shutdown
|
||||
private boolean shutdown;
|
||||
|
||||
protected AbstractPoller() {
|
||||
this.requestList = new LinkedList<Request>();
|
||||
this.shutdown = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the poller thread
|
||||
*/
|
||||
public void start() {
|
||||
final Runnable thisRunnable = this;
|
||||
AccessController.doPrivileged(new PrivilegedAction<Object>() {
|
||||
@Override
|
||||
public Object run() {
|
||||
Thread thr = new Thread(thisRunnable);
|
||||
thr.setDaemon(true);
|
||||
thr.start();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wakeup poller thread so that it can service pending requests
|
||||
*/
|
||||
abstract void wakeup() throws IOException;
|
||||
|
||||
/**
|
||||
* Executed by poller thread to register directory for changes
|
||||
*/
|
||||
abstract Object implRegister(Path path,
|
||||
Set<? extends WatchEvent.Kind<?>> events,
|
||||
WatchEvent.Modifier... modifiers);
|
||||
|
||||
/**
|
||||
* Executed by poller thread to cancel key
|
||||
*/
|
||||
abstract void implCancelKey(WatchKey key);
|
||||
|
||||
/**
|
||||
* Executed by poller thread to shutdown and cancel all keys
|
||||
*/
|
||||
abstract void implCloseAll();
|
||||
|
||||
/**
|
||||
* Requests, and waits on, poller thread to register given file.
|
||||
*/
|
||||
final WatchKey register(Path dir,
|
||||
WatchEvent.Kind<?>[] events,
|
||||
WatchEvent.Modifier... modifiers)
|
||||
throws IOException
|
||||
{
|
||||
// validate arguments before request to poller
|
||||
if (dir == null)
|
||||
throw new NullPointerException();
|
||||
Set<WatchEvent.Kind<?>> eventSet = new HashSet<>(events.length);
|
||||
for (WatchEvent.Kind<?> event: events) {
|
||||
// standard events
|
||||
if (event == StandardWatchEventKinds.ENTRY_CREATE ||
|
||||
event == StandardWatchEventKinds.ENTRY_MODIFY ||
|
||||
event == StandardWatchEventKinds.ENTRY_DELETE)
|
||||
{
|
||||
eventSet.add(event);
|
||||
continue;
|
||||
}
|
||||
|
||||
// OVERFLOW is ignored
|
||||
if (event == StandardWatchEventKinds.OVERFLOW)
|
||||
continue;
|
||||
|
||||
// null/unsupported
|
||||
if (event == null)
|
||||
throw new NullPointerException("An element in event set is 'null'");
|
||||
throw new UnsupportedOperationException(event.name());
|
||||
}
|
||||
if (eventSet.isEmpty())
|
||||
throw new IllegalArgumentException("No events to register");
|
||||
return (WatchKey)invoke(RequestType.REGISTER, dir, eventSet, modifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels, and waits on, poller thread to cancel given key.
|
||||
*/
|
||||
final void cancel(WatchKey key) {
|
||||
try {
|
||||
invoke(RequestType.CANCEL, key);
|
||||
} catch (IOException x) {
|
||||
// should not happen
|
||||
throw new AssertionError(x.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown poller thread
|
||||
*/
|
||||
final void close() throws IOException {
|
||||
invoke(RequestType.CLOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Types of request that the poller thread must handle
|
||||
*/
|
||||
private static enum RequestType {
|
||||
REGISTER,
|
||||
CANCEL,
|
||||
CLOSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates a request (command) to the poller thread.
|
||||
*/
|
||||
private static class Request {
|
||||
private final RequestType type;
|
||||
private final Object[] params;
|
||||
|
||||
private boolean completed = false;
|
||||
private Object result = null;
|
||||
|
||||
Request(RequestType type, Object... params) {
|
||||
this.type = type;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
RequestType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
Object[] parameters() {
|
||||
return params;
|
||||
}
|
||||
|
||||
void release(Object result) {
|
||||
synchronized (this) {
|
||||
this.completed = true;
|
||||
this.result = result;
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Await completion of the request. The return value is the result of
|
||||
* the request.
|
||||
*/
|
||||
Object awaitResult() {
|
||||
boolean interrupted = false;
|
||||
synchronized (this) {
|
||||
while (!completed) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException x) {
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
if (interrupted)
|
||||
Thread.currentThread().interrupt();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues request to poller thread and waits for result
|
||||
*/
|
||||
private Object invoke(RequestType type, Object... params) throws IOException {
|
||||
// submit request
|
||||
Request req = new Request(type, params);
|
||||
synchronized (requestList) {
|
||||
if (shutdown) {
|
||||
throw new ClosedWatchServiceException();
|
||||
}
|
||||
requestList.add(req);
|
||||
}
|
||||
|
||||
// wakeup thread
|
||||
wakeup();
|
||||
|
||||
// wait for result
|
||||
Object result = req.awaitResult();
|
||||
|
||||
if (result instanceof RuntimeException)
|
||||
throw (RuntimeException)result;
|
||||
if (result instanceof IOException )
|
||||
throw (IOException)result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by poller thread to process all pending requests
|
||||
*
|
||||
* @return true if poller thread should shutdown
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
boolean processRequests() {
|
||||
synchronized (requestList) {
|
||||
Request req;
|
||||
while ((req = requestList.poll()) != null) {
|
||||
// if in process of shutdown then reject request
|
||||
if (shutdown) {
|
||||
req.release(new ClosedWatchServiceException());
|
||||
}
|
||||
|
||||
switch (req.type()) {
|
||||
/**
|
||||
* Register directory
|
||||
*/
|
||||
case REGISTER: {
|
||||
Object[] params = req.parameters();
|
||||
Path path = (Path)params[0];
|
||||
Set<? extends WatchEvent.Kind<?>> events =
|
||||
(Set<? extends WatchEvent.Kind<?>>)params[1];
|
||||
WatchEvent.Modifier[] modifiers =
|
||||
(WatchEvent.Modifier[])params[2];
|
||||
req.release(implRegister(path, events, modifiers));
|
||||
break;
|
||||
}
|
||||
/**
|
||||
* Cancel existing key
|
||||
*/
|
||||
case CANCEL : {
|
||||
Object[] params = req.parameters();
|
||||
WatchKey key = (WatchKey)params[0];
|
||||
implCancelKey(key);
|
||||
req.release(null);
|
||||
break;
|
||||
}
|
||||
/**
|
||||
* Close watch service
|
||||
*/
|
||||
case CLOSE: {
|
||||
implCloseAll();
|
||||
req.release(null);
|
||||
shutdown = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
req.release(new IOException("request not recognized"));
|
||||
}
|
||||
}
|
||||
}
|
||||
return shutdown;
|
||||
}
|
||||
}
|
||||
103
jdkSrc/jdk8/sun/nio/fs/AbstractUserDefinedFileAttributeView.java
Normal file
103
jdkSrc/jdk8/sun/nio/fs/AbstractUserDefinedFileAttributeView.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.attribute.UserDefinedFileAttributeView;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Base implementation of UserDefinedAttributeView
|
||||
*/
|
||||
|
||||
abstract class AbstractUserDefinedFileAttributeView
|
||||
implements UserDefinedFileAttributeView, DynamicFileAttributeView
|
||||
{
|
||||
protected AbstractUserDefinedFileAttributeView() { }
|
||||
|
||||
protected void checkAccess(String file,
|
||||
boolean checkRead,
|
||||
boolean checkWrite)
|
||||
{
|
||||
assert checkRead || checkWrite;
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
if (checkRead)
|
||||
sm.checkRead(file);
|
||||
if (checkWrite)
|
||||
sm.checkWrite(file);
|
||||
sm.checkPermission(new RuntimePermission("accessUserDefinedAttributes"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String name() {
|
||||
return "user";
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setAttribute(String attribute, Object value)
|
||||
throws IOException
|
||||
{
|
||||
ByteBuffer bb;
|
||||
if (value instanceof byte[]) {
|
||||
bb = ByteBuffer.wrap((byte[])value);
|
||||
} else {
|
||||
bb = (ByteBuffer)value;
|
||||
}
|
||||
write(attribute, bb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map<String,Object> readAttributes(String[] attributes)
|
||||
throws IOException
|
||||
{
|
||||
// names of attributes to return
|
||||
List<String> names = new ArrayList<>();
|
||||
for (String name: attributes) {
|
||||
if (name.equals("*")) {
|
||||
names = list();
|
||||
break;
|
||||
} else {
|
||||
if (name.length() == 0)
|
||||
throw new IllegalArgumentException();
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
// read each value and return in map
|
||||
Map<String,Object> result = new HashMap<>();
|
||||
for (String name: names) {
|
||||
int size = size(name);
|
||||
byte[] buf = new byte[size];
|
||||
int n = read(name, ByteBuffer.wrap(buf));
|
||||
byte[] value = (n == size) ? buf : Arrays.copyOf(buf, n);
|
||||
result.put(name, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
223
jdkSrc/jdk8/sun/nio/fs/AbstractWatchKey.java
Normal file
223
jdkSrc/jdk8/sun/nio/fs/AbstractWatchKey.java
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Base implementation class for watch keys.
|
||||
*/
|
||||
|
||||
abstract class AbstractWatchKey implements WatchKey {
|
||||
|
||||
/**
|
||||
* Maximum size of event list (in the future this may be tunable)
|
||||
*/
|
||||
static final int MAX_EVENT_LIST_SIZE = 512;
|
||||
|
||||
/**
|
||||
* Special event to signal overflow
|
||||
*/
|
||||
static final Event<Object> OVERFLOW_EVENT =
|
||||
new Event<Object>(StandardWatchEventKinds.OVERFLOW, null);
|
||||
|
||||
/**
|
||||
* Possible key states
|
||||
*/
|
||||
private static enum State { READY, SIGNALLED };
|
||||
|
||||
// reference to watcher
|
||||
private final AbstractWatchService watcher;
|
||||
|
||||
// reference to the original directory
|
||||
private final Path dir;
|
||||
|
||||
// key state
|
||||
private State state;
|
||||
|
||||
// pending events
|
||||
private List<WatchEvent<?>> events;
|
||||
|
||||
// maps a context to the last event for the context (iff the last queued
|
||||
// event for the context is an ENTRY_MODIFY event).
|
||||
private Map<Object,WatchEvent<?>> lastModifyEvents;
|
||||
|
||||
protected AbstractWatchKey(Path dir, AbstractWatchService watcher) {
|
||||
this.watcher = watcher;
|
||||
this.dir = dir;
|
||||
this.state = State.READY;
|
||||
this.events = new ArrayList<WatchEvent<?>>();
|
||||
this.lastModifyEvents = new HashMap<Object,WatchEvent<?>>();
|
||||
}
|
||||
|
||||
final AbstractWatchService watcher() {
|
||||
return watcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original watchable (Path)
|
||||
*/
|
||||
@Override
|
||||
public Path watchable() {
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues this key to the watch service
|
||||
*/
|
||||
final void signal() {
|
||||
synchronized (this) {
|
||||
if (state == State.READY) {
|
||||
state = State.SIGNALLED;
|
||||
watcher.enqueueKey(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the event to this key and signals it.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
final void signalEvent(WatchEvent.Kind<?> kind, Object context) {
|
||||
boolean isModify = (kind == StandardWatchEventKinds.ENTRY_MODIFY);
|
||||
synchronized (this) {
|
||||
int size = events.size();
|
||||
if (size > 0) {
|
||||
// if the previous event is an OVERFLOW event or this is a
|
||||
// repeated event then we simply increment the counter
|
||||
WatchEvent<?> prev = events.get(size-1);
|
||||
if ((prev.kind() == StandardWatchEventKinds.OVERFLOW) ||
|
||||
((kind == prev.kind() &&
|
||||
Objects.equals(context, prev.context()))))
|
||||
{
|
||||
((Event<?>)prev).increment();
|
||||
return;
|
||||
}
|
||||
|
||||
// if this is a modify event and the last entry for the context
|
||||
// is a modify event then we simply increment the count
|
||||
if (!lastModifyEvents.isEmpty()) {
|
||||
if (isModify) {
|
||||
WatchEvent<?> ev = lastModifyEvents.get(context);
|
||||
if (ev != null) {
|
||||
assert ev.kind() == StandardWatchEventKinds.ENTRY_MODIFY;
|
||||
((Event<?>)ev).increment();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// not a modify event so remove from the map as the
|
||||
// last event will no longer be a modify event.
|
||||
lastModifyEvents.remove(context);
|
||||
}
|
||||
}
|
||||
|
||||
// if the list has reached the limit then drop pending events
|
||||
// and queue an OVERFLOW event
|
||||
if (size >= MAX_EVENT_LIST_SIZE) {
|
||||
kind = StandardWatchEventKinds.OVERFLOW;
|
||||
isModify = false;
|
||||
context = null;
|
||||
}
|
||||
}
|
||||
|
||||
// non-repeated event
|
||||
Event<Object> ev =
|
||||
new Event<Object>((WatchEvent.Kind<Object>)kind, context);
|
||||
if (isModify) {
|
||||
lastModifyEvents.put(context, ev);
|
||||
} else if (kind == StandardWatchEventKinds.OVERFLOW) {
|
||||
// drop all pending events
|
||||
events.clear();
|
||||
lastModifyEvents.clear();
|
||||
}
|
||||
events.add(ev);
|
||||
signal();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<WatchEvent<?>> pollEvents() {
|
||||
synchronized (this) {
|
||||
List<WatchEvent<?>> result = events;
|
||||
events = new ArrayList<WatchEvent<?>>();
|
||||
lastModifyEvents.clear();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean reset() {
|
||||
synchronized (this) {
|
||||
if (state == State.SIGNALLED && isValid()) {
|
||||
if (events.isEmpty()) {
|
||||
state = State.READY;
|
||||
} else {
|
||||
// pending events so re-queue key
|
||||
watcher.enqueueKey(this);
|
||||
}
|
||||
}
|
||||
return isValid();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WatchEvent implementation
|
||||
*/
|
||||
private static class Event<T> implements WatchEvent<T> {
|
||||
private final WatchEvent.Kind<T> kind;
|
||||
private final T context;
|
||||
|
||||
// synchronize on watch key to access/increment count
|
||||
private int count;
|
||||
|
||||
Event(WatchEvent.Kind<T> type, T context) {
|
||||
this.kind = type;
|
||||
this.context = context;
|
||||
this.count = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WatchEvent.Kind<T> kind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int count() {
|
||||
return count;
|
||||
}
|
||||
|
||||
// for repeated events
|
||||
void increment() {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
161
jdkSrc/jdk8/sun/nio/fs/AbstractWatchService.java
Normal file
161
jdkSrc/jdk8/sun/nio/fs/AbstractWatchService.java
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Base implementation class for watch services.
|
||||
*/
|
||||
|
||||
abstract class AbstractWatchService implements WatchService {
|
||||
|
||||
// signaled keys waiting to be dequeued
|
||||
private final LinkedBlockingDeque<WatchKey> pendingKeys =
|
||||
new LinkedBlockingDeque<WatchKey>();
|
||||
|
||||
// special key to indicate that watch service is closed
|
||||
private final WatchKey CLOSE_KEY =
|
||||
new AbstractWatchKey(null, null) {
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
};
|
||||
|
||||
// used when closing watch service
|
||||
private volatile boolean closed;
|
||||
private final Object closeLock = new Object();
|
||||
|
||||
protected AbstractWatchService() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given object with this watch service
|
||||
*/
|
||||
abstract WatchKey register(Path path,
|
||||
WatchEvent.Kind<?>[] events,
|
||||
WatchEvent.Modifier... modifers)
|
||||
throws IOException;
|
||||
|
||||
// used by AbstractWatchKey to enqueue key
|
||||
final void enqueueKey(WatchKey key) {
|
||||
pendingKeys.offer(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws ClosedWatchServiceException if watch service is closed
|
||||
*/
|
||||
private void checkOpen() {
|
||||
if (closed)
|
||||
throw new ClosedWatchServiceException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the key isn't the special CLOSE_KEY used to unblock threads when
|
||||
* the watch service is closed.
|
||||
*/
|
||||
private void checkKey(WatchKey key) {
|
||||
if (key == CLOSE_KEY) {
|
||||
// re-queue in case there are other threads blocked in take/poll
|
||||
enqueueKey(key);
|
||||
}
|
||||
checkOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey poll() {
|
||||
checkOpen();
|
||||
WatchKey key = pendingKeys.poll();
|
||||
checkKey(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey poll(long timeout, TimeUnit unit)
|
||||
throws InterruptedException
|
||||
{
|
||||
checkOpen();
|
||||
WatchKey key = pendingKeys.poll(timeout, unit);
|
||||
checkKey(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey take()
|
||||
throws InterruptedException
|
||||
{
|
||||
checkOpen();
|
||||
WatchKey key = pendingKeys.take();
|
||||
checkKey(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether or not this watch service is open.
|
||||
*/
|
||||
final boolean isOpen() {
|
||||
return !closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the object upon which the close method synchronizes.
|
||||
*/
|
||||
final Object closeLock() {
|
||||
return closeLock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this watch service. This method is invoked by the close
|
||||
* method to perform the actual work of closing the watch service.
|
||||
*/
|
||||
abstract void implClose() throws IOException;
|
||||
|
||||
@Override
|
||||
public final void close()
|
||||
throws IOException
|
||||
{
|
||||
synchronized (closeLock) {
|
||||
// nothing to do if already closed
|
||||
if (closed)
|
||||
return;
|
||||
closed = true;
|
||||
|
||||
implClose();
|
||||
|
||||
// clear pending keys and queue special key to ensure that any
|
||||
// threads blocked in take/poll wakeup
|
||||
pendingKeys.clear();
|
||||
pendingKeys.offer(CLOSE_KEY);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
jdkSrc/jdk8/sun/nio/fs/BasicFileAttributesHolder.java
Normal file
46
jdkSrc/jdk8/sun/nio/fs/BasicFileAttributesHolder.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
/**
|
||||
* Implemented by objects that may hold or cache the attributes of a file.
|
||||
*/
|
||||
|
||||
public interface BasicFileAttributesHolder {
|
||||
/**
|
||||
* Returns cached attributes (may be null). If file is a symbolic link then
|
||||
* the attributes are the link attributes and not the final target of the
|
||||
* file.
|
||||
*/
|
||||
BasicFileAttributes get();
|
||||
|
||||
/**
|
||||
* Invalidates cached attributes
|
||||
*/
|
||||
void invalidate();
|
||||
}
|
||||
137
jdkSrc/jdk8/sun/nio/fs/Cancellable.java
Normal file
137
jdkSrc/jdk8/sun/nio/fs/Cancellable.java
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* Base implementation of a task (typically native) that polls a memory location
|
||||
* during execution so that it may be aborted/cancelled before completion. The
|
||||
* task is executed by invoking the {@link runInterruptibly} method defined
|
||||
* here and cancelled by invoking Thread.interrupt.
|
||||
*/
|
||||
|
||||
abstract class Cancellable implements Runnable {
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
private final long pollingAddress;
|
||||
private final Object lock = new Object();
|
||||
|
||||
// the following require lock when examining or changing
|
||||
private boolean completed;
|
||||
private Throwable exception;
|
||||
|
||||
protected Cancellable() {
|
||||
pollingAddress = unsafe.allocateMemory(4);
|
||||
unsafe.putIntVolatile(null, pollingAddress, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the memory address of a 4-byte int that should be polled to
|
||||
* detect cancellation.
|
||||
*/
|
||||
protected long addressToPollForCancel() {
|
||||
return pollingAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* The value to write to the polled memory location to indicate that the
|
||||
* task has been cancelled. If this method is not overridden then it
|
||||
* defaults to MAX_VALUE.
|
||||
*/
|
||||
protected int cancelValue() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* "cancels" the task by writing bits into memory location that it polled
|
||||
* by the task.
|
||||
*/
|
||||
final void cancel() {
|
||||
synchronized (lock) {
|
||||
if (!completed) {
|
||||
unsafe.putIntVolatile(null, pollingAddress, cancelValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exception thrown by the task or null if the task completed
|
||||
* successfully.
|
||||
*/
|
||||
private Throwable exception() {
|
||||
synchronized (lock) {
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void run() {
|
||||
try {
|
||||
implRun();
|
||||
} catch (Throwable t) {
|
||||
synchronized (lock) {
|
||||
exception = t;
|
||||
}
|
||||
} finally {
|
||||
synchronized (lock) {
|
||||
completed = true;
|
||||
unsafe.freeMemory(pollingAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The task body. This should periodically poll the memory location
|
||||
* to check for cancellation.
|
||||
*/
|
||||
abstract void implRun() throws Throwable;
|
||||
|
||||
/**
|
||||
* Invokes the given task in its own thread. If this (meaning the current)
|
||||
* thread is interrupted then an attempt is make to cancel the background
|
||||
* thread by writing into the memory location that it polls cooperatively.
|
||||
*/
|
||||
static void runInterruptibly(Cancellable task) throws ExecutionException {
|
||||
Thread t = new Thread(task);
|
||||
t.start();
|
||||
boolean cancelledByInterrupt = false;
|
||||
while (t.isAlive()) {
|
||||
try {
|
||||
t.join();
|
||||
} catch (InterruptedException e) {
|
||||
cancelledByInterrupt = true;
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
if (cancelledByInterrupt)
|
||||
Thread.currentThread().interrupt();
|
||||
Throwable exc = task.exception();
|
||||
if (exc != null)
|
||||
throw new ExecutionException(exc);
|
||||
}
|
||||
}
|
||||
38
jdkSrc/jdk8/sun/nio/fs/DefaultFileSystemProvider.java
Normal file
38
jdkSrc/jdk8/sun/nio/fs/DefaultFileSystemProvider.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
|
||||
/**
|
||||
* Creates default provider on Windows
|
||||
*/
|
||||
public class DefaultFileSystemProvider {
|
||||
private DefaultFileSystemProvider() { }
|
||||
public static FileSystemProvider create() {
|
||||
return new WindowsFileSystemProvider();
|
||||
}
|
||||
}
|
||||
36
jdkSrc/jdk8/sun/nio/fs/DefaultFileTypeDetector.java
Normal file
36
jdkSrc/jdk8/sun/nio/fs/DefaultFileTypeDetector.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.spi.FileTypeDetector;
|
||||
|
||||
public class DefaultFileTypeDetector {
|
||||
private DefaultFileTypeDetector() { }
|
||||
|
||||
public static FileTypeDetector create() {
|
||||
return new RegistryFileTypeDetector();
|
||||
}
|
||||
}
|
||||
46
jdkSrc/jdk8/sun/nio/fs/DynamicFileAttributeView.java
Normal file
46
jdkSrc/jdk8/sun/nio/fs/DynamicFileAttributeView.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Implemented by FileAttributeView implementations to support access to
|
||||
* attributes by names.
|
||||
*/
|
||||
|
||||
interface DynamicFileAttributeView {
|
||||
/**
|
||||
* Sets/updates the value of an attribute.
|
||||
*/
|
||||
void setAttribute(String attribute, Object value) throws IOException;
|
||||
|
||||
/**
|
||||
* Reads a set of file attributes as a bulk operation.
|
||||
*/
|
||||
Map<String,Object> readAttributes(String[] attributes) throws IOException;
|
||||
}
|
||||
105
jdkSrc/jdk8/sun/nio/fs/FileOwnerAttributeViewImpl.java
Normal file
105
jdkSrc/jdk8/sun/nio/fs/FileOwnerAttributeViewImpl.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.attribute.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An implementation of FileOwnerAttributeView that delegates to a given
|
||||
* PosixFileAttributeView or AclFileAttributeView object.
|
||||
*/
|
||||
|
||||
final class FileOwnerAttributeViewImpl
|
||||
implements FileOwnerAttributeView, DynamicFileAttributeView
|
||||
{
|
||||
private static final String OWNER_NAME = "owner";
|
||||
|
||||
private final FileAttributeView view;
|
||||
private final boolean isPosixView;
|
||||
|
||||
FileOwnerAttributeViewImpl(PosixFileAttributeView view) {
|
||||
this.view = view;
|
||||
this.isPosixView = true;
|
||||
}
|
||||
|
||||
FileOwnerAttributeViewImpl(AclFileAttributeView view) {
|
||||
this.view = view;
|
||||
this.isPosixView = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "owner";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String attribute, Object value)
|
||||
throws IOException
|
||||
{
|
||||
if (attribute.equals(OWNER_NAME)) {
|
||||
setOwner((UserPrincipal)value);
|
||||
} else {
|
||||
throw new IllegalArgumentException("'" + name() + ":" +
|
||||
attribute + "' not recognized");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String,Object> readAttributes(String[] attributes) throws IOException {
|
||||
Map<String,Object> result = new HashMap<>();
|
||||
for (String attribute: attributes) {
|
||||
if (attribute.equals("*") || attribute.equals(OWNER_NAME)) {
|
||||
result.put(OWNER_NAME, getOwner());
|
||||
} else {
|
||||
throw new IllegalArgumentException("'" + name() + ":" +
|
||||
attribute + "' not recognized");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserPrincipal getOwner() throws IOException {
|
||||
if (isPosixView) {
|
||||
return ((PosixFileAttributeView)view).readAttributes().owner();
|
||||
} else {
|
||||
return ((AclFileAttributeView)view).getOwner();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwner(UserPrincipal owner)
|
||||
throws IOException
|
||||
{
|
||||
if (isPosixView) {
|
||||
((PosixFileAttributeView)view).setOwner(owner);
|
||||
} else {
|
||||
((AclFileAttributeView)view).setOwner(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
217
jdkSrc/jdk8/sun/nio/fs/Globs.java
Normal file
217
jdkSrc/jdk8/sun/nio/fs/Globs.java
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
public class Globs {
|
||||
private Globs() { }
|
||||
|
||||
private static final String regexMetaChars = ".^$+{[]|()";
|
||||
private static final String globMetaChars = "\\*?[{";
|
||||
|
||||
private static boolean isRegexMeta(char c) {
|
||||
return regexMetaChars.indexOf(c) != -1;
|
||||
}
|
||||
|
||||
private static boolean isGlobMeta(char c) {
|
||||
return globMetaChars.indexOf(c) != -1;
|
||||
}
|
||||
private static char EOL = 0; //TBD
|
||||
|
||||
private static char next(String glob, int i) {
|
||||
if (i < glob.length()) {
|
||||
return glob.charAt(i);
|
||||
}
|
||||
return EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a regex pattern from the given glob expression.
|
||||
*
|
||||
* @throws PatternSyntaxException
|
||||
*/
|
||||
private static String toRegexPattern(String globPattern, boolean isDos) {
|
||||
boolean inGroup = false;
|
||||
StringBuilder regex = new StringBuilder("^");
|
||||
|
||||
int i = 0;
|
||||
while (i < globPattern.length()) {
|
||||
char c = globPattern.charAt(i++);
|
||||
switch (c) {
|
||||
case '\\':
|
||||
// escape special characters
|
||||
if (i == globPattern.length()) {
|
||||
throw new PatternSyntaxException("No character to escape",
|
||||
globPattern, i - 1);
|
||||
}
|
||||
char next = globPattern.charAt(i++);
|
||||
if (isGlobMeta(next) || isRegexMeta(next)) {
|
||||
regex.append('\\');
|
||||
}
|
||||
regex.append(next);
|
||||
break;
|
||||
case '/':
|
||||
if (isDos) {
|
||||
regex.append("\\\\");
|
||||
} else {
|
||||
regex.append(c);
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
// don't match name separator in class
|
||||
if (isDos) {
|
||||
regex.append("[[^\\\\]&&[");
|
||||
} else {
|
||||
regex.append("[[^/]&&[");
|
||||
}
|
||||
if (next(globPattern, i) == '^') {
|
||||
// escape the regex negation char if it appears
|
||||
regex.append("\\^");
|
||||
i++;
|
||||
} else {
|
||||
// negation
|
||||
if (next(globPattern, i) == '!') {
|
||||
regex.append('^');
|
||||
i++;
|
||||
}
|
||||
// hyphen allowed at start
|
||||
if (next(globPattern, i) == '-') {
|
||||
regex.append('-');
|
||||
i++;
|
||||
}
|
||||
}
|
||||
boolean hasRangeStart = false;
|
||||
char last = 0;
|
||||
while (i < globPattern.length()) {
|
||||
c = globPattern.charAt(i++);
|
||||
if (c == ']') {
|
||||
break;
|
||||
}
|
||||
if (c == '/' || (isDos && c == '\\')) {
|
||||
throw new PatternSyntaxException("Explicit 'name separator' in class",
|
||||
globPattern, i - 1);
|
||||
}
|
||||
// TBD: how to specify ']' in a class?
|
||||
if (c == '\\' || c == '[' ||
|
||||
c == '&' && next(globPattern, i) == '&') {
|
||||
// escape '\', '[' or "&&" for regex class
|
||||
regex.append('\\');
|
||||
}
|
||||
regex.append(c);
|
||||
|
||||
if (c == '-') {
|
||||
if (!hasRangeStart) {
|
||||
throw new PatternSyntaxException("Invalid range",
|
||||
globPattern, i - 1);
|
||||
}
|
||||
if ((c = next(globPattern, i++)) == EOL || c == ']') {
|
||||
break;
|
||||
}
|
||||
if (c < last) {
|
||||
throw new PatternSyntaxException("Invalid range",
|
||||
globPattern, i - 3);
|
||||
}
|
||||
regex.append(c);
|
||||
hasRangeStart = false;
|
||||
} else {
|
||||
hasRangeStart = true;
|
||||
last = c;
|
||||
}
|
||||
}
|
||||
if (c != ']') {
|
||||
throw new PatternSyntaxException("Missing ']", globPattern, i - 1);
|
||||
}
|
||||
regex.append("]]");
|
||||
break;
|
||||
case '{':
|
||||
if (inGroup) {
|
||||
throw new PatternSyntaxException("Cannot nest groups",
|
||||
globPattern, i - 1);
|
||||
}
|
||||
regex.append("(?:(?:");
|
||||
inGroup = true;
|
||||
break;
|
||||
case '}':
|
||||
if (inGroup) {
|
||||
regex.append("))");
|
||||
inGroup = false;
|
||||
} else {
|
||||
regex.append('}');
|
||||
}
|
||||
break;
|
||||
case ',':
|
||||
if (inGroup) {
|
||||
regex.append(")|(?:");
|
||||
} else {
|
||||
regex.append(',');
|
||||
}
|
||||
break;
|
||||
case '*':
|
||||
if (next(globPattern, i) == '*') {
|
||||
// crosses directory boundaries
|
||||
regex.append(".*");
|
||||
i++;
|
||||
} else {
|
||||
// within directory boundary
|
||||
if (isDos) {
|
||||
regex.append("[^\\\\]*");
|
||||
} else {
|
||||
regex.append("[^/]*");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
if (isDos) {
|
||||
regex.append("[^\\\\]");
|
||||
} else {
|
||||
regex.append("[^/]");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isRegexMeta(c)) {
|
||||
regex.append('\\');
|
||||
}
|
||||
regex.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (inGroup) {
|
||||
throw new PatternSyntaxException("Missing '}", globPattern, i - 1);
|
||||
}
|
||||
|
||||
return regex.append('$').toString();
|
||||
}
|
||||
|
||||
static String toUnixRegexPattern(String globPattern) {
|
||||
return toRegexPattern(globPattern, false);
|
||||
}
|
||||
|
||||
static String toWindowsRegexPattern(String globPattern) {
|
||||
return toRegexPattern(globPattern, true);
|
||||
}
|
||||
}
|
||||
87
jdkSrc/jdk8/sun/nio/fs/NativeBuffer.java
Normal file
87
jdkSrc/jdk8/sun/nio/fs/NativeBuffer.java
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
import sun.misc.Cleaner;
|
||||
|
||||
/**
|
||||
* A light-weight buffer in native memory.
|
||||
*/
|
||||
|
||||
class NativeBuffer {
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
private final long address;
|
||||
private final int size;
|
||||
private final Cleaner cleaner;
|
||||
|
||||
// optional "owner" to avoid copying
|
||||
// (only safe for use by thread-local caches)
|
||||
private Object owner;
|
||||
|
||||
private static class Deallocator implements Runnable {
|
||||
private final long address;
|
||||
Deallocator(long address) {
|
||||
this.address = address;
|
||||
}
|
||||
public void run() {
|
||||
unsafe.freeMemory(address);
|
||||
}
|
||||
}
|
||||
|
||||
NativeBuffer(int size) {
|
||||
this.address = unsafe.allocateMemory(size);
|
||||
this.size = size;
|
||||
this.cleaner = Cleaner.create(this, new Deallocator(address));
|
||||
}
|
||||
|
||||
void release() {
|
||||
NativeBuffers.releaseNativeBuffer(this);
|
||||
}
|
||||
|
||||
long address() {
|
||||
return address;
|
||||
}
|
||||
|
||||
int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
Cleaner cleaner() {
|
||||
return cleaner;
|
||||
}
|
||||
|
||||
// not synchronized; only safe for use by thread-local caches
|
||||
void setOwner(Object owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
// not synchronized; only safe for use by thread-local caches
|
||||
Object owner() {
|
||||
return owner;
|
||||
}
|
||||
}
|
||||
154
jdkSrc/jdk8/sun/nio/fs/NativeBuffers.java
Normal file
154
jdkSrc/jdk8/sun/nio/fs/NativeBuffers.java
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import jdk.internal.misc.TerminatingThreadLocal;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
/**
|
||||
* Factory for native buffers.
|
||||
*/
|
||||
|
||||
class NativeBuffers {
|
||||
private NativeBuffers() { }
|
||||
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
private static final int TEMP_BUF_POOL_SIZE = 3;
|
||||
private static ThreadLocal<NativeBuffer[]> threadLocal = new TerminatingThreadLocal<NativeBuffer[]>() {
|
||||
@Override
|
||||
protected void threadTerminated(NativeBuffer[] buffers) {
|
||||
// threadLocal may be initialized but with initialValue of null
|
||||
if (buffers != null) {
|
||||
for (int i = 0; i < TEMP_BUF_POOL_SIZE; i++) {
|
||||
NativeBuffer buffer = buffers[i];
|
||||
if (buffer != null) {
|
||||
buffer.cleaner().clean();
|
||||
buffers[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocates a native buffer, of at least the given size, from the heap.
|
||||
*/
|
||||
static NativeBuffer allocNativeBuffer(int size) {
|
||||
// Make a new one of at least 2k
|
||||
if (size < 2048) size = 2048;
|
||||
return new NativeBuffer(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a native buffer, of at least the given size, from the thread
|
||||
* local cache.
|
||||
*/
|
||||
static NativeBuffer getNativeBufferFromCache(int size) {
|
||||
// return from cache if possible
|
||||
NativeBuffer[] buffers = threadLocal.get();
|
||||
if (buffers != null) {
|
||||
for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
|
||||
NativeBuffer buffer = buffers[i];
|
||||
if (buffer != null && buffer.size() >= size) {
|
||||
buffers[i] = null;
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a native buffer, of at least the given size. The native buffer
|
||||
* is taken from the thread local cache if possible; otherwise it is
|
||||
* allocated from the heap.
|
||||
*/
|
||||
static NativeBuffer getNativeBuffer(int size) {
|
||||
NativeBuffer buffer = getNativeBufferFromCache(size);
|
||||
if (buffer != null) {
|
||||
buffer.setOwner(null);
|
||||
return buffer;
|
||||
} else {
|
||||
return allocNativeBuffer(size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the given buffer. If there is space in the thread local cache
|
||||
* then the buffer goes into the cache; otherwise the memory is deallocated.
|
||||
*/
|
||||
static void releaseNativeBuffer(NativeBuffer buffer) {
|
||||
// create cache if it doesn't exist
|
||||
NativeBuffer[] buffers = threadLocal.get();
|
||||
if (buffers == null) {
|
||||
buffers = new NativeBuffer[TEMP_BUF_POOL_SIZE];
|
||||
buffers[0] = buffer;
|
||||
threadLocal.set(buffers);
|
||||
return;
|
||||
}
|
||||
// Put it in an empty slot if such exists
|
||||
for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
|
||||
if (buffers[i] == null) {
|
||||
buffers[i] = buffer;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Otherwise replace a smaller one in the cache if such exists
|
||||
for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
|
||||
NativeBuffer existing = buffers[i];
|
||||
if (existing.size() < buffer.size()) {
|
||||
existing.cleaner().clean();
|
||||
buffers[i] = buffer;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// free it
|
||||
buffer.cleaner().clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a byte array and zero terminator into a given native buffer.
|
||||
*/
|
||||
static void copyCStringToNativeBuffer(byte[] cstr, NativeBuffer buffer) {
|
||||
long offset = Unsafe.ARRAY_BYTE_BASE_OFFSET;
|
||||
long len = cstr.length;
|
||||
assert buffer.size() >= (len + 1);
|
||||
unsafe.copyMemory(cstr, offset, null, buffer.address(), len);
|
||||
unsafe.putByte(buffer.address() + len, (byte)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a byte array and zero terminator into a native buffer, returning
|
||||
* the buffer.
|
||||
*/
|
||||
static NativeBuffer asNativeBuffer(byte[] cstr) {
|
||||
NativeBuffer buffer = getNativeBuffer(cstr.length+1);
|
||||
copyCStringToNativeBuffer(cstr, buffer);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
411
jdkSrc/jdk8/sun/nio/fs/PollingWatchService.java
Normal file
411
jdkSrc/jdk8/sun/nio/fs/PollingWatchService.java
Normal file
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import com.sun.nio.file.SensitivityWatchEventModifier;
|
||||
|
||||
/**
|
||||
* Simple WatchService implementation that uses periodic tasks to poll
|
||||
* registered directories for changes. This implementation is for use on
|
||||
* operating systems that do not have native file change notification support.
|
||||
*/
|
||||
|
||||
class PollingWatchService
|
||||
extends AbstractWatchService
|
||||
{
|
||||
// map of registrations
|
||||
private final Map<Object,PollingWatchKey> map =
|
||||
new HashMap<Object,PollingWatchKey>();
|
||||
|
||||
// used to execute the periodic tasks that poll for changes
|
||||
private final ScheduledExecutorService scheduledExecutor;
|
||||
|
||||
PollingWatchService() {
|
||||
// TBD: Make the number of threads configurable
|
||||
scheduledExecutor = Executors
|
||||
.newSingleThreadScheduledExecutor(new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r);
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given file with this watch service
|
||||
*/
|
||||
@Override
|
||||
WatchKey register(final Path path,
|
||||
WatchEvent.Kind<?>[] events,
|
||||
WatchEvent.Modifier... modifiers)
|
||||
throws IOException
|
||||
{
|
||||
// check events - CCE will be thrown if there are invalid elements
|
||||
final Set<WatchEvent.Kind<?>> eventSet =
|
||||
new HashSet<WatchEvent.Kind<?>>(events.length);
|
||||
for (WatchEvent.Kind<?> event: events) {
|
||||
// standard events
|
||||
if (event == StandardWatchEventKinds.ENTRY_CREATE ||
|
||||
event == StandardWatchEventKinds.ENTRY_MODIFY ||
|
||||
event == StandardWatchEventKinds.ENTRY_DELETE)
|
||||
{
|
||||
eventSet.add(event);
|
||||
continue;
|
||||
}
|
||||
|
||||
// OVERFLOW is ignored
|
||||
if (event == StandardWatchEventKinds.OVERFLOW) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// null/unsupported
|
||||
if (event == null)
|
||||
throw new NullPointerException("An element in event set is 'null'");
|
||||
throw new UnsupportedOperationException(event.name());
|
||||
}
|
||||
if (eventSet.isEmpty())
|
||||
throw new IllegalArgumentException("No events to register");
|
||||
|
||||
// A modifier may be used to specify the sensitivity level
|
||||
SensitivityWatchEventModifier sensivity = SensitivityWatchEventModifier.MEDIUM;
|
||||
if (modifiers.length > 0) {
|
||||
for (WatchEvent.Modifier modifier: modifiers) {
|
||||
if (modifier == null)
|
||||
throw new NullPointerException();
|
||||
if (modifier instanceof SensitivityWatchEventModifier) {
|
||||
sensivity = (SensitivityWatchEventModifier)modifier;
|
||||
continue;
|
||||
}
|
||||
throw new UnsupportedOperationException("Modifier not supported");
|
||||
}
|
||||
}
|
||||
|
||||
// check if watch service is closed
|
||||
if (!isOpen())
|
||||
throw new ClosedWatchServiceException();
|
||||
|
||||
// registration is done in privileged block as it requires the
|
||||
// attributes of the entries in the directory.
|
||||
try {
|
||||
final SensitivityWatchEventModifier s = sensivity;
|
||||
return AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<PollingWatchKey>() {
|
||||
@Override
|
||||
public PollingWatchKey run() throws IOException {
|
||||
return doPrivilegedRegister(path, eventSet, s);
|
||||
}
|
||||
});
|
||||
} catch (PrivilegedActionException pae) {
|
||||
Throwable cause = pae.getCause();
|
||||
if (cause != null && cause instanceof IOException)
|
||||
throw (IOException)cause;
|
||||
throw new AssertionError(pae);
|
||||
}
|
||||
}
|
||||
|
||||
// registers directory returning a new key if not already registered or
|
||||
// existing key if already registered
|
||||
private PollingWatchKey doPrivilegedRegister(Path path,
|
||||
Set<? extends WatchEvent.Kind<?>> events,
|
||||
SensitivityWatchEventModifier sensivity)
|
||||
throws IOException
|
||||
{
|
||||
// check file is a directory and get its file key if possible
|
||||
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
|
||||
if (!attrs.isDirectory()) {
|
||||
throw new NotDirectoryException(path.toString());
|
||||
}
|
||||
Object fileKey = attrs.fileKey();
|
||||
if (fileKey == null)
|
||||
throw new AssertionError("File keys must be supported");
|
||||
|
||||
// grab close lock to ensure that watch service cannot be closed
|
||||
synchronized (closeLock()) {
|
||||
if (!isOpen())
|
||||
throw new ClosedWatchServiceException();
|
||||
|
||||
PollingWatchKey watchKey;
|
||||
synchronized (map) {
|
||||
watchKey = map.get(fileKey);
|
||||
if (watchKey == null) {
|
||||
// new registration
|
||||
watchKey = new PollingWatchKey(path, this, fileKey);
|
||||
map.put(fileKey, watchKey);
|
||||
} else {
|
||||
// update to existing registration
|
||||
watchKey.disable();
|
||||
}
|
||||
}
|
||||
watchKey.enable(events, sensivity.sensitivityValueInSeconds());
|
||||
return watchKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void implClose() throws IOException {
|
||||
synchronized (map) {
|
||||
for (Map.Entry<Object,PollingWatchKey> entry: map.entrySet()) {
|
||||
PollingWatchKey watchKey = entry.getValue();
|
||||
watchKey.disable();
|
||||
watchKey.invalidate();
|
||||
}
|
||||
map.clear();
|
||||
}
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
scheduledExecutor.shutdown();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry in directory cache to record file last-modified-time and tick-count
|
||||
*/
|
||||
private static class CacheEntry {
|
||||
private long lastModified;
|
||||
private int lastTickCount;
|
||||
|
||||
CacheEntry(long lastModified, int lastTickCount) {
|
||||
this.lastModified = lastModified;
|
||||
this.lastTickCount = lastTickCount;
|
||||
}
|
||||
|
||||
int lastTickCount() {
|
||||
return lastTickCount;
|
||||
}
|
||||
|
||||
long lastModified() {
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
void update(long lastModified, int tickCount) {
|
||||
this.lastModified = lastModified;
|
||||
this.lastTickCount = tickCount;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WatchKey implementation that encapsulates a map of the entries of the
|
||||
* entries in the directory. Polling the key causes it to re-scan the
|
||||
* directory and queue keys when entries are added, modified, or deleted.
|
||||
*/
|
||||
private class PollingWatchKey extends AbstractWatchKey {
|
||||
private final Object fileKey;
|
||||
|
||||
// current event set
|
||||
private Set<? extends WatchEvent.Kind<?>> events;
|
||||
|
||||
// the result of the periodic task that causes this key to be polled
|
||||
private ScheduledFuture<?> poller;
|
||||
|
||||
// indicates if the key is valid
|
||||
private volatile boolean valid;
|
||||
|
||||
// used to detect files that have been deleted
|
||||
private int tickCount;
|
||||
|
||||
// map of entries in directory
|
||||
private Map<Path,CacheEntry> entries;
|
||||
|
||||
PollingWatchKey(Path dir, PollingWatchService watcher, Object fileKey)
|
||||
throws IOException
|
||||
{
|
||||
super(dir, watcher);
|
||||
this.fileKey = fileKey;
|
||||
this.valid = true;
|
||||
this.tickCount = 0;
|
||||
this.entries = new HashMap<Path,CacheEntry>();
|
||||
|
||||
// get the initial entries in the directory
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
|
||||
for (Path entry: stream) {
|
||||
// don't follow links
|
||||
long lastModified =
|
||||
Files.getLastModifiedTime(entry, LinkOption.NOFOLLOW_LINKS).toMillis();
|
||||
entries.put(entry.getFileName(), new CacheEntry(lastModified, tickCount));
|
||||
}
|
||||
} catch (DirectoryIteratorException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
Object fileKey() {
|
||||
return fileKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
void invalidate() {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
// enables periodic polling
|
||||
void enable(Set<? extends WatchEvent.Kind<?>> events, long period) {
|
||||
synchronized (this) {
|
||||
// update the events
|
||||
this.events = events;
|
||||
|
||||
// create the periodic task
|
||||
Runnable thunk = new Runnable() { public void run() { poll(); }};
|
||||
this.poller = scheduledExecutor
|
||||
.scheduleAtFixedRate(thunk, period, period, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
// disables periodic polling
|
||||
void disable() {
|
||||
synchronized (this) {
|
||||
if (poller != null)
|
||||
poller.cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
valid = false;
|
||||
synchronized (map) {
|
||||
map.remove(fileKey());
|
||||
}
|
||||
disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls the directory to detect for new files, modified files, or
|
||||
* deleted files.
|
||||
*/
|
||||
synchronized void poll() {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
// update tick
|
||||
tickCount++;
|
||||
|
||||
// open directory
|
||||
DirectoryStream<Path> stream = null;
|
||||
try {
|
||||
stream = Files.newDirectoryStream(watchable());
|
||||
} catch (IOException x) {
|
||||
// directory is no longer accessible so cancel key
|
||||
cancel();
|
||||
signal();
|
||||
return;
|
||||
}
|
||||
|
||||
// iterate over all entries in directory
|
||||
try {
|
||||
for (Path entry: stream) {
|
||||
long lastModified = 0L;
|
||||
try {
|
||||
lastModified =
|
||||
Files.getLastModifiedTime(entry, LinkOption.NOFOLLOW_LINKS).toMillis();
|
||||
} catch (IOException x) {
|
||||
// unable to get attributes of entry. If file has just
|
||||
// been deleted then we'll report it as deleted on the
|
||||
// next poll
|
||||
continue;
|
||||
}
|
||||
|
||||
// lookup cache
|
||||
CacheEntry e = entries.get(entry.getFileName());
|
||||
if (e == null) {
|
||||
// new file found
|
||||
entries.put(entry.getFileName(),
|
||||
new CacheEntry(lastModified, tickCount));
|
||||
|
||||
// queue ENTRY_CREATE if event enabled
|
||||
if (events.contains(StandardWatchEventKinds.ENTRY_CREATE)) {
|
||||
signalEvent(StandardWatchEventKinds.ENTRY_CREATE, entry.getFileName());
|
||||
continue;
|
||||
} else {
|
||||
// if ENTRY_CREATE is not enabled and ENTRY_MODIFY is
|
||||
// enabled then queue event to avoid missing out on
|
||||
// modifications to the file immediately after it is
|
||||
// created.
|
||||
if (events.contains(StandardWatchEventKinds.ENTRY_MODIFY)) {
|
||||
signalEvent(StandardWatchEventKinds.ENTRY_MODIFY, entry.getFileName());
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if file has changed
|
||||
if (e.lastModified != lastModified) {
|
||||
if (events.contains(StandardWatchEventKinds.ENTRY_MODIFY)) {
|
||||
signalEvent(StandardWatchEventKinds.ENTRY_MODIFY,
|
||||
entry.getFileName());
|
||||
}
|
||||
}
|
||||
// entry in cache so update poll time
|
||||
e.update(lastModified, tickCount);
|
||||
|
||||
}
|
||||
} catch (DirectoryIteratorException e) {
|
||||
// ignore for now; if the directory is no longer accessible
|
||||
// then the key will be cancelled on the next poll
|
||||
} finally {
|
||||
|
||||
// close directory stream
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException x) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over cache to detect entries that have been deleted
|
||||
Iterator<Map.Entry<Path,CacheEntry>> i = entries.entrySet().iterator();
|
||||
while (i.hasNext()) {
|
||||
Map.Entry<Path,CacheEntry> mapEntry = i.next();
|
||||
CacheEntry entry = mapEntry.getValue();
|
||||
if (entry.lastTickCount() != tickCount) {
|
||||
Path name = mapEntry.getKey();
|
||||
// remove from map and queue delete event (if enabled)
|
||||
i.remove();
|
||||
if (events.contains(StandardWatchEventKinds.ENTRY_DELETE)) {
|
||||
signalEvent(StandardWatchEventKinds.ENTRY_DELETE, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
jdkSrc/jdk8/sun/nio/fs/Reflect.java
Normal file
63
jdkSrc/jdk8/sun/nio/fs/Reflect.java
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/**
|
||||
* Utility class for reflection.
|
||||
*/
|
||||
|
||||
class Reflect {
|
||||
private Reflect() {}
|
||||
|
||||
private static void setAccessible(final AccessibleObject ao) {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Object>() {
|
||||
@Override
|
||||
public Object run() {
|
||||
ao.setAccessible(true);
|
||||
return null;
|
||||
}});
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the field of a given class.
|
||||
*/
|
||||
static Field lookupField(String className, String fieldName) {
|
||||
try {
|
||||
Class<?> cl = Class.forName(className);
|
||||
Field f = cl.getDeclaredField(fieldName);
|
||||
setAccessible(f);
|
||||
return f;
|
||||
} catch (ClassNotFoundException x) {
|
||||
throw new AssertionError(x);
|
||||
} catch (NoSuchFieldException x) {
|
||||
throw new AssertionError(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
87
jdkSrc/jdk8/sun/nio/fs/RegistryFileTypeDetector.java
Normal file
87
jdkSrc/jdk8/sun/nio/fs/RegistryFileTypeDetector.java
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.io.IOException;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/**
|
||||
* File type detector that does lookup of file extension using Windows Registry.
|
||||
*/
|
||||
|
||||
public class RegistryFileTypeDetector
|
||||
extends AbstractFileTypeDetector
|
||||
{
|
||||
public RegistryFileTypeDetector() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String implProbeContentType(Path file) throws IOException {
|
||||
if (!(file instanceof Path))
|
||||
return null;
|
||||
|
||||
// get file extension
|
||||
Path name = file.getFileName();
|
||||
if (name == null)
|
||||
return null;
|
||||
String filename = name.toString();
|
||||
int dot = filename.lastIndexOf('.');
|
||||
if ((dot < 0) || (dot == (filename.length()-1)))
|
||||
return null;
|
||||
|
||||
// query HKEY_CLASSES_ROOT\<ext>
|
||||
String key = filename.substring(dot);
|
||||
NativeBuffer keyBuffer = null;
|
||||
NativeBuffer nameBuffer = null;
|
||||
try {
|
||||
keyBuffer = WindowsNativeDispatcher.asNativeBuffer(key);
|
||||
nameBuffer = WindowsNativeDispatcher.asNativeBuffer("Content Type");
|
||||
return queryStringValue(keyBuffer.address(), nameBuffer.address());
|
||||
} catch (WindowsException we) {
|
||||
we.rethrowAsIOException(file.toString());
|
||||
return null; // keep compiler happy
|
||||
} finally {
|
||||
nameBuffer.release();
|
||||
keyBuffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static native String queryStringValue(long subKey, long name);
|
||||
|
||||
static {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
// nio.dll has dependency on net.dll
|
||||
System.loadLibrary("net");
|
||||
System.loadLibrary("nio");
|
||||
return null;
|
||||
}});
|
||||
}
|
||||
}
|
||||
133
jdkSrc/jdk8/sun/nio/fs/Util.java
Normal file
133
jdkSrc/jdk8/sun/nio/fs/Util.java
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.util.*;
|
||||
import java.nio.file.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.*;
|
||||
import sun.security.action.*;
|
||||
|
||||
/**
|
||||
* Utility methods
|
||||
*/
|
||||
|
||||
class Util {
|
||||
private Util() { }
|
||||
|
||||
private static final Charset jnuEncoding = Charset.forName(
|
||||
AccessController.doPrivileged(new GetPropertyAction("sun.jnu.encoding")));
|
||||
|
||||
/**
|
||||
* Returns {@code Charset} corresponding to the sun.jnu.encoding property
|
||||
*/
|
||||
static Charset jnuEncoding() {
|
||||
return jnuEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given String into a sequence of bytes using the {@code Charset}
|
||||
* specified by the sun.jnu.encoding property.
|
||||
*/
|
||||
static byte[] toBytes(String s) {
|
||||
return s.getBytes(jnuEncoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new String by decoding the specified array of bytes using the
|
||||
* {@code Charset} specified by the sun.jnu.encoding property.
|
||||
*/
|
||||
static String toString(byte[] bytes) {
|
||||
return new String(bytes, jnuEncoding);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string around the given character. The array returned by this
|
||||
* method contains each substring that is terminated by the character. Use
|
||||
* for simple string spilting cases when needing to avoid loading regex.
|
||||
*/
|
||||
static String[] split(String s, char c) {
|
||||
int count = 0;
|
||||
for (int i=0; i<s.length(); i++) {
|
||||
if (s.charAt(i) == c)
|
||||
count++;
|
||||
}
|
||||
String[] result = new String[count+1];
|
||||
int n = 0;
|
||||
int last = 0;
|
||||
for (int i=0; i<s.length(); i++) {
|
||||
if (s.charAt(i) == c) {
|
||||
result[n++] = s.substring(last, i);
|
||||
last = i + 1;
|
||||
}
|
||||
}
|
||||
result[n] = s.substring(last, s.length());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Set containing the given elements.
|
||||
*/
|
||||
@SafeVarargs
|
||||
static <E> Set<E> newSet(E... elements) {
|
||||
HashSet<E> set = new HashSet<>();
|
||||
for (E e: elements) {
|
||||
set.add(e);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Set containing all the elements of the given Set plus
|
||||
* the given elements.
|
||||
*/
|
||||
@SafeVarargs
|
||||
static <E> Set<E> newSet(Set<E> other, E... elements) {
|
||||
HashSet<E> set = new HashSet<>(other);
|
||||
for (E e: elements) {
|
||||
set.add(e);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if symbolic links should be followed
|
||||
*/
|
||||
static boolean followLinks(LinkOption... options) {
|
||||
boolean followLinks = true;
|
||||
for (LinkOption option: options) {
|
||||
if (option == LinkOption.NOFOLLOW_LINKS) {
|
||||
followLinks = false;
|
||||
} else if (option == null) {
|
||||
throw new NullPointerException();
|
||||
} else {
|
||||
throw new AssertionError("Should not get here");
|
||||
}
|
||||
}
|
||||
return followLinks;
|
||||
}
|
||||
}
|
||||
226
jdkSrc/jdk8/sun/nio/fs/WindowsAclFileAttributeView.java
Normal file
226
jdkSrc/jdk8/sun/nio/fs/WindowsAclFileAttributeView.java
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.ProviderMismatchException;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
/**
|
||||
* Windows implementation of AclFileAttributeView.
|
||||
*/
|
||||
|
||||
class WindowsAclFileAttributeView
|
||||
extends AbstractAclFileAttributeView
|
||||
{
|
||||
/**
|
||||
* typedef struct _SECURITY_DESCRIPTOR {
|
||||
* BYTE Revision;
|
||||
* BYTE Sbz1;
|
||||
* SECURITY_DESCRIPTOR_CONTROL Control;
|
||||
* PSID Owner;
|
||||
* PSID Group;
|
||||
* PACL Sacl;
|
||||
* PACL Dacl;
|
||||
* } SECURITY_DESCRIPTOR;
|
||||
*/
|
||||
private static final short SIZEOF_SECURITY_DESCRIPTOR = 20;
|
||||
|
||||
private final WindowsPath file;
|
||||
private final boolean followLinks;
|
||||
|
||||
WindowsAclFileAttributeView(WindowsPath file, boolean followLinks) {
|
||||
this.file = file;
|
||||
this.followLinks = followLinks;
|
||||
}
|
||||
|
||||
// permision check
|
||||
private void checkAccess(WindowsPath file,
|
||||
boolean checkRead,
|
||||
boolean checkWrite)
|
||||
{
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
if (checkRead)
|
||||
sm.checkRead(file.getPathForPermissionCheck());
|
||||
if (checkWrite)
|
||||
sm.checkWrite(file.getPathForPermissionCheck());
|
||||
sm.checkPermission(new RuntimePermission("accessUserInformation"));
|
||||
}
|
||||
}
|
||||
|
||||
// invokes GetFileSecurity to get requested security information
|
||||
static NativeBuffer getFileSecurity(String path, int request)
|
||||
throws IOException
|
||||
{
|
||||
// invoke get to buffer size
|
||||
int size = 0;
|
||||
try {
|
||||
size = GetFileSecurity(path, request, 0L, 0);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(path);
|
||||
}
|
||||
assert size > 0;
|
||||
|
||||
// allocate buffer and re-invoke to get security information
|
||||
NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
|
||||
try {
|
||||
for (;;) {
|
||||
int newSize = GetFileSecurity(path, request, buffer.address(), size);
|
||||
if (newSize <= size)
|
||||
return buffer;
|
||||
|
||||
// buffer was insufficient
|
||||
buffer.release();
|
||||
buffer = NativeBuffers.getNativeBuffer(newSize);
|
||||
size = newSize;
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
buffer.release();
|
||||
x.rethrowAsIOException(path);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserPrincipal getOwner()
|
||||
throws IOException
|
||||
{
|
||||
checkAccess(file, true, false);
|
||||
|
||||
// GetFileSecurity does not follow links so when following links we
|
||||
// need the final target
|
||||
String path = WindowsLinkSupport.getFinalPath(file, followLinks);
|
||||
NativeBuffer buffer = getFileSecurity(path, OWNER_SECURITY_INFORMATION);
|
||||
try {
|
||||
// get the address of the SID
|
||||
long sidAddress = GetSecurityDescriptorOwner(buffer.address());
|
||||
if (sidAddress == 0L)
|
||||
throw new IOException("no owner");
|
||||
return WindowsUserPrincipals.fromSid(sidAddress);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
return null;
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AclEntry> getAcl()
|
||||
throws IOException
|
||||
{
|
||||
checkAccess(file, true, false);
|
||||
|
||||
// GetFileSecurity does not follow links so when following links we
|
||||
// need the final target
|
||||
String path = WindowsLinkSupport.getFinalPath(file, followLinks);
|
||||
|
||||
// ALLOW and DENY entries in DACL;
|
||||
// AUDIT entries in SACL (ignore for now as it requires privileges)
|
||||
NativeBuffer buffer = getFileSecurity(path, DACL_SECURITY_INFORMATION);
|
||||
try {
|
||||
return WindowsSecurityDescriptor.getAcl(buffer.address());
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwner(UserPrincipal obj)
|
||||
throws IOException
|
||||
{
|
||||
if (obj == null)
|
||||
throw new NullPointerException("'owner' is null");
|
||||
if (!(obj instanceof WindowsUserPrincipals.User))
|
||||
throw new ProviderMismatchException();
|
||||
WindowsUserPrincipals.User owner = (WindowsUserPrincipals.User)obj;
|
||||
|
||||
// permission check
|
||||
checkAccess(file, false, true);
|
||||
|
||||
// SetFileSecurity does not follow links so when following links we
|
||||
// need the final target
|
||||
String path = WindowsLinkSupport.getFinalPath(file, followLinks);
|
||||
|
||||
// ConvertStringSidToSid allocates memory for SID so must invoke
|
||||
// LocalFree to free it when we are done
|
||||
long pOwner = 0L;
|
||||
try {
|
||||
pOwner = ConvertStringSidToSid(owner.sidString());
|
||||
} catch (WindowsException x) {
|
||||
throw new IOException("Failed to get SID for " + owner.getName()
|
||||
+ ": " + x.errorString());
|
||||
}
|
||||
|
||||
// Allocate buffer for security descriptor, initialize it, set
|
||||
// owner information and update the file.
|
||||
try {
|
||||
NativeBuffer buffer = NativeBuffers.getNativeBuffer(SIZEOF_SECURITY_DESCRIPTOR);
|
||||
try {
|
||||
InitializeSecurityDescriptor(buffer.address());
|
||||
SetSecurityDescriptorOwner(buffer.address(), pOwner);
|
||||
// may need SeRestorePrivilege to set the owner
|
||||
WindowsSecurity.Privilege priv =
|
||||
WindowsSecurity.enablePrivilege("SeRestorePrivilege");
|
||||
try {
|
||||
SetFileSecurity(path,
|
||||
OWNER_SECURITY_INFORMATION,
|
||||
buffer.address());
|
||||
} finally {
|
||||
priv.drop();
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
} finally {
|
||||
LocalFree(pOwner);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAcl(List<AclEntry> acl) throws IOException {
|
||||
checkAccess(file, false, true);
|
||||
|
||||
// SetFileSecurity does not follow links so when following links we
|
||||
// need the final target
|
||||
String path = WindowsLinkSupport.getFinalPath(file, followLinks);
|
||||
WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.create(acl);
|
||||
try {
|
||||
SetFileSecurity(path, DACL_SECURITY_INFORMATION, sd.address());
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
} finally {
|
||||
sd.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
344
jdkSrc/jdk8/sun/nio/fs/WindowsChannelFactory.java
Normal file
344
jdkSrc/jdk8/sun/nio/fs/WindowsChannelFactory.java
Normal file
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.AsynchronousFileChannel;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sun.nio.file.ExtendedOpenOption;
|
||||
|
||||
import sun.misc.JavaIOFileDescriptorAccess;
|
||||
import sun.misc.SharedSecrets;
|
||||
import sun.nio.ch.FileChannelImpl;
|
||||
import sun.nio.ch.ThreadPool;
|
||||
import sun.nio.ch.WindowsAsynchronousFileChannelImpl;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
/**
|
||||
* Factory to create FileChannels and AsynchronousFileChannels.
|
||||
*/
|
||||
|
||||
class WindowsChannelFactory {
|
||||
private static final JavaIOFileDescriptorAccess fdAccess =
|
||||
SharedSecrets.getJavaIOFileDescriptorAccess();
|
||||
|
||||
private WindowsChannelFactory() { }
|
||||
|
||||
/**
|
||||
* Do not follow reparse points when opening an existing file. Do not fail
|
||||
* if the file is a reparse point.
|
||||
*/
|
||||
static final OpenOption OPEN_REPARSE_POINT = new OpenOption() { };
|
||||
|
||||
/**
|
||||
* Represents the flags from a user-supplied set of open options.
|
||||
*/
|
||||
private static class Flags {
|
||||
boolean read;
|
||||
boolean write;
|
||||
boolean append;
|
||||
boolean truncateExisting;
|
||||
boolean create;
|
||||
boolean createNew;
|
||||
boolean deleteOnClose;
|
||||
boolean sparse;
|
||||
boolean overlapped;
|
||||
boolean sync;
|
||||
boolean dsync;
|
||||
|
||||
// non-standard
|
||||
boolean shareRead = true;
|
||||
boolean shareWrite = true;
|
||||
boolean shareDelete = true;
|
||||
boolean noFollowLinks;
|
||||
boolean openReparsePoint;
|
||||
|
||||
static Flags toFlags(Set<? extends OpenOption> options) {
|
||||
Flags flags = new Flags();
|
||||
for (OpenOption option: options) {
|
||||
if (option instanceof StandardOpenOption) {
|
||||
switch ((StandardOpenOption)option) {
|
||||
case READ : flags.read = true; break;
|
||||
case WRITE : flags.write = true; break;
|
||||
case APPEND : flags.append = true; break;
|
||||
case TRUNCATE_EXISTING : flags.truncateExisting = true; break;
|
||||
case CREATE : flags.create = true; break;
|
||||
case CREATE_NEW : flags.createNew = true; break;
|
||||
case DELETE_ON_CLOSE : flags.deleteOnClose = true; break;
|
||||
case SPARSE : flags.sparse = true; break;
|
||||
case SYNC : flags.sync = true; break;
|
||||
case DSYNC : flags.dsync = true; break;
|
||||
default: throw new UnsupportedOperationException();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (option instanceof ExtendedOpenOption) {
|
||||
switch ((ExtendedOpenOption)option) {
|
||||
case NOSHARE_READ : flags.shareRead = false; break;
|
||||
case NOSHARE_WRITE : flags.shareWrite = false; break;
|
||||
case NOSHARE_DELETE : flags.shareDelete = false; break;
|
||||
default: throw new UnsupportedOperationException();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (option == LinkOption.NOFOLLOW_LINKS) {
|
||||
flags.noFollowLinks = true;
|
||||
continue;
|
||||
}
|
||||
if (option == OPEN_REPARSE_POINT) {
|
||||
flags.openReparsePoint = true;
|
||||
continue;
|
||||
}
|
||||
if (option == null)
|
||||
throw new NullPointerException();
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open/creates file, returning FileChannel to access the file
|
||||
*
|
||||
* @param pathForWindows
|
||||
* The path of the file to open/create
|
||||
* @param pathToCheck
|
||||
* The path used for permission checks (if security manager)
|
||||
*/
|
||||
static FileChannel newFileChannel(String pathForWindows,
|
||||
String pathToCheck,
|
||||
Set<? extends OpenOption> options,
|
||||
long pSecurityDescriptor)
|
||||
throws WindowsException
|
||||
{
|
||||
Flags flags = Flags.toFlags(options);
|
||||
|
||||
// default is reading; append => writing
|
||||
if (!flags.read && !flags.write) {
|
||||
if (flags.append) {
|
||||
flags.write = true;
|
||||
} else {
|
||||
flags.read = true;
|
||||
}
|
||||
}
|
||||
|
||||
// validation
|
||||
if (flags.read && flags.append)
|
||||
throw new IllegalArgumentException("READ + APPEND not allowed");
|
||||
if (flags.append && flags.truncateExisting)
|
||||
throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
|
||||
|
||||
FileDescriptor fdObj = open(pathForWindows, pathToCheck, flags, pSecurityDescriptor);
|
||||
return FileChannelImpl.open(fdObj, pathForWindows, flags.read, flags.write, flags.append, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open/creates file, returning AsynchronousFileChannel to access the file
|
||||
*
|
||||
* @param pathForWindows
|
||||
* The path of the file to open/create
|
||||
* @param pathToCheck
|
||||
* The path used for permission checks (if security manager)
|
||||
* @param pool
|
||||
* The thread pool that the channel is associated with
|
||||
*/
|
||||
static AsynchronousFileChannel newAsynchronousFileChannel(String pathForWindows,
|
||||
String pathToCheck,
|
||||
Set<? extends OpenOption> options,
|
||||
long pSecurityDescriptor,
|
||||
ThreadPool pool)
|
||||
throws IOException
|
||||
{
|
||||
Flags flags = Flags.toFlags(options);
|
||||
|
||||
// Overlapped I/O required
|
||||
flags.overlapped = true;
|
||||
|
||||
// default is reading
|
||||
if (!flags.read && !flags.write) {
|
||||
flags.read = true;
|
||||
}
|
||||
|
||||
// validation
|
||||
if (flags.append)
|
||||
throw new UnsupportedOperationException("APPEND not allowed");
|
||||
|
||||
// open file for overlapped I/O
|
||||
FileDescriptor fdObj;
|
||||
try {
|
||||
fdObj = open(pathForWindows, pathToCheck, flags, pSecurityDescriptor);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(pathForWindows);
|
||||
return null;
|
||||
}
|
||||
|
||||
// create the AsynchronousFileChannel
|
||||
try {
|
||||
return WindowsAsynchronousFileChannelImpl.open(fdObj, flags.read, flags.write, pool);
|
||||
} catch (IOException x) {
|
||||
// IOException is thrown if the file handle cannot be associated
|
||||
// with the completion port. All we can do is close the file.
|
||||
long handle = fdAccess.getHandle(fdObj);
|
||||
CloseHandle(handle);
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
201
jdkSrc/jdk8/sun/nio/fs/WindowsConstants.java
Normal file
201
jdkSrc/jdk8/sun/nio/fs/WindowsConstants.java
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
/**
|
||||
* Win32 APIs constants.
|
||||
*/
|
||||
|
||||
class WindowsConstants {
|
||||
private WindowsConstants() { }
|
||||
|
||||
// general
|
||||
public static final long INVALID_HANDLE_VALUE = -1L;
|
||||
|
||||
// generic rights
|
||||
public static final int GENERIC_READ = 0x80000000;
|
||||
public static final int GENERIC_WRITE = 0x40000000;
|
||||
|
||||
// share modes
|
||||
public static final int FILE_SHARE_READ = 0x00000001;
|
||||
public static final int FILE_SHARE_WRITE = 0x00000002;
|
||||
public static final int FILE_SHARE_DELETE = 0x00000004;
|
||||
|
||||
// creation modes
|
||||
public static final int CREATE_NEW = 1;
|
||||
public static final int CREATE_ALWAYS = 2;
|
||||
public static final int OPEN_EXISTING = 3;
|
||||
public static final int OPEN_ALWAYS = 4;
|
||||
public static final int TRUNCATE_EXISTING = 5;
|
||||
|
||||
// attributes and flags
|
||||
public static final int FILE_ATTRIBUTE_READONLY = 0x00000001;
|
||||
public static final int FILE_ATTRIBUTE_HIDDEN = 0x00000002;
|
||||
public static final int FILE_ATTRIBUTE_SYSTEM = 0x00000004;
|
||||
public static final int FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
|
||||
public static final int FILE_ATTRIBUTE_ARCHIVE = 0x00000020;
|
||||
public static final int FILE_ATTRIBUTE_DEVICE = 0x00000040;
|
||||
public static final int FILE_ATTRIBUTE_NORMAL = 0x00000080;
|
||||
public static final int FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
|
||||
public static final int FILE_FLAG_NO_BUFFERING = 0x20000000;
|
||||
public static final int FILE_FLAG_OVERLAPPED = 0x40000000;
|
||||
public static final int FILE_FLAG_WRITE_THROUGH = 0x80000000;
|
||||
public static final int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
|
||||
public static final int FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
|
||||
public static final int FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
|
||||
|
||||
// stream ids
|
||||
public static final int BACKUP_ALTERNATE_DATA = 0x00000004;
|
||||
public static final int BACKUP_SPARSE_BLOCK = 0x00000009;
|
||||
|
||||
// reparse point/symbolic link related constants
|
||||
public static final int IO_REPARSE_TAG_SYMLINK = 0xA000000C;
|
||||
public static final int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024;
|
||||
public static final int SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1;
|
||||
|
||||
// volume flags
|
||||
public static final int FILE_CASE_SENSITIVE_SEARCH = 0x00000001;
|
||||
public static final int FILE_CASE_PRESERVED_NAMES = 0x00000002;
|
||||
public static final int FILE_PERSISTENT_ACLS = 0x00000008;
|
||||
public static final int FILE_VOLUME_IS_COMPRESSED = 0x00008000;
|
||||
public static final int FILE_NAMED_STREAMS = 0x00040000;
|
||||
public static final int FILE_READ_ONLY_VOLUME = 0x00080000;
|
||||
|
||||
// error codes
|
||||
public static final int ERROR_FILE_NOT_FOUND = 2;
|
||||
public static final int ERROR_PATH_NOT_FOUND = 3;
|
||||
public static final int ERROR_ACCESS_DENIED = 5;
|
||||
public static final int ERROR_INVALID_HANDLE = 6;
|
||||
public static final int ERROR_INVALID_DATA = 13;
|
||||
public static final int ERROR_NOT_SAME_DEVICE = 17;
|
||||
public static final int ERROR_NOT_READY = 21;
|
||||
public static final int ERROR_SHARING_VIOLATION = 32;
|
||||
public static final int ERROR_FILE_EXISTS = 80;
|
||||
public static final int ERROR_INVALID_PARAMATER = 87;
|
||||
public static final int ERROR_DISK_FULL = 112;
|
||||
public static final int ERROR_INSUFFICIENT_BUFFER = 122;
|
||||
public static final int ERROR_INVALID_LEVEL = 124;
|
||||
public static final int ERROR_DIR_NOT_ROOT = 144;
|
||||
public static final int ERROR_DIR_NOT_EMPTY = 145;
|
||||
public static final int ERROR_ALREADY_EXISTS = 183;
|
||||
public static final int ERROR_MORE_DATA = 234;
|
||||
public static final int ERROR_DIRECTORY = 267;
|
||||
public static final int ERROR_NOTIFY_ENUM_DIR = 1022;
|
||||
public static final int ERROR_NONE_MAPPED = 1332;
|
||||
public static final int ERROR_NOT_A_REPARSE_POINT = 4390;
|
||||
public static final int ERROR_INVALID_REPARSE_DATA = 4392;
|
||||
|
||||
// notify filters
|
||||
public static final int FILE_NOTIFY_CHANGE_FILE_NAME = 0x00000001;
|
||||
public static final int FILE_NOTIFY_CHANGE_DIR_NAME = 0x00000002;
|
||||
public static final int FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x00000004;
|
||||
public static final int FILE_NOTIFY_CHANGE_SIZE = 0x00000008;
|
||||
public static final int FILE_NOTIFY_CHANGE_LAST_WRITE = 0x00000010;
|
||||
public static final int FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x00000020;
|
||||
public static final int FILE_NOTIFY_CHANGE_CREATION = 0x00000040;
|
||||
public static final int FILE_NOTIFY_CHANGE_SECURITY = 0x00000100;
|
||||
|
||||
// notify actions
|
||||
public final static int FILE_ACTION_ADDED = 0x00000001;
|
||||
public final static int FILE_ACTION_REMOVED = 0x00000002;
|
||||
public final static int FILE_ACTION_MODIFIED = 0x00000003;
|
||||
public final static int FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
|
||||
public final static int FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
|
||||
|
||||
// copy flags
|
||||
public static final int COPY_FILE_FAIL_IF_EXISTS = 0x00000001;
|
||||
public static final int COPY_FILE_COPY_SYMLINK = 0x00000800;
|
||||
|
||||
// move flags
|
||||
public static final int MOVEFILE_REPLACE_EXISTING = 0x00000001;
|
||||
public static final int MOVEFILE_COPY_ALLOWED = 0x00000002;
|
||||
|
||||
// drive types
|
||||
public static final int DRIVE_UNKNOWN = 0;
|
||||
public static final int DRIVE_NO_ROOT_DIR = 1;
|
||||
public static final int DRIVE_REMOVABLE = 2;
|
||||
public static final int DRIVE_FIXED = 3;
|
||||
public static final int DRIVE_REMOTE = 4;
|
||||
public static final int DRIVE_CDROM = 5;
|
||||
public static final int DRIVE_RAMDISK = 6;
|
||||
|
||||
// file security
|
||||
public static final int OWNER_SECURITY_INFORMATION = 0x00000001;
|
||||
public static final int GROUP_SECURITY_INFORMATION = 0x00000002;
|
||||
public static final int DACL_SECURITY_INFORMATION = 0x00000004;
|
||||
public static final int SACL_SECURITY_INFORMATION = 0x00000008;
|
||||
|
||||
public static final int SidTypeUser = 1;
|
||||
public static final int SidTypeGroup = 2;
|
||||
public static final int SidTypeDomain = 3;
|
||||
public static final int SidTypeAlias = 4;
|
||||
public static final int SidTypeWellKnownGroup = 5;
|
||||
public static final int SidTypeDeletedAccount = 6;
|
||||
public static final int SidTypeInvalid = 7;
|
||||
public static final int SidTypeUnknown = 8;
|
||||
public static final int SidTypeComputer= 9;
|
||||
|
||||
public static final byte ACCESS_ALLOWED_ACE_TYPE = 0x0;
|
||||
public static final byte ACCESS_DENIED_ACE_TYPE = 0x1;
|
||||
|
||||
public static final byte OBJECT_INHERIT_ACE = 0x1;
|
||||
public static final byte CONTAINER_INHERIT_ACE = 0x2;
|
||||
public static final byte NO_PROPAGATE_INHERIT_ACE = 0x4;
|
||||
public static final byte INHERIT_ONLY_ACE = 0x8;
|
||||
|
||||
public static final int DELETE = 0x00010000;
|
||||
public static final int READ_CONTROL = 0x00020000;
|
||||
public static final int WRITE_DAC = 0x00040000;
|
||||
public static final int WRITE_OWNER = 0x00080000;
|
||||
public static final int SYNCHRONIZE = 0x00100000;
|
||||
|
||||
public static final int FILE_LIST_DIRECTORY = 0x0001;
|
||||
public static final int FILE_READ_DATA = 0x0001;
|
||||
public static final int FILE_WRITE_DATA = 0x0002;
|
||||
public static final int FILE_APPEND_DATA = 0x0004;
|
||||
public static final int FILE_READ_EA = 0x0008;
|
||||
public static final int FILE_WRITE_EA = 0x0010;
|
||||
public static final int FILE_EXECUTE = 0x0020;
|
||||
public static final int FILE_DELETE_CHILD = 0x0040;
|
||||
public static final int FILE_READ_ATTRIBUTES = 0x0080;
|
||||
public static final int FILE_WRITE_ATTRIBUTES = 0x0100;
|
||||
|
||||
public static final int FILE_GENERIC_READ = 0x00120089;
|
||||
public static final int FILE_GENERIC_WRITE = 0x00120116;
|
||||
public static final int FILE_GENERIC_EXECUTE = 0x001200a0;
|
||||
public static final int FILE_ALL_ACCESS = 0x001f01ff;
|
||||
|
||||
// operating system security
|
||||
public static final int TOKEN_DUPLICATE = 0x0002;
|
||||
public static final int TOKEN_IMPERSONATE = 0x0004;
|
||||
public static final int TOKEN_QUERY = 0x0008;
|
||||
public static final int TOKEN_ADJUST_PRIVILEGES = 0x0020;
|
||||
|
||||
public static final int SE_PRIVILEGE_ENABLED = 0x00000002;
|
||||
|
||||
public static final int TokenUser = 1;
|
||||
public static final int PROCESS_QUERY_INFORMATION = 0x0400;
|
||||
}
|
||||
232
jdkSrc/jdk8/sun/nio/fs/WindowsDirectoryStream.java
Normal file
232
jdkSrc/jdk8/sun/nio/fs/WindowsDirectoryStream.java
Normal file
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.io.IOException;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
/**
|
||||
* Windows implementation of DirectoryStream
|
||||
*/
|
||||
|
||||
class WindowsDirectoryStream
|
||||
implements DirectoryStream<Path>
|
||||
{
|
||||
private final WindowsPath dir;
|
||||
private final DirectoryStream.Filter<? super Path> filter;
|
||||
|
||||
// handle to directory
|
||||
private final long handle;
|
||||
// first entry in the directory
|
||||
private final String firstName;
|
||||
|
||||
// buffer for WIN32_FIND_DATA structure that receives information about file
|
||||
private final NativeBuffer findDataBuffer;
|
||||
|
||||
private final Object closeLock = new Object();
|
||||
|
||||
// need closeLock to access these
|
||||
private boolean isOpen = true;
|
||||
private Iterator<Path> iterator;
|
||||
|
||||
|
||||
WindowsDirectoryStream(WindowsPath dir, DirectoryStream.Filter<? super Path> filter)
|
||||
throws IOException
|
||||
{
|
||||
this.dir = dir;
|
||||
this.filter = filter;
|
||||
|
||||
try {
|
||||
// Need to append * or \* to match entries in directory.
|
||||
String search = dir.getPathForWin32Calls();
|
||||
char last = search.charAt(search.length() -1);
|
||||
if (last == ':' || last == '\\') {
|
||||
search += "*";
|
||||
} else {
|
||||
search += "\\*";
|
||||
}
|
||||
|
||||
FirstFile first = FindFirstFile(search);
|
||||
this.handle = first.handle();
|
||||
this.firstName = first.name();
|
||||
this.findDataBuffer = WindowsFileAttributes.getBufferForFindData();
|
||||
} catch (WindowsException x) {
|
||||
if (x.lastError() == ERROR_DIRECTORY) {
|
||||
throw new NotDirectoryException(dir.getPathForExceptionMessage());
|
||||
}
|
||||
x.rethrowAsIOException(dir);
|
||||
|
||||
// keep compiler happy
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
throws IOException
|
||||
{
|
||||
synchronized (closeLock) {
|
||||
if (!isOpen)
|
||||
return;
|
||||
isOpen = false;
|
||||
}
|
||||
findDataBuffer.release();
|
||||
try {
|
||||
FindClose(handle);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(dir);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Path> iterator() {
|
||||
if (!isOpen) {
|
||||
throw new IllegalStateException("Directory stream is closed");
|
||||
}
|
||||
synchronized (this) {
|
||||
if (iterator != null)
|
||||
throw new IllegalStateException("Iterator already obtained");
|
||||
iterator = new WindowsDirectoryIterator(firstName);
|
||||
return iterator;
|
||||
}
|
||||
}
|
||||
|
||||
private class WindowsDirectoryIterator implements Iterator<Path> {
|
||||
private boolean atEof;
|
||||
private String first;
|
||||
private Path nextEntry;
|
||||
private String prefix;
|
||||
|
||||
WindowsDirectoryIterator(String first) {
|
||||
atEof = false;
|
||||
this.first = first;
|
||||
if (dir.needsSlashWhenResolving()) {
|
||||
prefix = dir.toString() + "\\";
|
||||
} else {
|
||||
prefix = dir.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// links to self and parent directories are ignored
|
||||
private boolean isSelfOrParent(String name) {
|
||||
return name.equals(".") || name.equals("..");
|
||||
}
|
||||
|
||||
// applies filter and also ignores "." and ".."
|
||||
private Path acceptEntry(String s, BasicFileAttributes attrs) {
|
||||
Path entry = WindowsPath
|
||||
.createFromNormalizedPath(dir.getFileSystem(), prefix + s, attrs);
|
||||
try {
|
||||
if (filter.accept(entry))
|
||||
return entry;
|
||||
} catch (IOException ioe) {
|
||||
throw new DirectoryIteratorException(ioe);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// reads next directory entry
|
||||
private Path readNextEntry() {
|
||||
// handle first element returned by search
|
||||
if (first != null) {
|
||||
nextEntry = isSelfOrParent(first) ? null : acceptEntry(first, null);
|
||||
first = null;
|
||||
if (nextEntry != null)
|
||||
return nextEntry;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
String name = null;
|
||||
WindowsFileAttributes attrs;
|
||||
|
||||
// synchronize on closeLock to prevent close while reading
|
||||
synchronized (closeLock) {
|
||||
try {
|
||||
if (isOpen) {
|
||||
name = FindNextFile(handle, findDataBuffer.address());
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
IOException ioe = x.asIOException(dir);
|
||||
throw new DirectoryIteratorException(ioe);
|
||||
}
|
||||
|
||||
// NO_MORE_FILES or stream closed
|
||||
if (name == null) {
|
||||
atEof = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
// ignore link to self and parent directories
|
||||
if (isSelfOrParent(name))
|
||||
continue;
|
||||
|
||||
// grab the attributes from the WIN32_FIND_DATA structure
|
||||
// (needs to be done while holding closeLock because close
|
||||
// will release the buffer)
|
||||
attrs = WindowsFileAttributes
|
||||
.fromFindData(findDataBuffer.address());
|
||||
}
|
||||
|
||||
// return entry if accepted by filter
|
||||
Path entry = acceptEntry(name, attrs);
|
||||
if (entry != null)
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean hasNext() {
|
||||
if (nextEntry == null && !atEof)
|
||||
nextEntry = readNextEntry();
|
||||
return nextEntry != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Path next() {
|
||||
Path result = null;
|
||||
if (nextEntry == null && !atEof) {
|
||||
result = readNextEntry();
|
||||
} else {
|
||||
result = nextEntry;
|
||||
nextEntry = null;
|
||||
}
|
||||
if (result == null)
|
||||
throw new NoSuchElementException();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
109
jdkSrc/jdk8/sun/nio/fs/WindowsException.java
Normal file
109
jdkSrc/jdk8/sun/nio/fs/WindowsException.java
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
/**
|
||||
* Internal exception thrown when a Win32 calls fails.
|
||||
*/
|
||||
|
||||
class WindowsException extends Exception {
|
||||
static final long serialVersionUID = 2765039493083748820L;
|
||||
|
||||
private int lastError;
|
||||
private String msg;
|
||||
|
||||
WindowsException(int lastError) {
|
||||
this.lastError = lastError;
|
||||
this.msg = null;
|
||||
}
|
||||
|
||||
WindowsException(String msg) {
|
||||
this.lastError = 0;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
int lastError() {
|
||||
return lastError;
|
||||
}
|
||||
|
||||
String errorString() {
|
||||
if (msg == null) {
|
||||
msg = WindowsNativeDispatcher.FormatMessage(lastError);
|
||||
if (msg == null) {
|
||||
msg = "Unknown error: 0x" + Integer.toHexString(lastError);
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return errorString();
|
||||
}
|
||||
|
||||
private IOException translateToIOException(String file, String other) {
|
||||
// not created with last error
|
||||
if (lastError() == 0)
|
||||
return new IOException(errorString());
|
||||
|
||||
// handle specific cases
|
||||
if (lastError() == ERROR_FILE_NOT_FOUND || lastError() == ERROR_PATH_NOT_FOUND)
|
||||
return new NoSuchFileException(file, other, null);
|
||||
if (lastError() == ERROR_FILE_EXISTS || lastError() == ERROR_ALREADY_EXISTS)
|
||||
return new FileAlreadyExistsException(file, other, null);
|
||||
if (lastError() == ERROR_ACCESS_DENIED)
|
||||
return new AccessDeniedException(file, other, null);
|
||||
|
||||
// fallback to the more general exception
|
||||
return new FileSystemException(file, other, errorString());
|
||||
}
|
||||
|
||||
void rethrowAsIOException(String file) throws IOException {
|
||||
IOException x = translateToIOException(file, null);
|
||||
throw x;
|
||||
}
|
||||
|
||||
void rethrowAsIOException(WindowsPath file, WindowsPath other) throws IOException {
|
||||
String a = (file == null) ? null : file.getPathForExceptionMessage();
|
||||
String b = (other == null) ? null : other.getPathForExceptionMessage();
|
||||
IOException x = translateToIOException(a, b);
|
||||
throw x;
|
||||
}
|
||||
|
||||
void rethrowAsIOException(WindowsPath file) throws IOException {
|
||||
rethrowAsIOException(file, null);
|
||||
}
|
||||
|
||||
IOException asIOException(WindowsPath file) {
|
||||
return translateToIOException(file.getPathForExceptionMessage(), null);
|
||||
}
|
||||
|
||||
}
|
||||
295
jdkSrc/jdk8/sun/nio/fs/WindowsFileAttributeViews.java
Normal file
295
jdkSrc/jdk8/sun/nio/fs/WindowsFileAttributeViews.java
Normal file
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
|
||||
import java.nio.file.attribute.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
class WindowsFileAttributeViews {
|
||||
|
||||
private static class Basic extends AbstractBasicFileAttributeView {
|
||||
final WindowsPath file;
|
||||
final boolean followLinks;
|
||||
|
||||
Basic(WindowsPath file, boolean followLinks) {
|
||||
this.file = file;
|
||||
this.followLinks = followLinks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowsFileAttributes readAttributes() throws IOException {
|
||||
file.checkRead();
|
||||
try {
|
||||
return WindowsFileAttributes.get(file, followLinks);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
return null; // keep compiler happy
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts a Windows time for the FAT epoch.
|
||||
*/
|
||||
private long adjustForFatEpoch(long time) {
|
||||
// 1/1/1980 in Windows Time
|
||||
final long FAT_EPOCH = 119600064000000000L;
|
||||
if (time != -1L && time < FAT_EPOCH) {
|
||||
return FAT_EPOCH;
|
||||
} else {
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameter values in Windows times.
|
||||
*/
|
||||
void setFileTimes(long createTime,
|
||||
long lastAccessTime,
|
||||
long lastWriteTime)
|
||||
throws IOException
|
||||
{
|
||||
long handle = -1L;
|
||||
try {
|
||||
int flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||||
if (!followLinks && file.getFileSystem().supportsLinks())
|
||||
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
|
||||
|
||||
handle = CreateFile(file.getPathForWin32Calls(),
|
||||
FILE_WRITE_ATTRIBUTES,
|
||||
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
|
||||
OPEN_EXISTING,
|
||||
flags);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
}
|
||||
|
||||
// update times
|
||||
try {
|
||||
SetFileTime(handle,
|
||||
createTime,
|
||||
lastAccessTime,
|
||||
lastWriteTime);
|
||||
} catch (WindowsException x) {
|
||||
// If ERROR_INVALID_PARAMATER is returned and the volume is
|
||||
// FAT then adjust to the FAT epoch and retry.
|
||||
if (followLinks && x.lastError() == ERROR_INVALID_PARAMATER) {
|
||||
try {
|
||||
if (WindowsFileStore.create(file).type().equals("FAT")) {
|
||||
SetFileTime(handle,
|
||||
adjustForFatEpoch(createTime),
|
||||
adjustForFatEpoch(lastAccessTime),
|
||||
adjustForFatEpoch(lastWriteTime));
|
||||
// retry succeeded
|
||||
x = null;
|
||||
}
|
||||
} catch (SecurityException ignore) {
|
||||
} catch (WindowsException ignore) {
|
||||
} catch (IOException ignore) {
|
||||
// ignore exceptions to let original exception be thrown
|
||||
}
|
||||
}
|
||||
if (x != null)
|
||||
x.rethrowAsIOException(file);
|
||||
} finally {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimes(FileTime lastModifiedTime,
|
||||
FileTime lastAccessTime,
|
||||
FileTime createTime) throws IOException
|
||||
{
|
||||
// if all null then do nothing
|
||||
if (lastModifiedTime == null && lastAccessTime == null &&
|
||||
createTime == null)
|
||||
{
|
||||
// no effect
|
||||
return;
|
||||
}
|
||||
|
||||
// permission check
|
||||
file.checkWrite();
|
||||
|
||||
// update times
|
||||
long t1 = (createTime == null) ? -1L :
|
||||
WindowsFileAttributes.toWindowsTime(createTime);
|
||||
long t2 = (lastAccessTime == null) ? -1L :
|
||||
WindowsFileAttributes.toWindowsTime(lastAccessTime);
|
||||
long t3 = (lastModifiedTime == null) ? -1L :
|
||||
WindowsFileAttributes.toWindowsTime(lastModifiedTime);
|
||||
setFileTimes(t1, t2, t3);
|
||||
}
|
||||
}
|
||||
|
||||
static class Dos extends Basic implements DosFileAttributeView {
|
||||
private static final String READONLY_NAME = "readonly";
|
||||
private static final String ARCHIVE_NAME = "archive";
|
||||
private static final String SYSTEM_NAME = "system";
|
||||
private static final String HIDDEN_NAME = "hidden";
|
||||
private static final String ATTRIBUTES_NAME = "attributes";
|
||||
|
||||
// the names of the DOS attribtues (includes basic)
|
||||
static final Set<String> dosAttributeNames =
|
||||
Util.newSet(basicAttributeNames,
|
||||
READONLY_NAME, ARCHIVE_NAME, SYSTEM_NAME, HIDDEN_NAME, ATTRIBUTES_NAME);
|
||||
|
||||
Dos(WindowsPath file, boolean followLinks) {
|
||||
super(file, followLinks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "dos";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String attribute, Object value)
|
||||
throws IOException
|
||||
{
|
||||
if (attribute.equals(READONLY_NAME)) {
|
||||
setReadOnly((Boolean)value);
|
||||
return;
|
||||
}
|
||||
if (attribute.equals(ARCHIVE_NAME)) {
|
||||
setArchive((Boolean)value);
|
||||
return;
|
||||
}
|
||||
if (attribute.equals(SYSTEM_NAME)) {
|
||||
setSystem((Boolean)value);
|
||||
return;
|
||||
}
|
||||
if (attribute.equals(HIDDEN_NAME)) {
|
||||
setHidden((Boolean)value);
|
||||
return;
|
||||
}
|
||||
super.setAttribute(attribute, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String,Object> readAttributes(String[] attributes)
|
||||
throws IOException
|
||||
{
|
||||
AttributesBuilder builder =
|
||||
AttributesBuilder.create(dosAttributeNames, attributes);
|
||||
WindowsFileAttributes attrs = readAttributes();
|
||||
addRequestedBasicAttributes(attrs, builder);
|
||||
if (builder.match(READONLY_NAME))
|
||||
builder.add(READONLY_NAME, attrs.isReadOnly());
|
||||
if (builder.match(ARCHIVE_NAME))
|
||||
builder.add(ARCHIVE_NAME, attrs.isArchive());
|
||||
if (builder.match(SYSTEM_NAME))
|
||||
builder.add(SYSTEM_NAME, attrs.isSystem());
|
||||
if (builder.match(HIDDEN_NAME))
|
||||
builder.add(HIDDEN_NAME, attrs.isHidden());
|
||||
if (builder.match(ATTRIBUTES_NAME))
|
||||
builder.add(ATTRIBUTES_NAME, attrs.attributes());
|
||||
return builder.unmodifiableMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update DOS attributes
|
||||
*/
|
||||
private void updateAttributes(int flag, boolean enable)
|
||||
throws IOException
|
||||
{
|
||||
file.checkWrite();
|
||||
|
||||
// GetFileAttribtues & SetFileAttributes do not follow links so when
|
||||
// following links we need the final target
|
||||
String path = WindowsLinkSupport.getFinalPath(file, followLinks);
|
||||
try {
|
||||
int oldValue = GetFileAttributes(path);
|
||||
int newValue = oldValue;
|
||||
if (enable) {
|
||||
newValue |= flag;
|
||||
} else {
|
||||
newValue &= ~flag;
|
||||
}
|
||||
if (newValue != oldValue) {
|
||||
SetFileAttributes(path, newValue);
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
// don't reveal target in exception
|
||||
x.rethrowAsIOException(file);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean value) throws IOException {
|
||||
updateAttributes(FILE_ATTRIBUTE_READONLY, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHidden(boolean value) throws IOException {
|
||||
updateAttributes(FILE_ATTRIBUTE_HIDDEN, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArchive(boolean value) throws IOException {
|
||||
updateAttributes(FILE_ATTRIBUTE_ARCHIVE, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSystem(boolean value) throws IOException {
|
||||
updateAttributes(FILE_ATTRIBUTE_SYSTEM, value);
|
||||
}
|
||||
|
||||
// package-private
|
||||
// Copy given attributes to the file.
|
||||
void setAttributes(WindowsFileAttributes attrs)
|
||||
throws IOException
|
||||
{
|
||||
// copy DOS attributes to target
|
||||
int flags = 0;
|
||||
if (attrs.isReadOnly()) flags |= FILE_ATTRIBUTE_READONLY;
|
||||
if (attrs.isHidden()) flags |= FILE_ATTRIBUTE_HIDDEN;
|
||||
if (attrs.isArchive()) flags |= FILE_ATTRIBUTE_ARCHIVE;
|
||||
if (attrs.isSystem()) flags |= FILE_ATTRIBUTE_SYSTEM;
|
||||
updateAttributes(flags, true);
|
||||
|
||||
// copy file times to target - must be done after updating FAT attributes
|
||||
// as otherwise the last modified time may be wrong.
|
||||
setFileTimes(
|
||||
WindowsFileAttributes.toWindowsTime(attrs.creationTime()),
|
||||
WindowsFileAttributes.toWindowsTime(attrs.lastModifiedTime()),
|
||||
WindowsFileAttributes.toWindowsTime(attrs.lastAccessTime()));
|
||||
}
|
||||
}
|
||||
|
||||
static Basic createBasicView(WindowsPath file, boolean followLinks) {
|
||||
return new Basic(file, followLinks);
|
||||
}
|
||||
|
||||
static Dos createDosView(WindowsPath file, boolean followLinks) {
|
||||
return new Dos(file, followLinks);
|
||||
}
|
||||
}
|
||||
476
jdkSrc/jdk8/sun/nio/fs/WindowsFileAttributes.java
Normal file
476
jdkSrc/jdk8/sun/nio/fs/WindowsFileAttributes.java
Normal file
@@ -0,0 +1,476 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.attribute.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.security.AccessController;
|
||||
import sun.misc.Unsafe;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
/**
|
||||
* Windows implementation of DosFileAttributes/BasicFileAttributes
|
||||
*/
|
||||
|
||||
class WindowsFileAttributes
|
||||
implements DosFileAttributes
|
||||
{
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
/*
|
||||
* typedef struct _BY_HANDLE_FILE_INFORMATION {
|
||||
* DWORD dwFileAttributes;
|
||||
* FILETIME ftCreationTime;
|
||||
* FILETIME ftLastAccessTime;
|
||||
* FILETIME ftLastWriteTime;
|
||||
* DWORD dwVolumeSerialNumber;
|
||||
* DWORD nFileSizeHigh;
|
||||
* DWORD nFileSizeLow;
|
||||
* DWORD nNumberOfLinks;
|
||||
* DWORD nFileIndexHigh;
|
||||
* DWORD nFileIndexLow;
|
||||
* } BY_HANDLE_FILE_INFORMATION;
|
||||
*/
|
||||
private static final short SIZEOF_FILE_INFORMATION = 52;
|
||||
private static final short OFFSETOF_FILE_INFORMATION_ATTRIBUTES = 0;
|
||||
private static final short OFFSETOF_FILE_INFORMATION_CREATETIME = 4;
|
||||
private static final short OFFSETOF_FILE_INFORMATION_LASTACCESSTIME = 12;
|
||||
private static final short OFFSETOF_FILE_INFORMATION_LASTWRITETIME = 20;
|
||||
private static final short OFFSETOF_FILE_INFORMATION_VOLSERIALNUM = 28;
|
||||
private static final short OFFSETOF_FILE_INFORMATION_SIZEHIGH = 32;
|
||||
private static final short OFFSETOF_FILE_INFORMATION_SIZELOW = 36;
|
||||
private static final short OFFSETOF_FILE_INFORMATION_INDEXHIGH = 44;
|
||||
private static final short OFFSETOF_FILE_INFORMATION_INDEXLOW = 48;
|
||||
|
||||
/*
|
||||
* typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
|
||||
* DWORD dwFileAttributes;
|
||||
* FILETIME ftCreationTime;
|
||||
* FILETIME ftLastAccessTime;
|
||||
* FILETIME ftLastWriteTime;
|
||||
* DWORD nFileSizeHigh;
|
||||
* DWORD nFileSizeLow;
|
||||
* } WIN32_FILE_ATTRIBUTE_DATA;
|
||||
*/
|
||||
private static final short SIZEOF_FILE_ATTRIBUTE_DATA = 36;
|
||||
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES = 0;
|
||||
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_CREATETIME = 4;
|
||||
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_LASTACCESSTIME = 12;
|
||||
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_LASTWRITETIME = 20;
|
||||
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH = 28;
|
||||
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW = 32;
|
||||
|
||||
/**
|
||||
* typedef struct _WIN32_FIND_DATA {
|
||||
* DWORD dwFileAttributes;
|
||||
* FILETIME ftCreationTime;
|
||||
* FILETIME ftLastAccessTime;
|
||||
* FILETIME ftLastWriteTime;
|
||||
* DWORD nFileSizeHigh;
|
||||
* DWORD nFileSizeLow;
|
||||
* DWORD dwReserved0;
|
||||
* DWORD dwReserved1;
|
||||
* TCHAR cFileName[MAX_PATH];
|
||||
* TCHAR cAlternateFileName[14];
|
||||
* } WIN32_FIND_DATA;
|
||||
*/
|
||||
private static final short SIZEOF_FIND_DATA = 592;
|
||||
private static final short OFFSETOF_FIND_DATA_ATTRIBUTES = 0;
|
||||
private static final short OFFSETOF_FIND_DATA_CREATETIME = 4;
|
||||
private static final short OFFSETOF_FIND_DATA_LASTACCESSTIME = 12;
|
||||
private static final short OFFSETOF_FIND_DATA_LASTWRITETIME = 20;
|
||||
private static final short OFFSETOF_FIND_DATA_SIZEHIGH = 28;
|
||||
private static final short OFFSETOF_FIND_DATA_SIZELOW = 32;
|
||||
private static final short OFFSETOF_FIND_DATA_RESERVED0 = 36;
|
||||
|
||||
// used to adjust values between Windows and java epoch
|
||||
private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
|
||||
|
||||
// indicates if accurate metadata is required (interesting on NTFS only)
|
||||
private static final boolean ensureAccurateMetadata;
|
||||
static {
|
||||
String propValue = AccessController.doPrivileged(
|
||||
new GetPropertyAction("sun.nio.fs.ensureAccurateMetadata", "false"));
|
||||
ensureAccurateMetadata = (propValue.length() == 0) ?
|
||||
true : Boolean.valueOf(propValue);
|
||||
}
|
||||
|
||||
// attributes
|
||||
private final int fileAttrs;
|
||||
private final long creationTime;
|
||||
private final long lastAccessTime;
|
||||
private final long lastWriteTime;
|
||||
private final long size;
|
||||
private final int reparseTag;
|
||||
|
||||
// additional attributes when using GetFileInformationByHandle
|
||||
private final int volSerialNumber;
|
||||
private final int fileIndexHigh;
|
||||
private final int fileIndexLow;
|
||||
|
||||
/**
|
||||
* Convert 64-bit value representing the number of 100-nanosecond intervals
|
||||
* since January 1, 1601 to a FileTime.
|
||||
*/
|
||||
static FileTime toFileTime(long time) {
|
||||
// 100ns -> us
|
||||
time /= 10L;
|
||||
// adjust to java epoch
|
||||
time += WINDOWS_EPOCH_IN_MICROSECONDS;
|
||||
return FileTime.from(time, TimeUnit.MICROSECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert FileTime to 64-bit value representing the number of 100-nanosecond
|
||||
* intervals since January 1, 1601.
|
||||
*/
|
||||
static long toWindowsTime(FileTime time) {
|
||||
long value = time.to(TimeUnit.MICROSECONDS);
|
||||
// adjust to Windows epoch+= 11644473600000000L;
|
||||
value -= WINDOWS_EPOCH_IN_MICROSECONDS;
|
||||
// us -> 100ns
|
||||
value *= 10L;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new instance of this class
|
||||
*/
|
||||
private WindowsFileAttributes(int fileAttrs,
|
||||
long creationTime,
|
||||
long lastAccessTime,
|
||||
long lastWriteTime,
|
||||
long size,
|
||||
int reparseTag,
|
||||
int volSerialNumber,
|
||||
int fileIndexHigh,
|
||||
int fileIndexLow)
|
||||
{
|
||||
this.fileAttrs = fileAttrs;
|
||||
this.creationTime = creationTime;
|
||||
this.lastAccessTime = lastAccessTime;
|
||||
this.lastWriteTime = lastWriteTime;
|
||||
this.size = size;
|
||||
this.reparseTag = reparseTag;
|
||||
this.volSerialNumber = volSerialNumber;
|
||||
this.fileIndexHigh = fileIndexHigh;
|
||||
this.fileIndexLow = fileIndexLow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WindowsFileAttributes from a BY_HANDLE_FILE_INFORMATION structure
|
||||
*/
|
||||
private static WindowsFileAttributes fromFileInformation(long address, int reparseTag) {
|
||||
int fileAttrs = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_ATTRIBUTES);
|
||||
long creationTime = unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_CREATETIME);
|
||||
long lastAccessTime = unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_LASTACCESSTIME);
|
||||
long lastWriteTime = unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_LASTWRITETIME);
|
||||
long size = ((long)(unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_SIZEHIGH)) << 32)
|
||||
+ (unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_SIZELOW) & 0xFFFFFFFFL);
|
||||
int volSerialNumber = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_VOLSERIALNUM);
|
||||
int fileIndexHigh = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_INDEXHIGH);
|
||||
int fileIndexLow = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_INDEXLOW);
|
||||
return new WindowsFileAttributes(fileAttrs,
|
||||
creationTime,
|
||||
lastAccessTime,
|
||||
lastWriteTime,
|
||||
size,
|
||||
reparseTag,
|
||||
volSerialNumber,
|
||||
fileIndexHigh,
|
||||
fileIndexLow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WindowsFileAttributes from a WIN32_FILE_ATTRIBUTE_DATA structure
|
||||
*/
|
||||
private static WindowsFileAttributes fromFileAttributeData(long address, int reparseTag) {
|
||||
int fileAttrs = unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES);
|
||||
long creationTime = unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_CREATETIME);
|
||||
long lastAccessTime = unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_LASTACCESSTIME);
|
||||
long lastWriteTime = unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_LASTWRITETIME);
|
||||
long size = ((long)(unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH)) << 32)
|
||||
+ (unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW) & 0xFFFFFFFFL);
|
||||
return new WindowsFileAttributes(fileAttrs,
|
||||
creationTime,
|
||||
lastAccessTime,
|
||||
lastWriteTime,
|
||||
size,
|
||||
reparseTag,
|
||||
0, // volSerialNumber
|
||||
0, // fileIndexHigh
|
||||
0); // fileIndexLow
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocates a native buffer for a WIN32_FIND_DATA structure
|
||||
*/
|
||||
static NativeBuffer getBufferForFindData() {
|
||||
return NativeBuffers.getNativeBuffer(SIZEOF_FIND_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WindowsFileAttributes from a WIN32_FIND_DATA structure
|
||||
*/
|
||||
static WindowsFileAttributes fromFindData(long address) {
|
||||
int fileAttrs = unsafe.getInt(address + OFFSETOF_FIND_DATA_ATTRIBUTES);
|
||||
long creationTime = unsafe.getLong(address + OFFSETOF_FIND_DATA_CREATETIME);
|
||||
long lastAccessTime = unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTACCESSTIME);
|
||||
long lastWriteTime = unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTWRITETIME);
|
||||
long size = ((long)(unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZEHIGH)) << 32)
|
||||
+ (unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZELOW) & 0xFFFFFFFFL);
|
||||
int reparseTag = isReparsePoint(fileAttrs) ?
|
||||
unsafe.getInt(address + OFFSETOF_FIND_DATA_RESERVED0) : 0;
|
||||
return new WindowsFileAttributes(fileAttrs,
|
||||
creationTime,
|
||||
lastAccessTime,
|
||||
lastWriteTime,
|
||||
size,
|
||||
reparseTag,
|
||||
0, // volSerialNumber
|
||||
0, // fileIndexHigh
|
||||
0); // fileIndexLow
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the attributes of an open file
|
||||
*/
|
||||
static WindowsFileAttributes readAttributes(long handle)
|
||||
throws WindowsException
|
||||
{
|
||||
NativeBuffer buffer = NativeBuffers
|
||||
.getNativeBuffer(SIZEOF_FILE_INFORMATION);
|
||||
try {
|
||||
long address = buffer.address();
|
||||
GetFileInformationByHandle(handle, address);
|
||||
|
||||
// if file is a reparse point then read the tag
|
||||
int reparseTag = 0;
|
||||
int fileAttrs = unsafe
|
||||
.getInt(address + OFFSETOF_FILE_INFORMATION_ATTRIBUTES);
|
||||
if (isReparsePoint(fileAttrs)) {
|
||||
int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
|
||||
NativeBuffer reparseBuffer = NativeBuffers.getNativeBuffer(size);
|
||||
try {
|
||||
DeviceIoControlGetReparsePoint(handle, reparseBuffer.address(), size);
|
||||
reparseTag = (int)unsafe.getLong(reparseBuffer.address());
|
||||
} finally {
|
||||
reparseBuffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
return fromFileInformation(address, reparseTag);
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns attributes of given file.
|
||||
*/
|
||||
static WindowsFileAttributes get(WindowsPath path, boolean followLinks)
|
||||
throws WindowsException
|
||||
{
|
||||
if (!ensureAccurateMetadata) {
|
||||
WindowsException firstException = null;
|
||||
|
||||
// GetFileAttributesEx is the fastest way to read the attributes
|
||||
NativeBuffer buffer =
|
||||
NativeBuffers.getNativeBuffer(SIZEOF_FILE_ATTRIBUTE_DATA);
|
||||
try {
|
||||
long address = buffer.address();
|
||||
GetFileAttributesEx(path.getPathForWin32Calls(), address);
|
||||
// if reparse point then file may be a sym link; otherwise
|
||||
// just return the attributes
|
||||
int fileAttrs = unsafe
|
||||
.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES);
|
||||
if (!isReparsePoint(fileAttrs))
|
||||
return fromFileAttributeData(address, 0);
|
||||
} catch (WindowsException x) {
|
||||
if (x.lastError() != ERROR_SHARING_VIOLATION)
|
||||
throw x;
|
||||
firstException = x;
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
|
||||
// For sharing violations, fallback to FindFirstFile if the file
|
||||
// is not a root directory.
|
||||
if (firstException != null) {
|
||||
String search = path.getPathForWin32Calls();
|
||||
char last = search.charAt(search.length() -1);
|
||||
if (last == ':' || last == '\\')
|
||||
throw firstException;
|
||||
buffer = getBufferForFindData();
|
||||
try {
|
||||
long handle = FindFirstFile(search, buffer.address());
|
||||
FindClose(handle);
|
||||
WindowsFileAttributes attrs = fromFindData(buffer.address());
|
||||
// FindFirstFile does not follow sym links. Even if
|
||||
// followLinks is false, there isn't sufficient information
|
||||
// in the WIN32_FIND_DATA structure to know if the reparse
|
||||
// point is a sym link.
|
||||
if (attrs.isReparsePoint())
|
||||
throw firstException;
|
||||
return attrs;
|
||||
} catch (WindowsException ignore) {
|
||||
throw firstException;
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// file is reparse point so need to open file to get attributes
|
||||
long handle = path.openForReadAttributeAccess(followLinks);
|
||||
try {
|
||||
return readAttributes(handle);
|
||||
} finally {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attributes are of the same file - both files must
|
||||
* be open.
|
||||
*/
|
||||
static boolean isSameFile(WindowsFileAttributes attrs1,
|
||||
WindowsFileAttributes attrs2)
|
||||
{
|
||||
// volume serial number and file index must be the same
|
||||
return (attrs1.volSerialNumber == attrs2.volSerialNumber) &&
|
||||
(attrs1.fileIndexHigh == attrs2.fileIndexHigh) &&
|
||||
(attrs1.fileIndexLow == attrs2.fileIndexLow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attributes are of a file with a reparse point.
|
||||
*/
|
||||
static boolean isReparsePoint(int attributes) {
|
||||
return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
|
||||
}
|
||||
|
||||
// package-private
|
||||
int attributes() {
|
||||
return fileAttrs;
|
||||
}
|
||||
|
||||
int volSerialNumber() {
|
||||
return volSerialNumber;
|
||||
}
|
||||
|
||||
int fileIndexHigh() {
|
||||
return fileIndexHigh;
|
||||
}
|
||||
|
||||
int fileIndexLow() {
|
||||
return fileIndexLow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTime lastModifiedTime() {
|
||||
return toFileTime(lastWriteTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTime lastAccessTime() {
|
||||
return toFileTime(lastAccessTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTime creationTime() {
|
||||
return toFileTime(creationTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fileKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// package private
|
||||
boolean isReparsePoint() {
|
||||
return isReparsePoint(fileAttrs);
|
||||
}
|
||||
|
||||
boolean isDirectoryLink() {
|
||||
return isSymbolicLink() && ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSymbolicLink() {
|
||||
return reparseTag == IO_REPARSE_TAG_SYMLINK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
// ignore FILE_ATTRIBUTE_DIRECTORY attribute if file is a sym link
|
||||
if (isSymbolicLink())
|
||||
return false;
|
||||
return ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOther() {
|
||||
if (isSymbolicLink())
|
||||
return false;
|
||||
// return true if device or reparse point
|
||||
return ((fileAttrs & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)) != 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegularFile() {
|
||||
return !isSymbolicLink() && !isDirectory() && !isOther();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return (fileAttrs & FILE_ATTRIBUTE_READONLY) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
return (fileAttrs & FILE_ATTRIBUTE_HIDDEN) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isArchive() {
|
||||
return (fileAttrs & FILE_ATTRIBUTE_ARCHIVE) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSystem() {
|
||||
return (fileAttrs & FILE_ATTRIBUTE_SYSTEM) != 0;
|
||||
}
|
||||
}
|
||||
505
jdkSrc/jdk8/sun/nio/fs/WindowsFileCopy.java
Normal file
505
jdkSrc/jdk8/sun/nio/fs/WindowsFileCopy.java
Normal file
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import com.sun.nio.file.ExtendedCopyOption;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
/**
|
||||
* Utility methods for copying and moving files.
|
||||
*/
|
||||
|
||||
class WindowsFileCopy {
|
||||
private WindowsFileCopy() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy file from source to target
|
||||
*/
|
||||
static void copy(final WindowsPath source,
|
||||
final WindowsPath target,
|
||||
CopyOption... options)
|
||||
throws IOException
|
||||
{
|
||||
// map options
|
||||
boolean replaceExisting = false;
|
||||
boolean copyAttributes = false;
|
||||
boolean followLinks = true;
|
||||
boolean interruptible = false;
|
||||
for (CopyOption option: options) {
|
||||
if (option == StandardCopyOption.REPLACE_EXISTING) {
|
||||
replaceExisting = true;
|
||||
continue;
|
||||
}
|
||||
if (option == LinkOption.NOFOLLOW_LINKS) {
|
||||
followLinks = false;
|
||||
continue;
|
||||
}
|
||||
if (option == StandardCopyOption.COPY_ATTRIBUTES) {
|
||||
copyAttributes = true;
|
||||
continue;
|
||||
}
|
||||
if (option == ExtendedCopyOption.INTERRUPTIBLE) {
|
||||
interruptible = true;
|
||||
continue;
|
||||
}
|
||||
if (option == null)
|
||||
throw new NullPointerException();
|
||||
throw new UnsupportedOperationException("Unsupported copy option");
|
||||
}
|
||||
|
||||
// check permissions. If the source file is a symbolic link then
|
||||
// later we must also check LinkPermission
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
source.checkRead();
|
||||
target.checkWrite();
|
||||
}
|
||||
|
||||
// get attributes of source file
|
||||
// attempt to get attributes of target file
|
||||
// if both files are the same there is nothing to do
|
||||
// if target exists and !replace then throw exception
|
||||
|
||||
WindowsFileAttributes sourceAttrs = null;
|
||||
WindowsFileAttributes targetAttrs = null;
|
||||
|
||||
long sourceHandle = 0L;
|
||||
try {
|
||||
sourceHandle = source.openForReadAttributeAccess(followLinks);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(source);
|
||||
}
|
||||
try {
|
||||
// source attributes
|
||||
try {
|
||||
sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(source);
|
||||
}
|
||||
|
||||
// open target (don't follow links)
|
||||
long targetHandle = 0L;
|
||||
try {
|
||||
targetHandle = target.openForReadAttributeAccess(false);
|
||||
try {
|
||||
targetAttrs = WindowsFileAttributes.readAttributes(targetHandle);
|
||||
|
||||
// if both files are the same then nothing to do
|
||||
if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// can't replace file
|
||||
if (!replaceExisting) {
|
||||
throw new FileAlreadyExistsException(
|
||||
target.getPathForExceptionMessage());
|
||||
}
|
||||
|
||||
} finally {
|
||||
CloseHandle(targetHandle);
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
} finally {
|
||||
CloseHandle(sourceHandle);
|
||||
}
|
||||
|
||||
// if source file is a symbolic link then we must check for LinkPermission
|
||||
if (sm != null && sourceAttrs.isSymbolicLink()) {
|
||||
sm.checkPermission(new LinkPermission("symbolic"));
|
||||
}
|
||||
|
||||
final String sourcePath = asWin32Path(source);
|
||||
final String targetPath = asWin32Path(target);
|
||||
|
||||
// if target exists then delete it.
|
||||
if (targetAttrs != null) {
|
||||
try {
|
||||
if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) {
|
||||
RemoveDirectory(targetPath);
|
||||
} else {
|
||||
DeleteFile(targetPath);
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
if (targetAttrs.isDirectory()) {
|
||||
// ERROR_ALREADY_EXISTS is returned when attempting to delete
|
||||
// non-empty directory on SAMBA servers.
|
||||
if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
|
||||
x.lastError() == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
throw new DirectoryNotEmptyException(
|
||||
target.getPathForExceptionMessage());
|
||||
}
|
||||
}
|
||||
x.rethrowAsIOException(target);
|
||||
}
|
||||
}
|
||||
|
||||
// Use CopyFileEx if the file is not a directory or junction
|
||||
if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {
|
||||
final int flags =
|
||||
(source.getFileSystem().supportsLinks() && !followLinks) ?
|
||||
COPY_FILE_COPY_SYMLINK : 0;
|
||||
|
||||
if (interruptible) {
|
||||
// interruptible copy
|
||||
Cancellable copyTask = new Cancellable() {
|
||||
@Override
|
||||
public int cancelValue() {
|
||||
return 1; // TRUE
|
||||
}
|
||||
@Override
|
||||
public void implRun() throws IOException {
|
||||
try {
|
||||
CopyFileEx(sourcePath, targetPath, flags,
|
||||
addressToPollForCancel());
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(source, target);
|
||||
}
|
||||
}
|
||||
};
|
||||
try {
|
||||
Cancellable.runInterruptibly(copyTask);
|
||||
} catch (ExecutionException e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof IOException)
|
||||
throw (IOException)t;
|
||||
throw new IOException(t);
|
||||
}
|
||||
} else {
|
||||
// non-interruptible copy
|
||||
try {
|
||||
CopyFileEx(sourcePath, targetPath, flags, 0L);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(source, target);
|
||||
}
|
||||
}
|
||||
if (copyAttributes) {
|
||||
// CopyFileEx does not copy security attributes
|
||||
try {
|
||||
copySecurityAttributes(source, target, followLinks);
|
||||
} catch (IOException x) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// copy directory or directory junction
|
||||
try {
|
||||
if (sourceAttrs.isDirectory()) {
|
||||
CreateDirectory(targetPath, 0L);
|
||||
} else {
|
||||
String linkTarget = WindowsLinkSupport.readLink(source);
|
||||
int flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
|
||||
CreateSymbolicLink(targetPath,
|
||||
WindowsPath.addPrefixIfNeeded(linkTarget),
|
||||
flags);
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(target);
|
||||
}
|
||||
if (copyAttributes) {
|
||||
// copy DOS/timestamps attributes
|
||||
WindowsFileAttributeViews.Dos view =
|
||||
WindowsFileAttributeViews.createDosView(target, false);
|
||||
try {
|
||||
view.setAttributes(sourceAttrs);
|
||||
} catch (IOException x) {
|
||||
if (sourceAttrs.isDirectory()) {
|
||||
try {
|
||||
RemoveDirectory(targetPath);
|
||||
} catch (WindowsException ignore) { }
|
||||
}
|
||||
}
|
||||
|
||||
// copy security attributes. If this fail it doesn't cause the move
|
||||
// to fail.
|
||||
try {
|
||||
copySecurityAttributes(source, target, followLinks);
|
||||
} catch (IOException ignore) { }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move file from source to target
|
||||
*/
|
||||
static void move(WindowsPath source, WindowsPath target, CopyOption... options)
|
||||
throws IOException
|
||||
{
|
||||
// map options
|
||||
boolean atomicMove = false;
|
||||
boolean replaceExisting = false;
|
||||
for (CopyOption option: options) {
|
||||
if (option == StandardCopyOption.ATOMIC_MOVE) {
|
||||
atomicMove = true;
|
||||
continue;
|
||||
}
|
||||
if (option == StandardCopyOption.REPLACE_EXISTING) {
|
||||
replaceExisting = true;
|
||||
continue;
|
||||
}
|
||||
if (option == LinkOption.NOFOLLOW_LINKS) {
|
||||
// ignore
|
||||
continue;
|
||||
}
|
||||
if (option == null) throw new NullPointerException();
|
||||
throw new UnsupportedOperationException("Unsupported copy option");
|
||||
}
|
||||
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
source.checkWrite();
|
||||
target.checkWrite();
|
||||
}
|
||||
|
||||
final String sourcePath = asWin32Path(source);
|
||||
final String targetPath = asWin32Path(target);
|
||||
|
||||
// atomic case
|
||||
if (atomicMove) {
|
||||
try {
|
||||
MoveFileEx(sourcePath, targetPath, MOVEFILE_REPLACE_EXISTING);
|
||||
} catch (WindowsException x) {
|
||||
if (x.lastError() == ERROR_NOT_SAME_DEVICE) {
|
||||
throw new AtomicMoveNotSupportedException(
|
||||
source.getPathForExceptionMessage(),
|
||||
target.getPathForExceptionMessage(),
|
||||
x.errorString());
|
||||
}
|
||||
x.rethrowAsIOException(source, target);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// get attributes of source file
|
||||
// attempt to get attributes of target file
|
||||
// if both files are the same there is nothing to do
|
||||
// if target exists and !replace then throw exception
|
||||
|
||||
WindowsFileAttributes sourceAttrs = null;
|
||||
WindowsFileAttributes targetAttrs = null;
|
||||
|
||||
long sourceHandle = 0L;
|
||||
try {
|
||||
sourceHandle = source.openForReadAttributeAccess(false);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(source);
|
||||
}
|
||||
try {
|
||||
// source attributes
|
||||
try {
|
||||
sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(source);
|
||||
}
|
||||
|
||||
// open target (don't follow links)
|
||||
long targetHandle = 0L;
|
||||
try {
|
||||
targetHandle = target.openForReadAttributeAccess(false);
|
||||
try {
|
||||
targetAttrs = WindowsFileAttributes.readAttributes(targetHandle);
|
||||
|
||||
// if both files are the same then nothing to do
|
||||
if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// can't replace file
|
||||
if (!replaceExisting) {
|
||||
throw new FileAlreadyExistsException(
|
||||
target.getPathForExceptionMessage());
|
||||
}
|
||||
|
||||
} finally {
|
||||
CloseHandle(targetHandle);
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
} finally {
|
||||
CloseHandle(sourceHandle);
|
||||
}
|
||||
|
||||
// if target exists then delete it.
|
||||
if (targetAttrs != null) {
|
||||
try {
|
||||
if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) {
|
||||
RemoveDirectory(targetPath);
|
||||
} else {
|
||||
DeleteFile(targetPath);
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
if (targetAttrs.isDirectory()) {
|
||||
// ERROR_ALREADY_EXISTS is returned when attempting to delete
|
||||
// non-empty directory on SAMBA servers.
|
||||
if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
|
||||
x.lastError() == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
throw new DirectoryNotEmptyException(
|
||||
target.getPathForExceptionMessage());
|
||||
}
|
||||
}
|
||||
x.rethrowAsIOException(target);
|
||||
}
|
||||
}
|
||||
|
||||
// first try MoveFileEx (no options). If target is on same volume then
|
||||
// all attributes (including security attributes) are preserved.
|
||||
try {
|
||||
MoveFileEx(sourcePath, targetPath, 0);
|
||||
return;
|
||||
} catch (WindowsException x) {
|
||||
if (x.lastError() != ERROR_NOT_SAME_DEVICE)
|
||||
x.rethrowAsIOException(source, target);
|
||||
}
|
||||
|
||||
// target is on different volume so use MoveFileEx with copy option
|
||||
if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {
|
||||
try {
|
||||
MoveFileEx(sourcePath, targetPath, MOVEFILE_COPY_ALLOWED);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(source, target);
|
||||
}
|
||||
// MoveFileEx does not copy security attributes when moving
|
||||
// across volumes.
|
||||
try {
|
||||
copySecurityAttributes(source, target, false);
|
||||
} catch (IOException x) {
|
||||
// ignore
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// moving directory or directory-link to another file system
|
||||
assert sourceAttrs.isDirectory() || sourceAttrs.isDirectoryLink();
|
||||
|
||||
// create new directory or directory junction
|
||||
try {
|
||||
if (sourceAttrs.isDirectory()) {
|
||||
CreateDirectory(targetPath, 0L);
|
||||
} else {
|
||||
String linkTarget = WindowsLinkSupport.readLink(source);
|
||||
CreateSymbolicLink(targetPath,
|
||||
WindowsPath.addPrefixIfNeeded(linkTarget),
|
||||
SYMBOLIC_LINK_FLAG_DIRECTORY);
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(target);
|
||||
}
|
||||
|
||||
// copy timestamps/DOS attributes
|
||||
WindowsFileAttributeViews.Dos view =
|
||||
WindowsFileAttributeViews.createDosView(target, false);
|
||||
try {
|
||||
view.setAttributes(sourceAttrs);
|
||||
} catch (IOException x) {
|
||||
// rollback
|
||||
try {
|
||||
RemoveDirectory(targetPath);
|
||||
} catch (WindowsException ignore) { }
|
||||
throw x;
|
||||
}
|
||||
|
||||
// copy security attributes. If this fails it doesn't cause the move
|
||||
// to fail.
|
||||
try {
|
||||
copySecurityAttributes(source, target, false);
|
||||
} catch (IOException ignore) { }
|
||||
|
||||
// delete source
|
||||
try {
|
||||
RemoveDirectory(sourcePath);
|
||||
} catch (WindowsException x) {
|
||||
// rollback
|
||||
try {
|
||||
RemoveDirectory(targetPath);
|
||||
} catch (WindowsException ignore) { }
|
||||
// ERROR_ALREADY_EXISTS is returned when attempting to delete
|
||||
// non-empty directory on SAMBA servers.
|
||||
if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
|
||||
x.lastError() == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
throw new DirectoryNotEmptyException(
|
||||
target.getPathForExceptionMessage());
|
||||
}
|
||||
x.rethrowAsIOException(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static String asWin32Path(WindowsPath path) throws IOException {
|
||||
try {
|
||||
return path.getPathForWin32Calls();
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(path);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy DACL/owner/group from source to target
|
||||
*/
|
||||
private static void copySecurityAttributes(WindowsPath source,
|
||||
WindowsPath target,
|
||||
boolean followLinks)
|
||||
throws IOException
|
||||
{
|
||||
String path = WindowsLinkSupport.getFinalPath(source, followLinks);
|
||||
|
||||
// may need SeRestorePrivilege to set file owner
|
||||
WindowsSecurity.Privilege priv =
|
||||
WindowsSecurity.enablePrivilege("SeRestorePrivilege");
|
||||
try {
|
||||
int request = (DACL_SECURITY_INFORMATION |
|
||||
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION);
|
||||
NativeBuffer buffer =
|
||||
WindowsAclFileAttributeView.getFileSecurity(path, request);
|
||||
try {
|
||||
try {
|
||||
SetFileSecurity(target.getPathForWin32Calls(), request,
|
||||
buffer.address());
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(target);
|
||||
}
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
} finally {
|
||||
priv.drop();
|
||||
}
|
||||
}
|
||||
}
|
||||
237
jdkSrc/jdk8/sun/nio/fs/WindowsFileStore.java
Normal file
237
jdkSrc/jdk8/sun/nio/fs/WindowsFileStore.java
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
|
||||
/**
|
||||
* Windows implementation of FileStore.
|
||||
*/
|
||||
|
||||
class WindowsFileStore
|
||||
extends FileStore
|
||||
{
|
||||
private final String root;
|
||||
private final VolumeInformation volInfo;
|
||||
private final int volType;
|
||||
private final String displayName; // returned by toString
|
||||
|
||||
private WindowsFileStore(String root) throws WindowsException {
|
||||
assert root.charAt(root.length()-1) == '\\';
|
||||
this.root = root;
|
||||
this.volInfo = GetVolumeInformation(root);
|
||||
this.volType = GetDriveType(root);
|
||||
|
||||
// file store "display name" is the volume name if available
|
||||
String vol = volInfo.volumeName();
|
||||
if (vol.length() > 0) {
|
||||
this.displayName = vol;
|
||||
} else {
|
||||
// TBD - should we map all types? Does this need to be localized?
|
||||
this.displayName = (volType == DRIVE_REMOVABLE) ? "Removable Disk" : "";
|
||||
}
|
||||
}
|
||||
|
||||
static WindowsFileStore create(String root, boolean ignoreNotReady)
|
||||
throws IOException
|
||||
{
|
||||
try {
|
||||
return new WindowsFileStore(root);
|
||||
} catch (WindowsException x) {
|
||||
if (ignoreNotReady && x.lastError() == ERROR_NOT_READY)
|
||||
return null;
|
||||
x.rethrowAsIOException(root);
|
||||
return null; // keep compiler happy
|
||||
}
|
||||
}
|
||||
|
||||
static WindowsFileStore create(WindowsPath file) throws IOException {
|
||||
try {
|
||||
// if the file is a link then GetVolumePathName returns the
|
||||
// volume that the link is on so we need to call it with the
|
||||
// final target
|
||||
String target;
|
||||
if (file.getFileSystem().supportsLinks()) {
|
||||
target = WindowsLinkSupport.getFinalPath(file, true);
|
||||
} else {
|
||||
// file must exist
|
||||
WindowsFileAttributes.get(file, true);
|
||||
target = file.getPathForWin32Calls();
|
||||
}
|
||||
try {
|
||||
return createFromPath(target);
|
||||
} catch (WindowsException e) {
|
||||
if (e.lastError() != ERROR_DIR_NOT_ROOT)
|
||||
throw e;
|
||||
target = WindowsLinkSupport.getFinalPath(file);
|
||||
if (target == null)
|
||||
throw new FileSystemException(file.getPathForExceptionMessage(),
|
||||
null, "Couldn't resolve path");
|
||||
return createFromPath(target);
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
return null; // keep compiler happy
|
||||
}
|
||||
}
|
||||
|
||||
private static WindowsFileStore createFromPath(String target) throws WindowsException {
|
||||
String root = GetVolumePathName(target);
|
||||
return new WindowsFileStore(root);
|
||||
}
|
||||
|
||||
VolumeInformation volumeInformation() {
|
||||
return volInfo;
|
||||
}
|
||||
|
||||
int volumeType() {
|
||||
return volType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return volInfo.volumeName(); // "SYSTEM", "DVD-RW", ...
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return volInfo.fileSystemName(); // "FAT", "NTFS", ...
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return ((volInfo.flags() & FILE_READ_ONLY_VOLUME) != 0);
|
||||
}
|
||||
|
||||
// read the free space info
|
||||
private DiskFreeSpace readDiskFreeSpace() throws IOException {
|
||||
try {
|
||||
return GetDiskFreeSpaceEx(root);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(root);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalSpace() throws IOException {
|
||||
return readDiskFreeSpace().totalNumberOfBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getUsableSpace() throws IOException {
|
||||
return readDiskFreeSpace().freeBytesAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getUnallocatedSpace() throws IOException {
|
||||
return readDiskFreeSpace().freeBytesAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
|
||||
if (type == null)
|
||||
throw new NullPointerException();
|
||||
return (V) null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String attribute) throws IOException {
|
||||
// standard
|
||||
if (attribute.equals("totalSpace"))
|
||||
return getTotalSpace();
|
||||
if (attribute.equals("usableSpace"))
|
||||
return getUsableSpace();
|
||||
if (attribute.equals("unallocatedSpace"))
|
||||
return getUnallocatedSpace();
|
||||
// windows specific for testing purposes
|
||||
if (attribute.equals("volume:vsn"))
|
||||
return volInfo.volumeSerialNumber();
|
||||
if (attribute.equals("volume:isRemovable"))
|
||||
return volType == DRIVE_REMOVABLE;
|
||||
if (attribute.equals("volume:isCdrom"))
|
||||
return volType == DRIVE_CDROM;
|
||||
throw new UnsupportedOperationException("'" + attribute + "' not recognized");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
|
||||
if (type == null)
|
||||
throw new NullPointerException();
|
||||
if (type == BasicFileAttributeView.class || type == DosFileAttributeView.class)
|
||||
return true;
|
||||
if (type == AclFileAttributeView.class || type == FileOwnerAttributeView.class)
|
||||
return ((volInfo.flags() & FILE_PERSISTENT_ACLS) != 0);
|
||||
if (type == UserDefinedFileAttributeView.class)
|
||||
return ((volInfo.flags() & FILE_NAMED_STREAMS) != 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFileAttributeView(String name) {
|
||||
if (name.equals("basic") || name.equals("dos"))
|
||||
return true;
|
||||
if (name.equals("acl"))
|
||||
return supportsFileAttributeView(AclFileAttributeView.class);
|
||||
if (name.equals("owner"))
|
||||
return supportsFileAttributeView(FileOwnerAttributeView.class);
|
||||
if (name.equals("user"))
|
||||
return supportsFileAttributeView(UserDefinedFileAttributeView.class);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object ob) {
|
||||
if (ob == this)
|
||||
return true;
|
||||
if (!(ob instanceof WindowsFileStore))
|
||||
return false;
|
||||
WindowsFileStore other = (WindowsFileStore)ob;
|
||||
return root.equals(other.root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return root.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(displayName);
|
||||
if (sb.length() > 0)
|
||||
sb.append(" ");
|
||||
sb.append("(");
|
||||
// drop trailing slash
|
||||
sb.append(root.subSequence(0, root.length()-1));
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
325
jdkSrc/jdk8/sun/nio/fs/WindowsFileSystem.java
Normal file
325
jdkSrc/jdk8/sun/nio/fs/WindowsFileSystem.java
Normal file
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.nio.file.spi.*;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.io.IOException;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
class WindowsFileSystem
|
||||
extends FileSystem
|
||||
{
|
||||
private final WindowsFileSystemProvider provider;
|
||||
|
||||
// default directory (is absolute), and default root
|
||||
private final String defaultDirectory;
|
||||
private final String defaultRoot;
|
||||
|
||||
private final boolean supportsLinks;
|
||||
private final boolean supportsStreamEnumeration;
|
||||
|
||||
// package-private
|
||||
WindowsFileSystem(WindowsFileSystemProvider provider,
|
||||
String dir)
|
||||
{
|
||||
this.provider = provider;
|
||||
|
||||
// parse default directory and check it is absolute
|
||||
WindowsPathParser.Result result = WindowsPathParser.parse(dir);
|
||||
|
||||
if ((result.type() != WindowsPathType.ABSOLUTE) &&
|
||||
(result.type() != WindowsPathType.UNC))
|
||||
throw new AssertionError("Default directory is not an absolute path");
|
||||
this.defaultDirectory = result.path();
|
||||
this.defaultRoot = result.root();
|
||||
|
||||
PrivilegedAction<String> pa = new GetPropertyAction("os.version");
|
||||
String osversion = AccessController.doPrivileged(pa);
|
||||
String[] vers = Util.split(osversion, '.');
|
||||
int major = Integer.parseInt(vers[0]);
|
||||
int minor = Integer.parseInt(vers[1]);
|
||||
|
||||
// symbolic links available on Vista and newer
|
||||
supportsLinks = (major >= 6);
|
||||
|
||||
// enumeration of data streams available on Windows Server 2003 and newer
|
||||
supportsStreamEnumeration = (major >= 6) || (major == 5 && minor >= 2);
|
||||
}
|
||||
|
||||
// package-private
|
||||
String defaultDirectory() {
|
||||
return defaultDirectory;
|
||||
}
|
||||
|
||||
String defaultRoot() {
|
||||
return defaultRoot;
|
||||
}
|
||||
|
||||
boolean supportsLinks() {
|
||||
return supportsLinks;
|
||||
}
|
||||
|
||||
boolean supportsStreamEnumeration() {
|
||||
return supportsStreamEnumeration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemProvider provider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSeparator() {
|
||||
return "\\";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Path> getRootDirectories() {
|
||||
int drives = 0;
|
||||
try {
|
||||
drives = WindowsNativeDispatcher.GetLogicalDrives();
|
||||
} catch (WindowsException x) {
|
||||
// shouldn't happen
|
||||
throw new AssertionError(x.getMessage());
|
||||
}
|
||||
|
||||
// iterate over roots, ignoring those that the security manager denies
|
||||
ArrayList<Path> result = new ArrayList<>();
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
for (int i = 0; i <= 25; i++) { // 0->A, 1->B, 2->C...
|
||||
if ((drives & (1 << i)) != 0) {
|
||||
StringBuilder sb = new StringBuilder(3);
|
||||
sb.append((char)('A' + i));
|
||||
sb.append(":\\");
|
||||
String root = sb.toString();
|
||||
if (sm != null) {
|
||||
try {
|
||||
sm.checkRead(root);
|
||||
} catch (SecurityException x) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
result.add(WindowsPath.createFromNormalizedPath(this, root));
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator returned by getFileStores method.
|
||||
*/
|
||||
private class FileStoreIterator implements Iterator<FileStore> {
|
||||
private final Iterator<Path> roots;
|
||||
private FileStore next;
|
||||
|
||||
FileStoreIterator() {
|
||||
this.roots = getRootDirectories().iterator();
|
||||
}
|
||||
|
||||
private FileStore readNext() {
|
||||
assert Thread.holdsLock(this);
|
||||
for (;;) {
|
||||
if (!roots.hasNext())
|
||||
return null;
|
||||
WindowsPath root = (WindowsPath)roots.next();
|
||||
// ignore if security manager denies access
|
||||
try {
|
||||
root.checkRead();
|
||||
} catch (SecurityException x) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
FileStore fs = WindowsFileStore.create(root.toString(), true);
|
||||
if (fs != null)
|
||||
return fs;
|
||||
} catch (IOException ioe) {
|
||||
// skip it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean hasNext() {
|
||||
if (next != null)
|
||||
return true;
|
||||
next = readNext();
|
||||
return next != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized FileStore next() {
|
||||
if (next == null)
|
||||
next = readNext();
|
||||
if (next == null) {
|
||||
throw new NoSuchElementException();
|
||||
} else {
|
||||
FileStore result = next;
|
||||
next = null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<FileStore> getFileStores() {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
try {
|
||||
sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
|
||||
} catch (SecurityException se) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
return new Iterable<FileStore>() {
|
||||
public Iterator<FileStore> iterator() {
|
||||
return new FileStoreIterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// supported views
|
||||
private static final Set<String> supportedFileAttributeViews = Collections
|
||||
.unmodifiableSet(new HashSet<String>(Arrays.asList("basic", "dos", "acl", "owner", "user")));
|
||||
|
||||
@Override
|
||||
public Set<String> supportedFileAttributeViews() {
|
||||
return supportedFileAttributeViews;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Path getPath(String first, String... more) {
|
||||
String path;
|
||||
if (more.length == 0) {
|
||||
path = first;
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(first);
|
||||
for (String segment: more) {
|
||||
if (segment.length() > 0) {
|
||||
if (sb.length() > 0)
|
||||
sb.append('\\');
|
||||
sb.append(segment);
|
||||
}
|
||||
}
|
||||
path = sb.toString();
|
||||
}
|
||||
return WindowsPath.parse(this, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserPrincipalLookupService getUserPrincipalLookupService() {
|
||||
return LookupService.instance;
|
||||
}
|
||||
|
||||
private static class LookupService {
|
||||
static final UserPrincipalLookupService instance =
|
||||
new UserPrincipalLookupService() {
|
||||
@Override
|
||||
public UserPrincipal lookupPrincipalByName(String name)
|
||||
throws IOException
|
||||
{
|
||||
return WindowsUserPrincipals.lookup(name);
|
||||
}
|
||||
@Override
|
||||
public GroupPrincipal lookupPrincipalByGroupName(String group)
|
||||
throws IOException
|
||||
{
|
||||
UserPrincipal user = WindowsUserPrincipals.lookup(group);
|
||||
if (!(user instanceof GroupPrincipal))
|
||||
throw new UserPrincipalNotFoundException(group);
|
||||
return (GroupPrincipal)user;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathMatcher getPathMatcher(String syntaxAndInput) {
|
||||
int pos = syntaxAndInput.indexOf(':');
|
||||
if (pos <= 0 || pos == syntaxAndInput.length())
|
||||
throw new IllegalArgumentException();
|
||||
String syntax = syntaxAndInput.substring(0, pos);
|
||||
String input = syntaxAndInput.substring(pos+1);
|
||||
|
||||
String expr;
|
||||
if (syntax.equals(GLOB_SYNTAX)) {
|
||||
expr = Globs.toWindowsRegexPattern(input);
|
||||
} else {
|
||||
if (syntax.equals(REGEX_SYNTAX)) {
|
||||
expr = input;
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Syntax '" + syntax +
|
||||
"' not recognized");
|
||||
}
|
||||
}
|
||||
|
||||
// match in unicode_case_insensitive
|
||||
final Pattern pattern = Pattern.compile(expr,
|
||||
Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
||||
|
||||
// return matcher
|
||||
return new PathMatcher() {
|
||||
@Override
|
||||
public boolean matches(Path path) {
|
||||
return pattern.matcher(path.toString()).matches();
|
||||
}
|
||||
};
|
||||
}
|
||||
private static final String GLOB_SYNTAX = "glob";
|
||||
private static final String REGEX_SYNTAX = "regex";
|
||||
|
||||
@Override
|
||||
public WatchService newWatchService()
|
||||
throws IOException
|
||||
{
|
||||
return new WindowsWatchService(this);
|
||||
}
|
||||
}
|
||||
631
jdkSrc/jdk8/sun/nio/fs/WindowsFileSystemProvider.java
Normal file
631
jdkSrc/jdk8/sun/nio/fs/WindowsFileSystemProvider.java
Normal file
@@ -0,0 +1,631 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.nio.channels.*;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.security.AccessController;
|
||||
import sun.misc.Unsafe;
|
||||
import sun.nio.ch.ThreadPool;
|
||||
import sun.security.util.SecurityConstants;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
import static sun.nio.fs.WindowsSecurity.*;
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
public class WindowsFileSystemProvider
|
||||
extends AbstractFileSystemProvider
|
||||
{
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
private static final String USER_DIR = "user.dir";
|
||||
private final WindowsFileSystem theFileSystem;
|
||||
|
||||
public WindowsFileSystemProvider() {
|
||||
theFileSystem = new WindowsFileSystem(this, System.getProperty(USER_DIR));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScheme() {
|
||||
return "file";
|
||||
}
|
||||
|
||||
private void checkUri(URI uri) {
|
||||
if (!uri.getScheme().equalsIgnoreCase(getScheme()))
|
||||
throw new IllegalArgumentException("URI does not match this provider");
|
||||
if (uri.getAuthority() != null)
|
||||
throw new IllegalArgumentException("Authority component present");
|
||||
if (uri.getPath() == null)
|
||||
throw new IllegalArgumentException("Path component is undefined");
|
||||
if (!uri.getPath().equals("/"))
|
||||
throw new IllegalArgumentException("Path component should be '/'");
|
||||
if (uri.getQuery() != null)
|
||||
throw new IllegalArgumentException("Query component present");
|
||||
if (uri.getFragment() != null)
|
||||
throw new IllegalArgumentException("Fragment component present");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystem newFileSystem(URI uri, Map<String,?> env)
|
||||
throws IOException
|
||||
{
|
||||
checkUri(uri);
|
||||
throw new FileSystemAlreadyExistsException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final FileSystem getFileSystem(URI uri) {
|
||||
checkUri(uri);
|
||||
return theFileSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPath(URI uri) {
|
||||
return WindowsUriSupport.fromUri(theFileSystem, uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileChannel newFileChannel(Path path,
|
||||
Set<? extends OpenOption> options,
|
||||
FileAttribute<?>... attrs)
|
||||
throws IOException
|
||||
{
|
||||
if (path == null)
|
||||
throw new NullPointerException();
|
||||
if (!(path instanceof WindowsPath))
|
||||
throw new ProviderMismatchException();
|
||||
WindowsPath file = (WindowsPath)path;
|
||||
|
||||
WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs);
|
||||
try {
|
||||
return WindowsChannelFactory
|
||||
.newFileChannel(file.getPathForWin32Calls(),
|
||||
file.getPathForPermissionCheck(),
|
||||
options,
|
||||
sd.address());
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
return null;
|
||||
} finally {
|
||||
if (sd != null)
|
||||
sd.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsynchronousFileChannel newAsynchronousFileChannel(Path path,
|
||||
Set<? extends OpenOption> options,
|
||||
ExecutorService executor,
|
||||
FileAttribute<?>... attrs)
|
||||
throws IOException
|
||||
{
|
||||
if (path == null)
|
||||
throw new NullPointerException();
|
||||
if (!(path instanceof WindowsPath))
|
||||
throw new ProviderMismatchException();
|
||||
WindowsPath file = (WindowsPath)path;
|
||||
ThreadPool pool = (executor == null) ? null : ThreadPool.wrap(executor, 0);
|
||||
WindowsSecurityDescriptor sd =
|
||||
WindowsSecurityDescriptor.fromAttribute(attrs);
|
||||
try {
|
||||
return WindowsChannelFactory
|
||||
.newAsynchronousFileChannel(file.getPathForWin32Calls(),
|
||||
file.getPathForPermissionCheck(),
|
||||
options,
|
||||
sd.address(),
|
||||
pool);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
return null;
|
||||
} finally {
|
||||
if (sd != null)
|
||||
sd.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V extends FileAttributeView> V
|
||||
getFileAttributeView(Path obj, Class<V> view, LinkOption... options)
|
||||
{
|
||||
WindowsPath file = WindowsPath.toWindowsPath(obj);
|
||||
if (view == null)
|
||||
throw new NullPointerException();
|
||||
boolean followLinks = Util.followLinks(options);
|
||||
if (view == BasicFileAttributeView.class)
|
||||
return (V) WindowsFileAttributeViews.createBasicView(file, followLinks);
|
||||
if (view == DosFileAttributeView.class)
|
||||
return (V) WindowsFileAttributeViews.createDosView(file, followLinks);
|
||||
if (view == AclFileAttributeView.class)
|
||||
return (V) new WindowsAclFileAttributeView(file, followLinks);
|
||||
if (view == FileOwnerAttributeView.class)
|
||||
return (V) new FileOwnerAttributeViewImpl(
|
||||
new WindowsAclFileAttributeView(file, followLinks));
|
||||
if (view == UserDefinedFileAttributeView.class)
|
||||
return (V) new WindowsUserDefinedFileAttributeView(file, followLinks);
|
||||
return (V) null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <A extends BasicFileAttributes> A readAttributes(Path file,
|
||||
Class<A> type,
|
||||
LinkOption... options)
|
||||
throws IOException
|
||||
{
|
||||
Class<? extends BasicFileAttributeView> view;
|
||||
if (type == BasicFileAttributes.class)
|
||||
view = BasicFileAttributeView.class;
|
||||
else if (type == DosFileAttributes.class)
|
||||
view = DosFileAttributeView.class;
|
||||
else if (type == null)
|
||||
throw new NullPointerException();
|
||||
else
|
||||
throw new UnsupportedOperationException();
|
||||
return (A) getFileAttributeView(file, view, options).readAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicFileAttributeView getFileAttributeView(Path obj, String name, LinkOption... options) {
|
||||
WindowsPath file = WindowsPath.toWindowsPath(obj);
|
||||
boolean followLinks = Util.followLinks(options);
|
||||
if (name.equals("basic"))
|
||||
return WindowsFileAttributeViews.createBasicView(file, followLinks);
|
||||
if (name.equals("dos"))
|
||||
return WindowsFileAttributeViews.createDosView(file, followLinks);
|
||||
if (name.equals("acl"))
|
||||
return new WindowsAclFileAttributeView(file, followLinks);
|
||||
if (name.equals("owner"))
|
||||
return new FileOwnerAttributeViewImpl(
|
||||
new WindowsAclFileAttributeView(file, followLinks));
|
||||
if (name.equals("user"))
|
||||
return new WindowsUserDefinedFileAttributeView(file, followLinks);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel newByteChannel(Path obj,
|
||||
Set<? extends OpenOption> options,
|
||||
FileAttribute<?>... attrs)
|
||||
throws IOException
|
||||
{
|
||||
WindowsPath file = WindowsPath.toWindowsPath(obj);
|
||||
WindowsSecurityDescriptor sd =
|
||||
WindowsSecurityDescriptor.fromAttribute(attrs);
|
||||
try {
|
||||
return WindowsChannelFactory
|
||||
.newFileChannel(file.getPathForWin32Calls(),
|
||||
file.getPathForPermissionCheck(),
|
||||
options,
|
||||
sd.address());
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
return null; // keep compiler happy
|
||||
} finally {
|
||||
sd.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean implDelete(Path obj, boolean failIfNotExists) throws IOException {
|
||||
WindowsPath file = WindowsPath.toWindowsPath(obj);
|
||||
file.checkDelete();
|
||||
|
||||
WindowsFileAttributes attrs = null;
|
||||
try {
|
||||
// need to know if file is a directory or junction
|
||||
attrs = WindowsFileAttributes.get(file, false);
|
||||
if (attrs.isDirectory() || attrs.isDirectoryLink()) {
|
||||
RemoveDirectory(file.getPathForWin32Calls());
|
||||
} else {
|
||||
DeleteFile(file.getPathForWin32Calls());
|
||||
}
|
||||
return true;
|
||||
} catch (WindowsException x) {
|
||||
|
||||
// no-op if file does not exist
|
||||
if (!failIfNotExists &&
|
||||
(x.lastError() == ERROR_FILE_NOT_FOUND ||
|
||||
x.lastError() == ERROR_PATH_NOT_FOUND)) return false;
|
||||
|
||||
if (attrs != null && attrs.isDirectory()) {
|
||||
// ERROR_ALREADY_EXISTS is returned when attempting to delete
|
||||
// non-empty directory on SAMBA servers.
|
||||
if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
|
||||
x.lastError() == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
throw new DirectoryNotEmptyException(
|
||||
file.getPathForExceptionMessage());
|
||||
}
|
||||
}
|
||||
x.rethrowAsIOException(file);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(Path source, Path target, CopyOption... options)
|
||||
throws IOException
|
||||
{
|
||||
WindowsFileCopy.copy(WindowsPath.toWindowsPath(source),
|
||||
WindowsPath.toWindowsPath(target),
|
||||
options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(Path source, Path target, CopyOption... options)
|
||||
throws IOException
|
||||
{
|
||||
WindowsFileCopy.move(WindowsPath.toWindowsPath(source),
|
||||
WindowsPath.toWindowsPath(target),
|
||||
options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the file security against desired access.
|
||||
*/
|
||||
private static boolean hasDesiredAccess(WindowsPath file, int rights) throws IOException {
|
||||
// read security descriptor containing ACL (symlinks are followed)
|
||||
boolean hasRights = false;
|
||||
String target = WindowsLinkSupport.getFinalPath(file, true);
|
||||
NativeBuffer aclBuffer = WindowsAclFileAttributeView
|
||||
.getFileSecurity(target,
|
||||
DACL_SECURITY_INFORMATION
|
||||
| OWNER_SECURITY_INFORMATION
|
||||
| GROUP_SECURITY_INFORMATION);
|
||||
try {
|
||||
hasRights = checkAccessMask(aclBuffer.address(), rights,
|
||||
FILE_GENERIC_READ,
|
||||
FILE_GENERIC_WRITE,
|
||||
FILE_GENERIC_EXECUTE,
|
||||
FILE_ALL_ACCESS);
|
||||
} catch (WindowsException exc) {
|
||||
exc.rethrowAsIOException(file);
|
||||
} finally {
|
||||
aclBuffer.release();
|
||||
}
|
||||
return hasRights;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given file(or directory) exists and is readable.
|
||||
*/
|
||||
private void checkReadAccess(WindowsPath file) throws IOException {
|
||||
try {
|
||||
Set<OpenOption> opts = Collections.emptySet();
|
||||
FileChannel fc = WindowsChannelFactory
|
||||
.newFileChannel(file.getPathForWin32Calls(),
|
||||
file.getPathForPermissionCheck(),
|
||||
opts,
|
||||
0L);
|
||||
fc.close();
|
||||
} catch (WindowsException exc) {
|
||||
// Windows errors are very inconsistent when the file is a directory
|
||||
// (ERROR_PATH_NOT_FOUND returned for root directories for example)
|
||||
// so we retry by attempting to open it as a directory.
|
||||
try {
|
||||
new WindowsDirectoryStream(file, null).close();
|
||||
} catch (IOException ioe) {
|
||||
// translate and throw original exception
|
||||
exc.rethrowAsIOException(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAccess(Path obj, AccessMode... modes) throws IOException {
|
||||
WindowsPath file = WindowsPath.toWindowsPath(obj);
|
||||
|
||||
boolean r = false;
|
||||
boolean w = false;
|
||||
boolean x = false;
|
||||
for (AccessMode mode: modes) {
|
||||
switch (mode) {
|
||||
case READ : r = true; break;
|
||||
case WRITE : w = true; break;
|
||||
case EXECUTE : x = true; break;
|
||||
default: throw new AssertionError("Should not get here");
|
||||
}
|
||||
}
|
||||
|
||||
// special-case read access to avoid needing to determine effective
|
||||
// access to file; default if modes not specified
|
||||
if (!w && !x) {
|
||||
checkReadAccess(file);
|
||||
return;
|
||||
}
|
||||
|
||||
int mask = 0;
|
||||
if (r) {
|
||||
file.checkRead();
|
||||
mask |= FILE_READ_DATA;
|
||||
}
|
||||
if (w) {
|
||||
file.checkWrite();
|
||||
mask |= FILE_WRITE_DATA;
|
||||
}
|
||||
if (x) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkExec(file.getPathForPermissionCheck());
|
||||
mask |= FILE_EXECUTE;
|
||||
}
|
||||
|
||||
if (!hasDesiredAccess(file, mask))
|
||||
throw new AccessDeniedException(
|
||||
file.getPathForExceptionMessage(), null,
|
||||
"Permissions does not allow requested access");
|
||||
|
||||
// for write access we neeed to check if the DOS readonly attribute
|
||||
// and if the volume is read-only
|
||||
if (w) {
|
||||
try {
|
||||
WindowsFileAttributes attrs = WindowsFileAttributes.get(file, true);
|
||||
if (!attrs.isDirectory() && attrs.isReadOnly())
|
||||
throw new AccessDeniedException(
|
||||
file.getPathForExceptionMessage(), null,
|
||||
"DOS readonly attribute is set");
|
||||
} catch (WindowsException exc) {
|
||||
exc.rethrowAsIOException(file);
|
||||
}
|
||||
|
||||
if (WindowsFileStore.create(file).isReadOnly()) {
|
||||
throw new AccessDeniedException(
|
||||
file.getPathForExceptionMessage(), null, "Read-only file system");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameFile(Path obj1, Path obj2) throws IOException {
|
||||
WindowsPath file1 = WindowsPath.toWindowsPath(obj1);
|
||||
if (file1.equals(obj2))
|
||||
return true;
|
||||
if (obj2 == null)
|
||||
throw new NullPointerException();
|
||||
if (!(obj2 instanceof WindowsPath))
|
||||
return false;
|
||||
WindowsPath file2 = (WindowsPath)obj2;
|
||||
|
||||
// check security manager access to both files
|
||||
file1.checkRead();
|
||||
file2.checkRead();
|
||||
|
||||
// open both files and see if they are the same
|
||||
long h1 = 0L;
|
||||
try {
|
||||
h1 = file1.openForReadAttributeAccess(true);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file1);
|
||||
}
|
||||
try {
|
||||
WindowsFileAttributes attrs1 = null;
|
||||
try {
|
||||
attrs1 = WindowsFileAttributes.readAttributes(h1);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file1);
|
||||
}
|
||||
long h2 = 0L;
|
||||
try {
|
||||
h2 = file2.openForReadAttributeAccess(true);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file2);
|
||||
}
|
||||
try {
|
||||
WindowsFileAttributes attrs2 = null;
|
||||
try {
|
||||
attrs2 = WindowsFileAttributes.readAttributes(h2);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file2);
|
||||
}
|
||||
return WindowsFileAttributes.isSameFile(attrs1, attrs2);
|
||||
} finally {
|
||||
CloseHandle(h2);
|
||||
}
|
||||
} finally {
|
||||
CloseHandle(h1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden(Path obj) throws IOException {
|
||||
WindowsPath file = WindowsPath.toWindowsPath(obj);
|
||||
file.checkRead();
|
||||
WindowsFileAttributes attrs = null;
|
||||
try {
|
||||
attrs = WindowsFileAttributes.get(file, true);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
}
|
||||
// DOS hidden attribute not meaningful when set on directories
|
||||
if (attrs.isDirectory())
|
||||
return false;
|
||||
return attrs.isHidden();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileStore getFileStore(Path obj) throws IOException {
|
||||
WindowsPath file = WindowsPath.toWindowsPath(obj);
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
|
||||
file.checkRead();
|
||||
}
|
||||
return WindowsFileStore.create(file);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void createDirectory(Path obj, FileAttribute<?>... attrs)
|
||||
throws IOException
|
||||
{
|
||||
WindowsPath dir = WindowsPath.toWindowsPath(obj);
|
||||
dir.checkWrite();
|
||||
WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs);
|
||||
try {
|
||||
CreateDirectory(dir.getPathForWin32Calls(), sd.address());
|
||||
} catch (WindowsException x) {
|
||||
// convert ERROR_ACCESS_DENIED to FileAlreadyExistsException if we can
|
||||
// verify that the directory exists
|
||||
if (x.lastError() == ERROR_ACCESS_DENIED) {
|
||||
try {
|
||||
if (WindowsFileAttributes.get(dir, false).isDirectory())
|
||||
throw new FileAlreadyExistsException(dir.toString());
|
||||
} catch (WindowsException ignore) { }
|
||||
}
|
||||
x.rethrowAsIOException(dir);
|
||||
} finally {
|
||||
sd.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryStream<Path> newDirectoryStream(Path obj, DirectoryStream.Filter<? super Path> filter)
|
||||
throws IOException
|
||||
{
|
||||
WindowsPath dir = WindowsPath.toWindowsPath(obj);
|
||||
dir.checkRead();
|
||||
if (filter == null)
|
||||
throw new NullPointerException();
|
||||
return new WindowsDirectoryStream(dir, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createSymbolicLink(Path obj1, Path obj2, FileAttribute<?>... attrs)
|
||||
throws IOException
|
||||
{
|
||||
WindowsPath link = WindowsPath.toWindowsPath(obj1);
|
||||
WindowsPath target = WindowsPath.toWindowsPath(obj2);
|
||||
|
||||
if (!link.getFileSystem().supportsLinks()) {
|
||||
throw new UnsupportedOperationException("Symbolic links not supported "
|
||||
+ "on this operating system");
|
||||
}
|
||||
|
||||
// no attributes allowed
|
||||
if (attrs.length > 0) {
|
||||
WindowsSecurityDescriptor.fromAttribute(attrs); // may throw NPE or UOE
|
||||
throw new UnsupportedOperationException("Initial file attributes" +
|
||||
"not supported when creating symbolic link");
|
||||
}
|
||||
|
||||
// permission check
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(new LinkPermission("symbolic"));
|
||||
link.checkWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw I/O exception for the drive-relative case because Windows
|
||||
* creates a link with the resolved target for this case.
|
||||
*/
|
||||
if (target.type() == WindowsPathType.DRIVE_RELATIVE) {
|
||||
throw new IOException("Cannot create symbolic link to working directory relative target");
|
||||
}
|
||||
|
||||
/*
|
||||
* Windows treates symbolic links to directories differently than it
|
||||
* does to other file types. For that reason we need to check if the
|
||||
* target is a directory (or a directory junction).
|
||||
*/
|
||||
WindowsPath resolvedTarget;
|
||||
if (target.type() == WindowsPathType.RELATIVE) {
|
||||
WindowsPath parent = link.getParent();
|
||||
resolvedTarget = (parent == null) ? target : parent.resolve(target);
|
||||
} else {
|
||||
resolvedTarget = link.resolve(target);
|
||||
}
|
||||
int flags = 0;
|
||||
try {
|
||||
WindowsFileAttributes wattrs = WindowsFileAttributes.get(resolvedTarget, false);
|
||||
if (wattrs.isDirectory() || wattrs.isDirectoryLink())
|
||||
flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
|
||||
} catch (WindowsException x) {
|
||||
// unable to access target so assume target is not a directory
|
||||
}
|
||||
|
||||
// create the link
|
||||
try {
|
||||
CreateSymbolicLink(link.getPathForWin32Calls(),
|
||||
WindowsPath.addPrefixIfNeeded(target.toString()),
|
||||
flags);
|
||||
} catch (WindowsException x) {
|
||||
if (x.lastError() == ERROR_INVALID_REPARSE_DATA) {
|
||||
x.rethrowAsIOException(link, target);
|
||||
} else {
|
||||
x.rethrowAsIOException(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createLink(Path obj1, Path obj2) throws IOException {
|
||||
WindowsPath link = WindowsPath.toWindowsPath(obj1);
|
||||
WindowsPath existing = WindowsPath.toWindowsPath(obj2);
|
||||
|
||||
// permission check
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(new LinkPermission("hard"));
|
||||
link.checkWrite();
|
||||
existing.checkWrite();
|
||||
}
|
||||
|
||||
// create hard link
|
||||
try {
|
||||
CreateHardLink(link.getPathForWin32Calls(),
|
||||
existing.getPathForWin32Calls());
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(link, existing);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path readSymbolicLink(Path obj1) throws IOException {
|
||||
WindowsPath link = WindowsPath.toWindowsPath(obj1);
|
||||
WindowsFileSystem fs = link.getFileSystem();
|
||||
if (!fs.supportsLinks()) {
|
||||
throw new UnsupportedOperationException("symbolic links not supported");
|
||||
}
|
||||
|
||||
// permission check
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
FilePermission perm = new FilePermission(link.getPathForPermissionCheck(),
|
||||
SecurityConstants.FILE_READLINK_ACTION);
|
||||
sm.checkPermission(perm);
|
||||
}
|
||||
|
||||
String target = WindowsLinkSupport.readLink(link);
|
||||
return WindowsPath.createFromNormalizedPath(fs, target);
|
||||
}
|
||||
}
|
||||
433
jdkSrc/jdk8/sun/nio/fs/WindowsLinkSupport.java
Normal file
433
jdkSrc/jdk8/sun/nio/fs/WindowsLinkSupport.java
Normal file
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.io.IOException;
|
||||
import java.io.IOError;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
/**
|
||||
* Utility methods for symbolic link support on Windows Vista and newer.
|
||||
*/
|
||||
|
||||
class WindowsLinkSupport {
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
private WindowsLinkSupport() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target of a symbolic link
|
||||
*/
|
||||
static String readLink(WindowsPath path) throws IOException {
|
||||
long handle = 0L;
|
||||
try {
|
||||
handle = path.openForReadAttributeAccess(false); // don't follow links
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(path);
|
||||
}
|
||||
try {
|
||||
return readLinkImpl(handle);
|
||||
} finally {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the final path (all symbolic links resolved) or null if this
|
||||
* operation is not supported.
|
||||
*/
|
||||
static String getFinalPath(WindowsPath input) throws IOException {
|
||||
long h = 0;
|
||||
try {
|
||||
h = input.openForReadAttributeAccess(true);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(input);
|
||||
}
|
||||
try {
|
||||
return stripPrefix(GetFinalPathNameByHandle(h));
|
||||
} catch (WindowsException x) {
|
||||
// ERROR_INVALID_LEVEL is the error returned when not supported
|
||||
// (a sym link to file on FAT32 or Samba server for example)
|
||||
if (x.lastError() != ERROR_INVALID_LEVEL)
|
||||
x.rethrowAsIOException(input);
|
||||
} finally {
|
||||
CloseHandle(h);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the final path of a given path as a String. This should be used
|
||||
* prior to calling Win32 system calls that do not follow links.
|
||||
*/
|
||||
static String getFinalPath(WindowsPath input, boolean followLinks)
|
||||
throws IOException
|
||||
{
|
||||
WindowsFileSystem fs = input.getFileSystem();
|
||||
try {
|
||||
// if not following links then don't need final path
|
||||
if (!followLinks || !fs.supportsLinks())
|
||||
return input.getPathForWin32Calls();
|
||||
|
||||
// if file is not a sym link then don't need final path
|
||||
if (!WindowsFileAttributes.get(input, false).isSymbolicLink()) {
|
||||
return input.getPathForWin32Calls();
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(input);
|
||||
}
|
||||
|
||||
// The file is a symbolic link so attempt to get the final path
|
||||
String result = getFinalPath(input);
|
||||
if (result != null)
|
||||
return result;
|
||||
|
||||
// Fallback: read target of link, resolve against parent, and repeat
|
||||
// until file is not a link.
|
||||
WindowsPath target = input;
|
||||
int linkCount = 0;
|
||||
do {
|
||||
try {
|
||||
WindowsFileAttributes attrs =
|
||||
WindowsFileAttributes.get(target, false);
|
||||
// non a link so we are done
|
||||
if (!attrs.isSymbolicLink()) {
|
||||
return target.getPathForWin32Calls();
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(target);
|
||||
}
|
||||
WindowsPath link = WindowsPath
|
||||
.createFromNormalizedPath(fs, readLink(target));
|
||||
WindowsPath parent = target.getParent();
|
||||
if (parent == null) {
|
||||
// no parent so use parent of absolute path
|
||||
final WindowsPath t = target;
|
||||
target = AccessController
|
||||
.doPrivileged(new PrivilegedAction<WindowsPath>() {
|
||||
@Override
|
||||
public WindowsPath run() {
|
||||
return t.toAbsolutePath();
|
||||
}});
|
||||
parent = target.getParent();
|
||||
}
|
||||
target = parent.resolve(link);
|
||||
|
||||
} while (++linkCount < 32);
|
||||
|
||||
throw new FileSystemException(input.getPathForExceptionMessage(), null,
|
||||
"Too many links");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual path of a file, optionally resolving all symbolic
|
||||
* links.
|
||||
*/
|
||||
static String getRealPath(WindowsPath input, boolean resolveLinks)
|
||||
throws IOException
|
||||
{
|
||||
WindowsFileSystem fs = input.getFileSystem();
|
||||
if (resolveLinks && !fs.supportsLinks())
|
||||
resolveLinks = false;
|
||||
|
||||
// Start with absolute path
|
||||
String path = null;
|
||||
try {
|
||||
path = input.toAbsolutePath().toString();
|
||||
} catch (IOError x) {
|
||||
throw (IOException)(x.getCause());
|
||||
}
|
||||
|
||||
// Collapse "." and ".."
|
||||
if (path.indexOf('.') >= 0) {
|
||||
try {
|
||||
path = GetFullPathName(path);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(input);
|
||||
}
|
||||
}
|
||||
|
||||
// string builder to build up components of path
|
||||
StringBuilder sb = new StringBuilder(path.length());
|
||||
|
||||
// Copy root component
|
||||
int start;
|
||||
char c0 = path.charAt(0);
|
||||
char c1 = path.charAt(1);
|
||||
if ((c0 <= 'z' && c0 >= 'a' || c0 <= 'Z' && c0 >= 'A') &&
|
||||
c1 == ':' && path.charAt(2) == '\\') {
|
||||
// Driver specifier
|
||||
sb.append(Character.toUpperCase(c0));
|
||||
sb.append(":\\");
|
||||
start = 3;
|
||||
} else if (c0 == '\\' && c1 == '\\') {
|
||||
// UNC pathname, begins with "\\\\host\\share"
|
||||
int last = path.length() - 1;
|
||||
int pos = path.indexOf('\\', 2);
|
||||
// skip both server and share names
|
||||
if (pos == -1 || (pos == last)) {
|
||||
// The UNC does not have a share name (collapsed by GetFullPathName)
|
||||
throw new FileSystemException(input.getPathForExceptionMessage(),
|
||||
null, "UNC has invalid share");
|
||||
}
|
||||
pos = path.indexOf('\\', pos+1);
|
||||
if (pos < 0) {
|
||||
pos = last;
|
||||
sb.append(path).append("\\");
|
||||
} else {
|
||||
sb.append(path, 0, pos+1);
|
||||
}
|
||||
start = pos + 1;
|
||||
} else {
|
||||
throw new AssertionError("path type not recognized");
|
||||
}
|
||||
|
||||
// if the result is only a root component then we simply check it exists
|
||||
if (start >= path.length()) {
|
||||
String result = sb.toString();
|
||||
try {
|
||||
GetFileAttributes(result);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// iterate through each component to get its actual name in the
|
||||
// directory
|
||||
int curr = start;
|
||||
while (curr < path.length()) {
|
||||
int next = path.indexOf('\\', curr);
|
||||
int end = (next == -1) ? path.length() : next;
|
||||
String search = sb.toString() + path.substring(curr, end);
|
||||
try {
|
||||
FirstFile fileData = FindFirstFile(WindowsPath.addPrefixIfNeeded(search));
|
||||
FindClose(fileData.handle());
|
||||
|
||||
// if a reparse point is encountered then we must return the
|
||||
// final path.
|
||||
if (resolveLinks &&
|
||||
WindowsFileAttributes.isReparsePoint(fileData.attributes()))
|
||||
{
|
||||
String result = getFinalPath(input);
|
||||
if (result == null) {
|
||||
// Fallback to slow path, usually because there is a sym
|
||||
// link to a file system that doesn't support sym links.
|
||||
WindowsPath resolved = resolveAllLinks(
|
||||
WindowsPath.createFromNormalizedPath(fs, path));
|
||||
result = getRealPath(resolved, false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// add the name to the result
|
||||
sb.append(fileData.name());
|
||||
if (next != -1) {
|
||||
sb.append('\\');
|
||||
}
|
||||
} catch (WindowsException e) {
|
||||
e.rethrowAsIOException(path);
|
||||
}
|
||||
curr = end + 1;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns target of a symbolic link given the handle of an open file
|
||||
* (that should be a link).
|
||||
*/
|
||||
private static String readLinkImpl(long handle) throws IOException {
|
||||
int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
|
||||
NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
|
||||
try {
|
||||
try {
|
||||
DeviceIoControlGetReparsePoint(handle, buffer.address(), size);
|
||||
} catch (WindowsException x) {
|
||||
// FIXME: exception doesn't have file name
|
||||
if (x.lastError() == ERROR_NOT_A_REPARSE_POINT)
|
||||
throw new NotLinkException(null, null, x.errorString());
|
||||
x.rethrowAsIOException((String)null);
|
||||
}
|
||||
|
||||
/*
|
||||
* typedef struct _REPARSE_DATA_BUFFER {
|
||||
* ULONG ReparseTag;
|
||||
* USHORT ReparseDataLength;
|
||||
* USHORT Reserved;
|
||||
* union {
|
||||
* struct {
|
||||
* USHORT SubstituteNameOffset;
|
||||
* USHORT SubstituteNameLength;
|
||||
* USHORT PrintNameOffset;
|
||||
* USHORT PrintNameLength;
|
||||
* WCHAR PathBuffer[1];
|
||||
* } SymbolicLinkReparseBuffer;
|
||||
* struct {
|
||||
* USHORT SubstituteNameOffset;
|
||||
* USHORT SubstituteNameLength;
|
||||
* USHORT PrintNameOffset;
|
||||
* USHORT PrintNameLength;
|
||||
* WCHAR PathBuffer[1];
|
||||
* } MountPointReparseBuffer;
|
||||
* struct {
|
||||
* UCHAR DataBuffer[1];
|
||||
* } GenericReparseBuffer;
|
||||
* };
|
||||
* } REPARSE_DATA_BUFFER
|
||||
*/
|
||||
final short OFFSETOF_REPARSETAG = 0;
|
||||
final short OFFSETOF_PATHOFFSET = 8;
|
||||
final short OFFSETOF_PATHLENGTH = 10;
|
||||
final short OFFSETOF_PATHBUFFER = 16 + 4; // check this
|
||||
|
||||
int tag = (int)unsafe.getLong(buffer.address() + OFFSETOF_REPARSETAG);
|
||||
if (tag != IO_REPARSE_TAG_SYMLINK) {
|
||||
// FIXME: exception doesn't have file name
|
||||
throw new NotLinkException(null, null, "Reparse point is not a symbolic link");
|
||||
}
|
||||
|
||||
// get offset and length of target
|
||||
short nameOffset = unsafe.getShort(buffer.address() + OFFSETOF_PATHOFFSET);
|
||||
short nameLengthInBytes = unsafe.getShort(buffer.address() + OFFSETOF_PATHLENGTH);
|
||||
if ((nameLengthInBytes % 2) != 0)
|
||||
throw new FileSystemException(null, null, "Symbolic link corrupted");
|
||||
|
||||
// copy into char array
|
||||
char[] name = new char[nameLengthInBytes/2];
|
||||
unsafe.copyMemory(null, buffer.address() + OFFSETOF_PATHBUFFER + nameOffset,
|
||||
name, Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);
|
||||
|
||||
// remove special prefix
|
||||
String target = stripPrefix(new String(name));
|
||||
if (target.length() == 0) {
|
||||
throw new IOException("Symbolic link target is invalid");
|
||||
}
|
||||
return target;
|
||||
} finally {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve all symbolic-links in a given absolute and normalized path
|
||||
*/
|
||||
private static WindowsPath resolveAllLinks(WindowsPath path)
|
||||
throws IOException
|
||||
{
|
||||
assert path.isAbsolute();
|
||||
WindowsFileSystem fs = path.getFileSystem();
|
||||
|
||||
// iterate through each name element of the path, resolving links as
|
||||
// we go.
|
||||
int linkCount = 0;
|
||||
int elem = 0;
|
||||
while (elem < path.getNameCount()) {
|
||||
WindowsPath current = path.getRoot().resolve(path.subpath(0, elem+1));
|
||||
|
||||
WindowsFileAttributes attrs = null;
|
||||
try {
|
||||
attrs = WindowsFileAttributes.get(current, false);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(current);
|
||||
}
|
||||
|
||||
/**
|
||||
* If a symbolic link then we resolve it against the parent
|
||||
* of the current name element. We then resolve any remaining
|
||||
* part of the path against the result. The target of the link
|
||||
* may have "." and ".." components so re-normalize and restart
|
||||
* the process from the first element.
|
||||
*/
|
||||
if (attrs.isSymbolicLink()) {
|
||||
linkCount++;
|
||||
if (linkCount > 32)
|
||||
throw new IOException("Too many links");
|
||||
WindowsPath target = WindowsPath
|
||||
.createFromNormalizedPath(fs, readLink(current));
|
||||
WindowsPath remainder = null;
|
||||
int count = path.getNameCount();
|
||||
if ((elem+1) < count) {
|
||||
remainder = path.subpath(elem+1, count);
|
||||
}
|
||||
path = current.getParent().resolve(target);
|
||||
try {
|
||||
String full = GetFullPathName(path.toString());
|
||||
if (!full.equals(path.toString())) {
|
||||
path = WindowsPath.createFromNormalizedPath(fs, full);
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(path);
|
||||
}
|
||||
if (remainder != null) {
|
||||
path = path.resolve(remainder);
|
||||
}
|
||||
|
||||
// reset
|
||||
elem = 0;
|
||||
} else {
|
||||
// not a link
|
||||
elem++;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip long path or symbolic link prefix from path
|
||||
*/
|
||||
private static String stripPrefix(String path) {
|
||||
// prefix for resolved/long path
|
||||
if (path.startsWith("\\\\?\\")) {
|
||||
if (path.startsWith("\\\\?\\UNC\\")) {
|
||||
path = "\\" + path.substring(7);
|
||||
} else {
|
||||
path = path.substring(4);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
// prefix for target of symbolic link
|
||||
if (path.startsWith("\\??\\")) {
|
||||
if (path.startsWith("\\??\\UNC\\")) {
|
||||
path = "\\" + path.substring(7);
|
||||
} else {
|
||||
path = path.substring(4);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
1169
jdkSrc/jdk8/sun/nio/fs/WindowsNativeDispatcher.java
Normal file
1169
jdkSrc/jdk8/sun/nio/fs/WindowsNativeDispatcher.java
Normal file
File diff suppressed because it is too large
Load Diff
877
jdkSrc/jdk8/sun/nio/fs/WindowsPath.java
Normal file
877
jdkSrc/jdk8/sun/nio/fs/WindowsPath.java
Normal file
@@ -0,0 +1,877 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.util.*;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import com.sun.nio.file.ExtendedWatchEventModifier;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
/**
|
||||
* Windows implementation of Path
|
||||
*/
|
||||
|
||||
class WindowsPath extends AbstractPath {
|
||||
|
||||
// The maximum path that does not require long path prefix. On Windows
|
||||
// the maximum path is 260 minus 1 (NUL) but for directories it is 260
|
||||
// minus 12 minus 1 (to allow for the creation of a 8.3 file in the
|
||||
// directory).
|
||||
private static final int MAX_PATH = 247;
|
||||
|
||||
// Maximum extended-length path
|
||||
private static final int MAX_LONG_PATH = 32000;
|
||||
|
||||
// FIXME - eliminate this reference to reduce space
|
||||
private final WindowsFileSystem fs;
|
||||
|
||||
// path type
|
||||
private final WindowsPathType type;
|
||||
// root component (may be empty)
|
||||
private final String root;
|
||||
// normalized path
|
||||
private final String path;
|
||||
|
||||
// the path to use in Win32 calls. This differs from path for relative
|
||||
// paths and has a long path prefix for all paths longer than MAX_PATH.
|
||||
private volatile WeakReference<String> pathForWin32Calls;
|
||||
|
||||
// offsets into name components (computed lazily)
|
||||
private volatile Integer[] offsets;
|
||||
|
||||
// computed hash code (computed lazily, no need to be volatile)
|
||||
private int hash;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes a new instance of this class.
|
||||
*/
|
||||
private WindowsPath(WindowsFileSystem fs,
|
||||
WindowsPathType type,
|
||||
String root,
|
||||
String path)
|
||||
{
|
||||
this.fs = fs;
|
||||
this.type = type;
|
||||
this.root = root;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Path by parsing the given path.
|
||||
*/
|
||||
static WindowsPath parse(WindowsFileSystem fs, String path) {
|
||||
WindowsPathParser.Result result = WindowsPathParser.parse(path);
|
||||
return new WindowsPath(fs, result.type(), result.root(), result.path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Path from a given path that is known to be normalized.
|
||||
*/
|
||||
static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,
|
||||
String path,
|
||||
BasicFileAttributes attrs)
|
||||
{
|
||||
try {
|
||||
WindowsPathParser.Result result =
|
||||
WindowsPathParser.parseNormalizedPath(path);
|
||||
if (attrs == null) {
|
||||
return new WindowsPath(fs,
|
||||
result.type(),
|
||||
result.root(),
|
||||
result.path());
|
||||
} else {
|
||||
return new WindowsPathWithAttributes(fs,
|
||||
result.type(),
|
||||
result.root(),
|
||||
result.path(),
|
||||
attrs);
|
||||
}
|
||||
} catch (InvalidPathException x) {
|
||||
throw new AssertionError(x.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WindowsPath from a given path that is known to be normalized.
|
||||
*/
|
||||
static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,
|
||||
String path)
|
||||
{
|
||||
return createFromNormalizedPath(fs, path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special implementation with attached/cached attributes (used to quicken
|
||||
* file tree traveral)
|
||||
*/
|
||||
private static class WindowsPathWithAttributes
|
||||
extends WindowsPath implements BasicFileAttributesHolder
|
||||
{
|
||||
final WeakReference<BasicFileAttributes> ref;
|
||||
|
||||
WindowsPathWithAttributes(WindowsFileSystem fs,
|
||||
WindowsPathType type,
|
||||
String root,
|
||||
String path,
|
||||
BasicFileAttributes attrs)
|
||||
{
|
||||
super(fs, type, root, path);
|
||||
ref = new WeakReference<BasicFileAttributes>(attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicFileAttributes get() {
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
ref.clear();
|
||||
}
|
||||
|
||||
// no need to override equals/hashCode.
|
||||
}
|
||||
|
||||
// use this message when throwing exceptions
|
||||
String getPathForExceptionMessage() {
|
||||
return path;
|
||||
}
|
||||
|
||||
// use this path for permission checks
|
||||
String getPathForPermissionCheck() {
|
||||
return path;
|
||||
}
|
||||
|
||||
// use this path for Win32 calls
|
||||
// This method will prefix long paths with \\?\ or \\?\UNC as required.
|
||||
String getPathForWin32Calls() throws WindowsException {
|
||||
// short absolute paths can be used directly
|
||||
if (isAbsolute() && path.length() <= MAX_PATH)
|
||||
return path;
|
||||
|
||||
// return cached values if available
|
||||
WeakReference<String> ref = pathForWin32Calls;
|
||||
String resolved = (ref != null) ? ref.get() : null;
|
||||
if (resolved != null) {
|
||||
// Win32 path already available
|
||||
return resolved;
|
||||
}
|
||||
|
||||
// resolve against default directory
|
||||
resolved = getAbsolutePath();
|
||||
|
||||
// Long paths need to have "." and ".." removed and be prefixed with
|
||||
// "\\?\". Note that it is okay to remove ".." even when it follows
|
||||
// a link - for example, it is okay for foo/link/../bar to be changed
|
||||
// to foo/bar. The reason is that Win32 APIs to access foo/link/../bar
|
||||
// will access foo/bar anyway (which differs to Unix systems)
|
||||
if (resolved.length() > MAX_PATH) {
|
||||
if (resolved.length() > MAX_LONG_PATH) {
|
||||
throw new WindowsException("Cannot access file with path exceeding "
|
||||
+ MAX_LONG_PATH + " characters");
|
||||
}
|
||||
resolved = addPrefixIfNeeded(GetFullPathName(resolved));
|
||||
}
|
||||
|
||||
// cache the resolved path (except drive relative paths as the working
|
||||
// directory on removal media devices can change during the lifetime
|
||||
// of the VM)
|
||||
if (type != WindowsPathType.DRIVE_RELATIVE) {
|
||||
synchronized (path) {
|
||||
pathForWin32Calls = new WeakReference<String>(resolved);
|
||||
}
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
// return this path resolved against the file system's default directory
|
||||
private String getAbsolutePath() throws WindowsException {
|
||||
if (isAbsolute())
|
||||
return path;
|
||||
|
||||
// Relative path ("foo" for example)
|
||||
if (type == WindowsPathType.RELATIVE) {
|
||||
String defaultDirectory = getFileSystem().defaultDirectory();
|
||||
if (isEmpty())
|
||||
return defaultDirectory;
|
||||
if (defaultDirectory.endsWith("\\")) {
|
||||
return defaultDirectory + path;
|
||||
} else {
|
||||
StringBuilder sb =
|
||||
new StringBuilder(defaultDirectory.length() + path.length() + 1);
|
||||
return sb.append(defaultDirectory).append('\\').append(path).toString();
|
||||
}
|
||||
}
|
||||
|
||||
// Directory relative path ("\foo" for example)
|
||||
if (type == WindowsPathType.DIRECTORY_RELATIVE) {
|
||||
String defaultRoot = getFileSystem().defaultRoot();
|
||||
return defaultRoot + path.substring(1);
|
||||
}
|
||||
|
||||
// Drive relative path ("C:foo" for example).
|
||||
if (isSameDrive(root, getFileSystem().defaultRoot())) {
|
||||
// relative to default directory
|
||||
String remaining = path.substring(root.length());
|
||||
String defaultDirectory = getFileSystem().defaultDirectory();
|
||||
String result;
|
||||
if (defaultDirectory.endsWith("\\")) {
|
||||
result = defaultDirectory + remaining;
|
||||
} else {
|
||||
result = defaultDirectory + "\\" + remaining;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
// relative to some other drive
|
||||
String wd;
|
||||
try {
|
||||
int dt = GetDriveType(root + "\\");
|
||||
if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR)
|
||||
throw new WindowsException("");
|
||||
wd = GetFullPathName(root + ".");
|
||||
} catch (WindowsException x) {
|
||||
throw new WindowsException("Unable to get working directory of drive '" +
|
||||
Character.toUpperCase(root.charAt(0)) + "'");
|
||||
}
|
||||
String result = wd;
|
||||
if (wd.endsWith("\\")) {
|
||||
result += path.substring(root.length());
|
||||
} else {
|
||||
if (path.length() > root.length())
|
||||
result += "\\" + path.substring(root.length());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// returns true if same drive letter
|
||||
private static boolean isSameDrive(String root1, String root2) {
|
||||
return Character.toUpperCase(root1.charAt(0)) ==
|
||||
Character.toUpperCase(root2.charAt(0));
|
||||
}
|
||||
|
||||
// Add long path prefix to path if required
|
||||
static String addPrefixIfNeeded(String path) {
|
||||
if (path.length() > MAX_PATH) {
|
||||
if (path.startsWith("\\\\")) {
|
||||
path = "\\\\?\\UNC" + path.substring(1, path.length());
|
||||
} else {
|
||||
path = "\\\\?\\" + path;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowsFileSystem getFileSystem() {
|
||||
return fs;
|
||||
}
|
||||
|
||||
// -- Path operations --
|
||||
|
||||
private boolean isEmpty() {
|
||||
return path.length() == 0;
|
||||
}
|
||||
|
||||
private WindowsPath emptyPath() {
|
||||
return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getFileName() {
|
||||
int len = path.length();
|
||||
// represents empty path
|
||||
if (len == 0)
|
||||
return this;
|
||||
// represents root component only
|
||||
if (root.length() == len)
|
||||
return null;
|
||||
int off = path.lastIndexOf('\\');
|
||||
if (off < root.length())
|
||||
off = root.length();
|
||||
else
|
||||
off++;
|
||||
return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", path.substring(off));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowsPath getParent() {
|
||||
// represents root component only
|
||||
if (root.length() == path.length())
|
||||
return null;
|
||||
int off = path.lastIndexOf('\\');
|
||||
if (off < root.length())
|
||||
return getRoot();
|
||||
else
|
||||
return new WindowsPath(getFileSystem(),
|
||||
type,
|
||||
root,
|
||||
path.substring(0, off));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowsPath getRoot() {
|
||||
if (root.length() == 0)
|
||||
return null;
|
||||
return new WindowsPath(getFileSystem(), type, root, root);
|
||||
}
|
||||
|
||||
// package-private
|
||||
WindowsPathType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
// package-private
|
||||
boolean isUnc() {
|
||||
return type == WindowsPathType.UNC;
|
||||
}
|
||||
|
||||
boolean needsSlashWhenResolving() {
|
||||
if (path.endsWith("\\"))
|
||||
return false;
|
||||
return path.length() > root.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAbsolute() {
|
||||
return type == WindowsPathType.ABSOLUTE || type == WindowsPathType.UNC;
|
||||
}
|
||||
|
||||
static WindowsPath toWindowsPath(Path path) {
|
||||
if (path == null)
|
||||
throw new NullPointerException();
|
||||
if (!(path instanceof WindowsPath)) {
|
||||
throw new ProviderMismatchException();
|
||||
}
|
||||
return (WindowsPath)path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowsPath relativize(Path obj) {
|
||||
WindowsPath other = toWindowsPath(obj);
|
||||
if (this.equals(other))
|
||||
return emptyPath();
|
||||
|
||||
// can only relativize paths of the same type
|
||||
if (this.type != other.type)
|
||||
throw new IllegalArgumentException("'other' is different type of Path");
|
||||
|
||||
// can only relativize paths if root component matches
|
||||
if (!this.root.equalsIgnoreCase(other.root))
|
||||
throw new IllegalArgumentException("'other' has different root");
|
||||
|
||||
int bn = this.getNameCount();
|
||||
int cn = other.getNameCount();
|
||||
|
||||
// skip matching names
|
||||
int n = (bn > cn) ? cn : bn;
|
||||
int i = 0;
|
||||
while (i < n) {
|
||||
if (!this.getName(i).equals(other.getName(i)))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
// append ..\ for remaining names in the base
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (int j=i; j<bn; j++) {
|
||||
result.append("..\\");
|
||||
}
|
||||
|
||||
// append remaining names in child
|
||||
for (int j=i; j<cn; j++) {
|
||||
result.append(other.getName(j).toString());
|
||||
result.append("\\");
|
||||
}
|
||||
|
||||
// drop trailing slash in result
|
||||
result.setLength(result.length()-1);
|
||||
return createFromNormalizedPath(getFileSystem(), result.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path normalize() {
|
||||
final int count = getNameCount();
|
||||
if (count == 0 || isEmpty())
|
||||
return this;
|
||||
|
||||
boolean[] ignore = new boolean[count]; // true => ignore name
|
||||
int remaining = count; // number of names remaining
|
||||
|
||||
// multiple passes to eliminate all occurrences of "." and "name/.."
|
||||
int prevRemaining;
|
||||
do {
|
||||
prevRemaining = remaining;
|
||||
int prevName = -1;
|
||||
for (int i=0; i<count; i++) {
|
||||
if (ignore[i])
|
||||
continue;
|
||||
|
||||
String name = elementAsString(i);
|
||||
|
||||
// not "." or ".."
|
||||
if (name.length() > 2) {
|
||||
prevName = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
// "." or something else
|
||||
if (name.length() == 1) {
|
||||
// ignore "."
|
||||
if (name.charAt(0) == '.') {
|
||||
ignore[i] = true;
|
||||
remaining--;
|
||||
} else {
|
||||
prevName = i;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// not ".."
|
||||
if (name.charAt(0) != '.' || name.charAt(1) != '.') {
|
||||
prevName = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
// ".." found
|
||||
if (prevName >= 0) {
|
||||
// name/<ignored>/.. found so mark name and ".." to be
|
||||
// ignored
|
||||
ignore[prevName] = true;
|
||||
ignore[i] = true;
|
||||
remaining = remaining - 2;
|
||||
prevName = -1;
|
||||
} else {
|
||||
// Cases:
|
||||
// C:\<ignored>\..
|
||||
// \\server\\share\<ignored>\..
|
||||
// \<ignored>..
|
||||
if (isAbsolute() || type == WindowsPathType.DIRECTORY_RELATIVE) {
|
||||
boolean hasPrevious = false;
|
||||
for (int j=0; j<i; j++) {
|
||||
if (!ignore[j]) {
|
||||
hasPrevious = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasPrevious) {
|
||||
// all proceeding names are ignored
|
||||
ignore[i] = true;
|
||||
remaining--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (prevRemaining > remaining);
|
||||
|
||||
// no redundant names
|
||||
if (remaining == count)
|
||||
return this;
|
||||
|
||||
// corner case - all names removed
|
||||
if (remaining == 0) {
|
||||
return (root.length() == 0) ? emptyPath() : getRoot();
|
||||
}
|
||||
|
||||
// re-constitute the path from the remaining names.
|
||||
StringBuilder result = new StringBuilder();
|
||||
if (root != null)
|
||||
result.append(root);
|
||||
for (int i=0; i<count; i++) {
|
||||
if (!ignore[i]) {
|
||||
result.append(getName(i));
|
||||
result.append("\\");
|
||||
}
|
||||
}
|
||||
|
||||
// drop trailing slash in result
|
||||
result.setLength(result.length()-1);
|
||||
return createFromNormalizedPath(getFileSystem(), result.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowsPath resolve(Path obj) {
|
||||
WindowsPath other = toWindowsPath(obj);
|
||||
if (other.isEmpty())
|
||||
return this;
|
||||
if (other.isAbsolute())
|
||||
return other;
|
||||
|
||||
switch (other.type) {
|
||||
case RELATIVE: {
|
||||
String result;
|
||||
if (path.endsWith("\\") || (root.length() == path.length())) {
|
||||
result = path + other.path;
|
||||
} else {
|
||||
result = path + "\\" + other.path;
|
||||
}
|
||||
return new WindowsPath(getFileSystem(), type, root, result);
|
||||
}
|
||||
|
||||
case DIRECTORY_RELATIVE: {
|
||||
String result;
|
||||
if (root.endsWith("\\")) {
|
||||
result = root + other.path.substring(1);
|
||||
} else {
|
||||
result = root + other.path;
|
||||
}
|
||||
return createFromNormalizedPath(getFileSystem(), result);
|
||||
}
|
||||
|
||||
case DRIVE_RELATIVE: {
|
||||
if (!root.endsWith("\\"))
|
||||
return other;
|
||||
// if different roots then return other
|
||||
String thisRoot = root.substring(0, root.length()-1);
|
||||
if (!thisRoot.equalsIgnoreCase(other.root))
|
||||
return other;
|
||||
// same roots
|
||||
String remaining = other.path.substring(other.root.length());
|
||||
String result;
|
||||
if (path.endsWith("\\")) {
|
||||
result = path + remaining;
|
||||
} else {
|
||||
result = path + "\\" + remaining;
|
||||
}
|
||||
return createFromNormalizedPath(getFileSystem(), result);
|
||||
}
|
||||
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
// generate offset array
|
||||
private void initOffsets() {
|
||||
if (offsets == null) {
|
||||
ArrayList<Integer> list = new ArrayList<>();
|
||||
if (isEmpty()) {
|
||||
// empty path considered to have one name element
|
||||
list.add(0);
|
||||
} else {
|
||||
int start = root.length();
|
||||
int off = root.length();
|
||||
while (off < path.length()) {
|
||||
if (path.charAt(off) != '\\') {
|
||||
off++;
|
||||
} else {
|
||||
list.add(start);
|
||||
start = ++off;
|
||||
}
|
||||
}
|
||||
if (start != off)
|
||||
list.add(start);
|
||||
}
|
||||
synchronized (this) {
|
||||
if (offsets == null)
|
||||
offsets = list.toArray(new Integer[list.size()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNameCount() {
|
||||
initOffsets();
|
||||
return offsets.length;
|
||||
}
|
||||
|
||||
private String elementAsString(int i) {
|
||||
initOffsets();
|
||||
if (i == (offsets.length-1))
|
||||
return path.substring(offsets[i]);
|
||||
return path.substring(offsets[i], offsets[i+1]-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowsPath getName(int index) {
|
||||
initOffsets();
|
||||
if (index < 0 || index >= offsets.length)
|
||||
throw new IllegalArgumentException();
|
||||
return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", elementAsString(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowsPath subpath(int beginIndex, int endIndex) {
|
||||
initOffsets();
|
||||
if (beginIndex < 0)
|
||||
throw new IllegalArgumentException();
|
||||
if (beginIndex >= offsets.length)
|
||||
throw new IllegalArgumentException();
|
||||
if (endIndex > offsets.length)
|
||||
throw new IllegalArgumentException();
|
||||
if (beginIndex >= endIndex)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Integer[] nelems = new Integer[endIndex - beginIndex];
|
||||
for (int i = beginIndex; i < endIndex; i++) {
|
||||
nelems[i-beginIndex] = sb.length();
|
||||
sb.append(elementAsString(i));
|
||||
if (i != (endIndex-1))
|
||||
sb.append("\\");
|
||||
}
|
||||
return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startsWith(Path obj) {
|
||||
if (!(Objects.requireNonNull(obj) instanceof WindowsPath))
|
||||
return false;
|
||||
WindowsPath other = (WindowsPath)obj;
|
||||
|
||||
// if this path has a root component the given path's root must match
|
||||
if (!this.root.equalsIgnoreCase(other.root)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// empty path starts with itself
|
||||
if (other.isEmpty())
|
||||
return this.isEmpty();
|
||||
|
||||
// roots match so compare elements
|
||||
int thisCount = getNameCount();
|
||||
int otherCount = other.getNameCount();
|
||||
if (otherCount <= thisCount) {
|
||||
while (--otherCount >= 0) {
|
||||
String thisElement = this.elementAsString(otherCount);
|
||||
String otherElement = other.elementAsString(otherCount);
|
||||
// FIXME: should compare in uppercase
|
||||
if (!thisElement.equalsIgnoreCase(otherElement))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean endsWith(Path obj) {
|
||||
if (!(Objects.requireNonNull(obj) instanceof WindowsPath))
|
||||
return false;
|
||||
WindowsPath other = (WindowsPath)obj;
|
||||
|
||||
// other path is longer
|
||||
if (other.path.length() > this.path.length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// empty path ends in itself
|
||||
if (other.isEmpty()) {
|
||||
return this.isEmpty();
|
||||
}
|
||||
|
||||
int thisCount = this.getNameCount();
|
||||
int otherCount = other.getNameCount();
|
||||
|
||||
// given path has more elements that this path
|
||||
if (otherCount > thisCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// compare roots
|
||||
if (other.root.length() > 0) {
|
||||
if (otherCount < thisCount)
|
||||
return false;
|
||||
// FIXME: should compare in uppercase
|
||||
if (!this.root.equalsIgnoreCase(other.root))
|
||||
return false;
|
||||
}
|
||||
|
||||
// match last 'otherCount' elements
|
||||
int off = thisCount - otherCount;
|
||||
while (--otherCount >= 0) {
|
||||
String thisElement = this.elementAsString(off + otherCount);
|
||||
String otherElement = other.elementAsString(otherCount);
|
||||
// FIXME: should compare in uppercase
|
||||
if (!thisElement.equalsIgnoreCase(otherElement))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Path obj) {
|
||||
if (obj == null)
|
||||
throw new NullPointerException();
|
||||
String s1 = path;
|
||||
String s2 = ((WindowsPath)obj).path;
|
||||
int n1 = s1.length();
|
||||
int n2 = s2.length();
|
||||
int min = Math.min(n1, n2);
|
||||
for (int i = 0; i < min; i++) {
|
||||
char c1 = s1.charAt(i);
|
||||
char c2 = s2.charAt(i);
|
||||
if (c1 != c2) {
|
||||
c1 = Character.toUpperCase(c1);
|
||||
c2 = Character.toUpperCase(c2);
|
||||
if (c1 != c2) {
|
||||
return c1 - c2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return n1 - n2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ((obj != null) && (obj instanceof WindowsPath)) {
|
||||
return compareTo((Path)obj) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// OK if two or more threads compute hash
|
||||
int h = hash;
|
||||
if (h == 0) {
|
||||
for (int i = 0; i< path.length(); i++) {
|
||||
h = 31*h + Character.toUpperCase(path.charAt(i));
|
||||
}
|
||||
hash = h;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return path;
|
||||
}
|
||||
|
||||
// -- file operations --
|
||||
|
||||
// package-private
|
||||
long openForReadAttributeAccess(boolean followLinks)
|
||||
throws WindowsException
|
||||
{
|
||||
int flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||||
if (!followLinks && getFileSystem().supportsLinks())
|
||||
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
|
||||
return CreateFile(getPathForWin32Calls(),
|
||||
FILE_READ_ATTRIBUTES,
|
||||
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
|
||||
0L,
|
||||
OPEN_EXISTING,
|
||||
flags);
|
||||
}
|
||||
|
||||
void checkRead() {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkRead(getPathForPermissionCheck());
|
||||
}
|
||||
}
|
||||
|
||||
void checkWrite() {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkWrite(getPathForPermissionCheck());
|
||||
}
|
||||
}
|
||||
|
||||
void checkDelete() {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkDelete(getPathForPermissionCheck());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI toUri() {
|
||||
return WindowsUriSupport.toUri(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowsPath toAbsolutePath() {
|
||||
if (isAbsolute())
|
||||
return this;
|
||||
|
||||
// permission check as per spec
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPropertyAccess("user.dir");
|
||||
}
|
||||
|
||||
try {
|
||||
return createFromNormalizedPath(getFileSystem(), getAbsolutePath());
|
||||
} catch (WindowsException x) {
|
||||
throw new IOError(new IOException(x.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowsPath toRealPath(LinkOption... options) throws IOException {
|
||||
checkRead();
|
||||
String rp = WindowsLinkSupport.getRealPath(this, Util.followLinks(options));
|
||||
return createFromNormalizedPath(getFileSystem(), rp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WatchKey register(WatchService watcher,
|
||||
WatchEvent.Kind<?>[] events,
|
||||
WatchEvent.Modifier... modifiers)
|
||||
throws IOException
|
||||
{
|
||||
if (watcher == null)
|
||||
throw new NullPointerException();
|
||||
if (!(watcher instanceof WindowsWatchService))
|
||||
throw new ProviderMismatchException();
|
||||
|
||||
// When a security manager is set then we need to make a defensive
|
||||
// copy of the modifiers and check for the Windows specific FILE_TREE
|
||||
// modifier. When the modifier is present then check that permission
|
||||
// has been granted recursively.
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
boolean watchSubtree = false;
|
||||
final int ml = modifiers.length;
|
||||
if (ml > 0) {
|
||||
modifiers = Arrays.copyOf(modifiers, ml);
|
||||
int i=0;
|
||||
while (i < ml) {
|
||||
if (modifiers[i++] == ExtendedWatchEventModifier.FILE_TREE) {
|
||||
watchSubtree = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
String s = getPathForPermissionCheck();
|
||||
sm.checkRead(s);
|
||||
if (watchSubtree)
|
||||
sm.checkRead(s + "\\-");
|
||||
}
|
||||
|
||||
return ((WindowsWatchService)watcher).register(this, events, modifiers);
|
||||
}
|
||||
}
|
||||
229
jdkSrc/jdk8/sun/nio/fs/WindowsPathParser.java
Normal file
229
jdkSrc/jdk8/sun/nio/fs/WindowsPathParser.java
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.InvalidPathException;
|
||||
|
||||
/**
|
||||
* A parser of Windows path strings
|
||||
*/
|
||||
|
||||
class WindowsPathParser {
|
||||
private WindowsPathParser() { }
|
||||
|
||||
/**
|
||||
* The result of a parse operation
|
||||
*/
|
||||
static class Result {
|
||||
private final WindowsPathType type;
|
||||
private final String root;
|
||||
private final String path;
|
||||
|
||||
Result(WindowsPathType type, String root, String path) {
|
||||
this.type = type;
|
||||
this.root = root;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path type
|
||||
*/
|
||||
WindowsPathType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The root component
|
||||
*/
|
||||
String root() {
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* The normalized path (includes root)
|
||||
*/
|
||||
String path() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given input as a Windows path
|
||||
*/
|
||||
static Result parse(String input) {
|
||||
return parse(input, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given input as a Windows path where it is known that the
|
||||
* path is already normalized.
|
||||
*/
|
||||
static Result parseNormalizedPath(String input) {
|
||||
return parse(input, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given input as a Windows path.
|
||||
*
|
||||
* @param requireToNormalize
|
||||
* Indicates if the path requires to be normalized
|
||||
*/
|
||||
private static Result parse(String input, boolean requireToNormalize) {
|
||||
String root = "";
|
||||
WindowsPathType type = null;
|
||||
|
||||
int len = input.length();
|
||||
int off = 0;
|
||||
if (len > 1) {
|
||||
char c0 = input.charAt(0);
|
||||
char c1 = input.charAt(1);
|
||||
char c = 0;
|
||||
int next = 2;
|
||||
if (isSlash(c0) && isSlash(c1)) {
|
||||
// UNC: We keep the first two slash, collapse all the
|
||||
// following, then take the hostname and share name out,
|
||||
// meanwhile collapsing all the redundant slashes.
|
||||
type = WindowsPathType.UNC;
|
||||
off = nextNonSlash(input, next, len);
|
||||
next = nextSlash(input, off, len);
|
||||
if (off == next)
|
||||
throw new InvalidPathException(input, "UNC path is missing hostname");
|
||||
String host = input.substring(off, next); //host
|
||||
off = nextNonSlash(input, next, len);
|
||||
next = nextSlash(input, off, len);
|
||||
if (off == next)
|
||||
throw new InvalidPathException(input, "UNC path is missing sharename");
|
||||
root = "\\\\" + host + "\\" + input.substring(off, next) + "\\";
|
||||
off = next;
|
||||
} else {
|
||||
if (isLetter(c0) && c1 == ':') {
|
||||
char c2;
|
||||
if (len > 2 && isSlash(c2 = input.charAt(2))) {
|
||||
// avoid concatenation when root is "D:\"
|
||||
if (c2 == '\\') {
|
||||
root = input.substring(0, 3);
|
||||
} else {
|
||||
root = input.substring(0, 2) + '\\';
|
||||
}
|
||||
off = 3;
|
||||
type = WindowsPathType.ABSOLUTE;
|
||||
} else {
|
||||
root = input.substring(0, 2);
|
||||
off = 2;
|
||||
type = WindowsPathType.DRIVE_RELATIVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (off == 0) {
|
||||
if (len > 0 && isSlash(input.charAt(0))) {
|
||||
type = WindowsPathType.DIRECTORY_RELATIVE;
|
||||
root = "\\";
|
||||
} else {
|
||||
type = WindowsPathType.RELATIVE;
|
||||
}
|
||||
}
|
||||
|
||||
if (requireToNormalize) {
|
||||
StringBuilder sb = new StringBuilder(input.length());
|
||||
sb.append(root);
|
||||
return new Result(type, root, normalize(sb, input, off));
|
||||
} else {
|
||||
return new Result(type, root, input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove redundant slashes from the rest of the path, forcing all slashes
|
||||
* into the preferred slash.
|
||||
*/
|
||||
private static String normalize(StringBuilder sb, String path, int off) {
|
||||
int len = path.length();
|
||||
off = nextNonSlash(path, off, len);
|
||||
int start = off;
|
||||
char lastC = 0;
|
||||
while (off < len) {
|
||||
char c = path.charAt(off);
|
||||
if (isSlash(c)) {
|
||||
if (lastC == ' ')
|
||||
throw new InvalidPathException(path,
|
||||
"Trailing char <" + lastC + ">",
|
||||
off - 1);
|
||||
sb.append(path, start, off);
|
||||
off = nextNonSlash(path, off, len);
|
||||
if (off != len) //no slash at the end of normalized path
|
||||
sb.append('\\');
|
||||
start = off;
|
||||
} else {
|
||||
if (isInvalidPathChar(c))
|
||||
throw new InvalidPathException(path,
|
||||
"Illegal char <" + c + ">",
|
||||
off);
|
||||
lastC = c;
|
||||
off++;
|
||||
}
|
||||
}
|
||||
if (start != off) {
|
||||
if (lastC == ' ')
|
||||
throw new InvalidPathException(path,
|
||||
"Trailing char <" + lastC + ">",
|
||||
off - 1);
|
||||
sb.append(path, start, off);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static final boolean isSlash(char c) {
|
||||
return (c == '\\') || (c == '/');
|
||||
}
|
||||
|
||||
private static final int nextNonSlash(String path, int off, int end) {
|
||||
while (off < end && isSlash(path.charAt(off))) { off++; }
|
||||
return off;
|
||||
}
|
||||
|
||||
private static final int nextSlash(String path, int off, int end) {
|
||||
char c;
|
||||
while (off < end && !isSlash(c=path.charAt(off))) {
|
||||
if (isInvalidPathChar(c))
|
||||
throw new InvalidPathException(path,
|
||||
"Illegal character [" + c + "] in path",
|
||||
off);
|
||||
off++;
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
private static final boolean isLetter(char c) {
|
||||
return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
|
||||
}
|
||||
|
||||
// Reserved characters for window path name
|
||||
private static final String reservedChars = "<>:\"|?*";
|
||||
private static final boolean isInvalidPathChar(char ch) {
|
||||
return ch < '\u0020' || reservedChars.indexOf(ch) != -1;
|
||||
}
|
||||
}
|
||||
38
jdkSrc/jdk8/sun/nio/fs/WindowsPathType.java
Normal file
38
jdkSrc/jdk8/sun/nio/fs/WindowsPathType.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
/**
|
||||
* A type safe enum of Windows path types.
|
||||
*/
|
||||
|
||||
enum WindowsPathType {
|
||||
ABSOLUTE, // C:\foo
|
||||
UNC, // \\server\share\foo
|
||||
RELATIVE, // foo
|
||||
DIRECTORY_RELATIVE, // \foo
|
||||
DRIVE_RELATIVE // C:foo
|
||||
}
|
||||
151
jdkSrc/jdk8/sun/nio/fs/WindowsSecurity.java
Normal file
151
jdkSrc/jdk8/sun/nio/fs/WindowsSecurity.java
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
/**
|
||||
* Security related utility methods.
|
||||
*/
|
||||
|
||||
class WindowsSecurity {
|
||||
private WindowsSecurity() { }
|
||||
|
||||
// opens process token for given access
|
||||
private static long openProcessToken(int access) {
|
||||
try {
|
||||
return OpenProcessToken(GetCurrentProcess(), access);
|
||||
} catch (WindowsException x) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the access token for this process with TOKEN_DUPLICATE access
|
||||
*/
|
||||
static final long processTokenWithDuplicateAccess =
|
||||
openProcessToken(TOKEN_DUPLICATE);
|
||||
|
||||
/**
|
||||
* Returns the access token for this process with TOKEN_QUERY access
|
||||
*/
|
||||
static final long processTokenWithQueryAccess =
|
||||
openProcessToken(TOKEN_QUERY);
|
||||
|
||||
/**
|
||||
* Returned by enablePrivilege when code may require a given privilege.
|
||||
* The drop method should be invoked after the operation completes so as
|
||||
* to revert the privilege.
|
||||
*/
|
||||
static interface Privilege {
|
||||
void drop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to enable the given privilege for this method.
|
||||
*/
|
||||
static Privilege enablePrivilege(String priv) {
|
||||
final long pLuid;
|
||||
try {
|
||||
pLuid = LookupPrivilegeValue(priv);
|
||||
} catch (WindowsException x) {
|
||||
// indicates bug in caller
|
||||
throw new AssertionError(x);
|
||||
}
|
||||
|
||||
long hToken = 0L;
|
||||
boolean impersontating = false;
|
||||
boolean elevated = false;
|
||||
try {
|
||||
hToken = OpenThreadToken(GetCurrentThread(),
|
||||
TOKEN_ADJUST_PRIVILEGES, false);
|
||||
if (hToken == 0L && processTokenWithDuplicateAccess != 0L) {
|
||||
hToken = DuplicateTokenEx(processTokenWithDuplicateAccess,
|
||||
(TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE));
|
||||
SetThreadToken(0L, hToken);
|
||||
impersontating = true;
|
||||
}
|
||||
|
||||
if (hToken != 0L) {
|
||||
AdjustTokenPrivileges(hToken, pLuid, SE_PRIVILEGE_ENABLED);
|
||||
elevated = true;
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
// nothing to do, privilege not enabled
|
||||
}
|
||||
|
||||
final long token = hToken;
|
||||
final boolean stopImpersontating = impersontating;
|
||||
final boolean needToRevert = elevated;
|
||||
|
||||
return () -> {
|
||||
try {
|
||||
if (token != 0L) {
|
||||
try {
|
||||
if (stopImpersontating)
|
||||
SetThreadToken(0L, 0L);
|
||||
else if (needToRevert)
|
||||
AdjustTokenPrivileges(token, pLuid, 0);
|
||||
} catch (WindowsException x) {
|
||||
// should not happen
|
||||
throw new AssertionError(x);
|
||||
} finally {
|
||||
CloseHandle(token);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
LocalFree(pLuid);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the access right against the securityInfo in the current thread.
|
||||
*/
|
||||
static boolean checkAccessMask(long securityInfo, int accessMask,
|
||||
int genericRead, int genericWrite, int genericExecute, int genericAll)
|
||||
throws WindowsException
|
||||
{
|
||||
int privilegies = TOKEN_QUERY;
|
||||
long hToken = OpenThreadToken(GetCurrentThread(), privilegies, false);
|
||||
if (hToken == 0L && processTokenWithDuplicateAccess != 0L)
|
||||
hToken = DuplicateTokenEx(processTokenWithDuplicateAccess,
|
||||
privilegies);
|
||||
|
||||
boolean hasRight = false;
|
||||
if (hToken != 0L) {
|
||||
try {
|
||||
hasRight = AccessCheck(hToken, securityInfo, accessMask,
|
||||
genericRead, genericWrite, genericExecute, genericAll);
|
||||
} finally {
|
||||
CloseHandle(hToken);
|
||||
}
|
||||
}
|
||||
return hasRight;
|
||||
}
|
||||
|
||||
}
|
||||
392
jdkSrc/jdk8/sun/nio/fs/WindowsSecurityDescriptor.java
Normal file
392
jdkSrc/jdk8/sun/nio/fs/WindowsSecurityDescriptor.java
Normal file
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.ProviderMismatchException;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
/**
|
||||
* A SecurityDescriptor for use when setting a file's ACL or creating a file
|
||||
* with an initial ACL.
|
||||
*/
|
||||
|
||||
class WindowsSecurityDescriptor {
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
/**
|
||||
* typedef struct _ACL {
|
||||
* BYTE AclRevision;
|
||||
* BYTE Sbz1;
|
||||
* WORD AclSize;
|
||||
* WORD AceCount;
|
||||
* WORD Sbz2;
|
||||
* } ACL;
|
||||
*
|
||||
* typedef struct _ACE_HEADER {
|
||||
* BYTE AceType;
|
||||
* BYTE AceFlags;
|
||||
* WORD AceSize;
|
||||
* } ACE_HEADER;
|
||||
*
|
||||
* typedef struct _ACCESS_ALLOWED_ACE {
|
||||
* ACE_HEADER Header;
|
||||
* ACCESS_MASK Mask;
|
||||
* DWORD SidStart;
|
||||
* } ACCESS_ALLOWED_ACE;
|
||||
*
|
||||
* typedef struct _ACCESS_DENIED_ACE {
|
||||
* ACE_HEADER Header;
|
||||
* ACCESS_MASK Mask;
|
||||
* DWORD SidStart;
|
||||
* } ACCESS_DENIED_ACE;
|
||||
*
|
||||
* typedef struct _SECURITY_DESCRIPTOR {
|
||||
* BYTE Revision;
|
||||
* BYTE Sbz1;
|
||||
* SECURITY_DESCRIPTOR_CONTROL Control;
|
||||
* PSID Owner;
|
||||
* PSID Group;
|
||||
* PACL Sacl;
|
||||
* PACL Dacl;
|
||||
* } SECURITY_DESCRIPTOR;
|
||||
*/
|
||||
private static final short SIZEOF_ACL = 8;
|
||||
private static final short SIZEOF_ACCESS_ALLOWED_ACE = 12;
|
||||
private static final short SIZEOF_ACCESS_DENIED_ACE = 12;
|
||||
private static final short SIZEOF_SECURITY_DESCRIPTOR = 20;
|
||||
|
||||
private static final short OFFSETOF_TYPE = 0;
|
||||
private static final short OFFSETOF_FLAGS = 1;
|
||||
private static final short OFFSETOF_ACCESS_MASK = 4;
|
||||
private static final short OFFSETOF_SID = 8;
|
||||
|
||||
// null security descriptor
|
||||
private static final WindowsSecurityDescriptor NULL_DESCRIPTOR =
|
||||
new WindowsSecurityDescriptor();
|
||||
|
||||
// native resources
|
||||
private final List<Long> sidList;
|
||||
private final NativeBuffer aclBuffer, sdBuffer;
|
||||
|
||||
/**
|
||||
* Creates the "null" SecurityDescriptor
|
||||
*/
|
||||
private WindowsSecurityDescriptor() {
|
||||
this.sidList = null;
|
||||
this.aclBuffer = null;
|
||||
this.sdBuffer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SecurityDescriptor from the given ACL
|
||||
*/
|
||||
private WindowsSecurityDescriptor(List<AclEntry> acl) throws IOException {
|
||||
boolean initialized = false;
|
||||
|
||||
// SECURITY: need to copy list in case size changes during processing
|
||||
acl = new ArrayList<AclEntry>(acl);
|
||||
|
||||
// list of SIDs
|
||||
sidList = new ArrayList<Long>(acl.size());
|
||||
try {
|
||||
// initial size of ACL
|
||||
int size = SIZEOF_ACL;
|
||||
|
||||
// get the SID for each entry
|
||||
for (AclEntry entry: acl) {
|
||||
UserPrincipal user = entry.principal();
|
||||
if (!(user instanceof WindowsUserPrincipals.User))
|
||||
throw new ProviderMismatchException();
|
||||
String sidString = ((WindowsUserPrincipals.User)user).sidString();
|
||||
try {
|
||||
long pSid = ConvertStringSidToSid(sidString);
|
||||
sidList.add(pSid);
|
||||
|
||||
// increase size to allow for entry
|
||||
size += GetLengthSid(pSid) +
|
||||
Math.max(SIZEOF_ACCESS_ALLOWED_ACE, SIZEOF_ACCESS_DENIED_ACE);
|
||||
|
||||
} catch (WindowsException x) {
|
||||
throw new IOException("Failed to get SID for " + user.getName()
|
||||
+ ": " + x.errorString());
|
||||
}
|
||||
}
|
||||
|
||||
// allocate memory for the ACL
|
||||
aclBuffer = NativeBuffers.getNativeBuffer(size);
|
||||
sdBuffer = NativeBuffers.getNativeBuffer(SIZEOF_SECURITY_DESCRIPTOR);
|
||||
|
||||
InitializeAcl(aclBuffer.address(), size);
|
||||
|
||||
// Add entry ACE to the ACL
|
||||
int i = 0;
|
||||
while (i < acl.size()) {
|
||||
AclEntry entry = acl.get(i);
|
||||
long pSid = sidList.get(i);
|
||||
try {
|
||||
encode(entry, pSid, aclBuffer.address());
|
||||
} catch (WindowsException x) {
|
||||
throw new IOException("Failed to encode ACE: " +
|
||||
x.errorString());
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// initialize security descriptor and set DACL
|
||||
InitializeSecurityDescriptor(sdBuffer.address());
|
||||
SetSecurityDescriptorDacl(sdBuffer.address(), aclBuffer.address());
|
||||
initialized = true;
|
||||
} catch (WindowsException x) {
|
||||
throw new IOException(x.getMessage());
|
||||
} finally {
|
||||
// release resources if not completely initialized
|
||||
if (!initialized)
|
||||
release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases memory associated with SecurityDescriptor
|
||||
*/
|
||||
void release() {
|
||||
if (sdBuffer != null)
|
||||
sdBuffer.release();
|
||||
if (aclBuffer != null)
|
||||
aclBuffer.release();
|
||||
if (sidList != null) {
|
||||
// release memory for SIDs
|
||||
for (Long sid: sidList) {
|
||||
LocalFree(sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns address of SecurityDescriptor
|
||||
*/
|
||||
long address() {
|
||||
return (sdBuffer == null) ? 0L : sdBuffer.address();
|
||||
}
|
||||
|
||||
// decode Windows ACE to NFSv4 AclEntry
|
||||
private static AclEntry decode(long aceAddress)
|
||||
throws IOException
|
||||
{
|
||||
// map type
|
||||
byte aceType = unsafe.getByte(aceAddress + OFFSETOF_TYPE);
|
||||
if (aceType != ACCESS_ALLOWED_ACE_TYPE && aceType != ACCESS_DENIED_ACE_TYPE)
|
||||
return null;
|
||||
AclEntryType type;
|
||||
if (aceType == ACCESS_ALLOWED_ACE_TYPE) {
|
||||
type = AclEntryType.ALLOW;
|
||||
} else {
|
||||
type = AclEntryType.DENY;
|
||||
}
|
||||
|
||||
// map flags
|
||||
byte aceFlags = unsafe.getByte(aceAddress + OFFSETOF_FLAGS);
|
||||
Set<AclEntryFlag> flags = EnumSet.noneOf(AclEntryFlag.class);
|
||||
if ((aceFlags & OBJECT_INHERIT_ACE) != 0)
|
||||
flags.add(AclEntryFlag.FILE_INHERIT);
|
||||
if ((aceFlags & CONTAINER_INHERIT_ACE) != 0)
|
||||
flags.add(AclEntryFlag.DIRECTORY_INHERIT);
|
||||
if ((aceFlags & NO_PROPAGATE_INHERIT_ACE) != 0)
|
||||
flags.add(AclEntryFlag.NO_PROPAGATE_INHERIT);
|
||||
if ((aceFlags & INHERIT_ONLY_ACE) != 0)
|
||||
flags.add(AclEntryFlag.INHERIT_ONLY);
|
||||
|
||||
// map access mask
|
||||
int mask = unsafe.getInt(aceAddress + OFFSETOF_ACCESS_MASK);
|
||||
Set<AclEntryPermission> perms = EnumSet.noneOf(AclEntryPermission.class);
|
||||
if ((mask & FILE_READ_DATA) > 0)
|
||||
perms.add(AclEntryPermission.READ_DATA);
|
||||
if ((mask & FILE_WRITE_DATA) > 0)
|
||||
perms.add(AclEntryPermission.WRITE_DATA);
|
||||
if ((mask & FILE_APPEND_DATA ) > 0)
|
||||
perms.add(AclEntryPermission.APPEND_DATA);
|
||||
if ((mask & FILE_READ_EA) > 0)
|
||||
perms.add(AclEntryPermission.READ_NAMED_ATTRS);
|
||||
if ((mask & FILE_WRITE_EA) > 0)
|
||||
perms.add(AclEntryPermission.WRITE_NAMED_ATTRS);
|
||||
if ((mask & FILE_EXECUTE) > 0)
|
||||
perms.add(AclEntryPermission.EXECUTE);
|
||||
if ((mask & FILE_DELETE_CHILD ) > 0)
|
||||
perms.add(AclEntryPermission.DELETE_CHILD);
|
||||
if ((mask & FILE_READ_ATTRIBUTES) > 0)
|
||||
perms.add(AclEntryPermission.READ_ATTRIBUTES);
|
||||
if ((mask & FILE_WRITE_ATTRIBUTES) > 0)
|
||||
perms.add(AclEntryPermission.WRITE_ATTRIBUTES);
|
||||
if ((mask & DELETE) > 0)
|
||||
perms.add(AclEntryPermission.DELETE);
|
||||
if ((mask & READ_CONTROL) > 0)
|
||||
perms.add(AclEntryPermission.READ_ACL);
|
||||
if ((mask & WRITE_DAC) > 0)
|
||||
perms.add(AclEntryPermission.WRITE_ACL);
|
||||
if ((mask & WRITE_OWNER) > 0)
|
||||
perms.add(AclEntryPermission.WRITE_OWNER);
|
||||
if ((mask & SYNCHRONIZE) > 0)
|
||||
perms.add(AclEntryPermission.SYNCHRONIZE);
|
||||
|
||||
// lookup SID to create UserPrincipal
|
||||
long sidAddress = aceAddress + OFFSETOF_SID;
|
||||
UserPrincipal user = WindowsUserPrincipals.fromSid(sidAddress);
|
||||
|
||||
return AclEntry.newBuilder()
|
||||
.setType(type)
|
||||
.setPrincipal(user)
|
||||
.setFlags(flags).setPermissions(perms).build();
|
||||
}
|
||||
|
||||
// encode NFSv4 AclEntry as Windows ACE to given ACL
|
||||
private static void encode(AclEntry ace, long sidAddress, long aclAddress)
|
||||
throws WindowsException
|
||||
{
|
||||
// ignore non-allow/deny entries for now
|
||||
if (ace.type() != AclEntryType.ALLOW && ace.type() != AclEntryType.DENY)
|
||||
return;
|
||||
boolean allow = (ace.type() == AclEntryType.ALLOW);
|
||||
|
||||
// map access mask
|
||||
Set<AclEntryPermission> aceMask = ace.permissions();
|
||||
int mask = 0;
|
||||
if (aceMask.contains(AclEntryPermission.READ_DATA))
|
||||
mask |= FILE_READ_DATA;
|
||||
if (aceMask.contains(AclEntryPermission.WRITE_DATA))
|
||||
mask |= FILE_WRITE_DATA;
|
||||
if (aceMask.contains(AclEntryPermission.APPEND_DATA))
|
||||
mask |= FILE_APPEND_DATA;
|
||||
if (aceMask.contains(AclEntryPermission.READ_NAMED_ATTRS))
|
||||
mask |= FILE_READ_EA;
|
||||
if (aceMask.contains(AclEntryPermission.WRITE_NAMED_ATTRS))
|
||||
mask |= FILE_WRITE_EA;
|
||||
if (aceMask.contains(AclEntryPermission.EXECUTE))
|
||||
mask |= FILE_EXECUTE;
|
||||
if (aceMask.contains(AclEntryPermission.DELETE_CHILD))
|
||||
mask |= FILE_DELETE_CHILD;
|
||||
if (aceMask.contains(AclEntryPermission.READ_ATTRIBUTES))
|
||||
mask |= FILE_READ_ATTRIBUTES;
|
||||
if (aceMask.contains(AclEntryPermission.WRITE_ATTRIBUTES))
|
||||
mask |= FILE_WRITE_ATTRIBUTES;
|
||||
if (aceMask.contains(AclEntryPermission.DELETE))
|
||||
mask |= DELETE;
|
||||
if (aceMask.contains(AclEntryPermission.READ_ACL))
|
||||
mask |= READ_CONTROL;
|
||||
if (aceMask.contains(AclEntryPermission.WRITE_ACL))
|
||||
mask |= WRITE_DAC;
|
||||
if (aceMask.contains(AclEntryPermission.WRITE_OWNER))
|
||||
mask |= WRITE_OWNER;
|
||||
if (aceMask.contains(AclEntryPermission.SYNCHRONIZE))
|
||||
mask |= SYNCHRONIZE;
|
||||
|
||||
// map flags
|
||||
Set<AclEntryFlag> aceFlags = ace.flags();
|
||||
byte flags = 0;
|
||||
if (aceFlags.contains(AclEntryFlag.FILE_INHERIT))
|
||||
flags |= OBJECT_INHERIT_ACE;
|
||||
if (aceFlags.contains(AclEntryFlag.DIRECTORY_INHERIT))
|
||||
flags |= CONTAINER_INHERIT_ACE;
|
||||
if (aceFlags.contains(AclEntryFlag.NO_PROPAGATE_INHERIT))
|
||||
flags |= NO_PROPAGATE_INHERIT_ACE;
|
||||
if (aceFlags.contains(AclEntryFlag.INHERIT_ONLY))
|
||||
flags |= INHERIT_ONLY_ACE;
|
||||
|
||||
if (allow) {
|
||||
AddAccessAllowedAceEx(aclAddress, flags, mask, sidAddress);
|
||||
} else {
|
||||
AddAccessDeniedAceEx(aclAddress, flags, mask, sidAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a security descriptor with a DACL representing the given ACL.
|
||||
*/
|
||||
static WindowsSecurityDescriptor create(List<AclEntry> acl)
|
||||
throws IOException
|
||||
{
|
||||
return new WindowsSecurityDescriptor(acl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the array of attributes looking for the attribute "acl:acl".
|
||||
* Returns security descriptor representing the ACL or the "null" security
|
||||
* descriptor if the attribute is not in the array.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static WindowsSecurityDescriptor fromAttribute(FileAttribute<?>... attrs)
|
||||
throws IOException
|
||||
{
|
||||
WindowsSecurityDescriptor sd = NULL_DESCRIPTOR;
|
||||
for (FileAttribute<?> attr: attrs) {
|
||||
// if more than one ACL specified then last one wins
|
||||
if (sd != NULL_DESCRIPTOR)
|
||||
sd.release();
|
||||
if (attr == null)
|
||||
throw new NullPointerException();
|
||||
if (attr.name().equals("acl:acl")) {
|
||||
List<AclEntry> acl = (List<AclEntry>)attr.value();
|
||||
sd = new WindowsSecurityDescriptor(acl);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("'" + attr.name() +
|
||||
"' not supported as initial attribute");
|
||||
}
|
||||
}
|
||||
return sd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts DACL from security descriptor.
|
||||
*/
|
||||
static List<AclEntry> getAcl(long pSecurityDescriptor) throws IOException {
|
||||
// get address of DACL
|
||||
long aclAddress = GetSecurityDescriptorDacl(pSecurityDescriptor);
|
||||
|
||||
// get ACE count
|
||||
int aceCount = 0;
|
||||
if (aclAddress == 0L) {
|
||||
// no ACEs
|
||||
aceCount = 0;
|
||||
} else {
|
||||
AclInformation aclInfo = GetAclInformation(aclAddress);
|
||||
aceCount = aclInfo.aceCount();
|
||||
}
|
||||
ArrayList<AclEntry> result = new ArrayList<>(aceCount);
|
||||
|
||||
// decode each of the ACEs to AclEntry objects
|
||||
for (int i=0; i<aceCount; i++) {
|
||||
long aceAddress = GetAce(aclAddress, i);
|
||||
AclEntry entry = decode(aceAddress);
|
||||
if (entry != null)
|
||||
result.add(entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
168
jdkSrc/jdk8/sun/nio/fs/WindowsUriSupport.java
Normal file
168
jdkSrc/jdk8/sun/nio/fs/WindowsUriSupport.java
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
/**
|
||||
* Utility methods to convert between Path and URIs.
|
||||
*/
|
||||
|
||||
class WindowsUriSupport {
|
||||
private WindowsUriSupport() {
|
||||
}
|
||||
|
||||
// suffix for IPv6 literal address
|
||||
private static final String IPV6_LITERAL_SUFFIX = ".ipv6-literal.net";
|
||||
|
||||
/**
|
||||
* Returns URI to represent the given (absolute) path
|
||||
*/
|
||||
private static URI toUri(String path, boolean isUnc, boolean addSlash) {
|
||||
String uriHost;
|
||||
String uriPath;
|
||||
|
||||
if (isUnc) {
|
||||
int slash = path.indexOf('\\', 2);
|
||||
uriHost = path.substring(2, slash);
|
||||
uriPath = path.substring(slash).replace('\\', '/');
|
||||
|
||||
// handle IPv6 literal addresses
|
||||
// 1. drop .ivp6-literal.net
|
||||
// 2. replace "-" with ":"
|
||||
// 3. replace "s" with "%" (zone/scopeID delimiter)
|
||||
if (uriHost.endsWith(IPV6_LITERAL_SUFFIX)) {
|
||||
uriHost = uriHost
|
||||
.substring(0, uriHost.length() - IPV6_LITERAL_SUFFIX.length())
|
||||
.replace('-', ':')
|
||||
.replace('s', '%');
|
||||
}
|
||||
} else {
|
||||
uriHost = "";
|
||||
uriPath = "/" + path.replace('\\', '/');
|
||||
}
|
||||
|
||||
// append slash if known to be directory
|
||||
if (addSlash)
|
||||
uriPath += "/";
|
||||
|
||||
// return file:///C:/My%20Documents or file://server/share/foo
|
||||
try {
|
||||
return new URI("file", uriHost, uriPath, null);
|
||||
} catch (URISyntaxException x) {
|
||||
if (!isUnc)
|
||||
throw new AssertionError(x);
|
||||
}
|
||||
|
||||
// if we get here it means we've got a UNC with reserved characters
|
||||
// in the server name. The authority component cannot contain escaped
|
||||
// octets so fallback to encoding the server name into the URI path
|
||||
// component.
|
||||
uriPath = "//" + path.replace('\\', '/');
|
||||
if (addSlash)
|
||||
uriPath += "/";
|
||||
try {
|
||||
return new URI("file", null, uriPath, null);
|
||||
} catch (URISyntaxException x) {
|
||||
throw new AssertionError(x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts given Path to a URI
|
||||
*/
|
||||
static URI toUri(WindowsPath path) {
|
||||
path = path.toAbsolutePath();
|
||||
String s = path.toString();
|
||||
|
||||
// trailing slash will be added if file is a directory. Skip check if
|
||||
// already have trailing space
|
||||
boolean addSlash = false;
|
||||
if (!s.endsWith("\\")) {
|
||||
try {
|
||||
path.checkRead();
|
||||
addSlash = WindowsFileAttributes.get(path, true).isDirectory();
|
||||
} catch (SecurityException | WindowsException x) {
|
||||
}
|
||||
}
|
||||
|
||||
return toUri(s, path.isUnc(), addSlash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts given URI to a Path
|
||||
*/
|
||||
static WindowsPath fromUri(WindowsFileSystem fs, URI uri) {
|
||||
if (!uri.isAbsolute())
|
||||
throw new IllegalArgumentException("URI is not absolute");
|
||||
if (uri.isOpaque())
|
||||
throw new IllegalArgumentException("URI is not hierarchical");
|
||||
String scheme = uri.getScheme();
|
||||
if ((scheme == null) || !scheme.equalsIgnoreCase("file"))
|
||||
throw new IllegalArgumentException("URI scheme is not \"file\"");
|
||||
if (uri.getFragment() != null)
|
||||
throw new IllegalArgumentException("URI has a fragment component");
|
||||
if (uri.getQuery() != null)
|
||||
throw new IllegalArgumentException("URI has a query component");
|
||||
String path = uri.getPath();
|
||||
if (path.equals(""))
|
||||
throw new IllegalArgumentException("URI path component is empty");
|
||||
|
||||
// UNC
|
||||
String auth = uri.getAuthority();
|
||||
if (auth != null && !auth.equals("")) {
|
||||
String host = uri.getHost();
|
||||
if (host == null)
|
||||
throw new IllegalArgumentException("URI authority component has undefined host");
|
||||
if (uri.getUserInfo() != null)
|
||||
throw new IllegalArgumentException("URI authority component has user-info");
|
||||
if (uri.getPort() != -1)
|
||||
throw new IllegalArgumentException("URI authority component has port number");
|
||||
|
||||
// IPv6 literal
|
||||
// 1. drop enclosing brackets
|
||||
// 2. replace ":" with "-"
|
||||
// 3. replace "%" with "s" (zone/scopeID delimiter)
|
||||
// 4. Append .ivp6-literal.net
|
||||
if (host.startsWith("[")) {
|
||||
host = host.substring(1, host.length()-1)
|
||||
.replace(':', '-')
|
||||
.replace('%', 's');
|
||||
host += IPV6_LITERAL_SUFFIX;
|
||||
}
|
||||
|
||||
// reconstitute the UNC
|
||||
path = "\\\\" + host + path;
|
||||
} else {
|
||||
if ((path.length() > 2) && (path.charAt(2) == ':')) {
|
||||
// "/c:/foo" --> "c:/foo"
|
||||
path = path.substring(1);
|
||||
}
|
||||
}
|
||||
return WindowsPath.parse(fs, path);
|
||||
}
|
||||
}
|
||||
342
jdkSrc/jdk8/sun/nio/fs/WindowsUserDefinedFileAttributeView.java
Normal file
342
jdkSrc/jdk8/sun/nio/fs/WindowsUserDefinedFileAttributeView.java
Normal file
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import static java.nio.file.StandardOpenOption.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
/**
|
||||
* Windows emulation of NamedAttributeView using Alternative Data Streams
|
||||
*/
|
||||
|
||||
class WindowsUserDefinedFileAttributeView
|
||||
extends AbstractUserDefinedFileAttributeView
|
||||
{
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
// syntax to address named streams
|
||||
private String join(String file, String name) {
|
||||
if (name == null)
|
||||
throw new NullPointerException("'name' is null");
|
||||
return file + ":" + name;
|
||||
}
|
||||
private String join(WindowsPath file, String name) throws WindowsException {
|
||||
return join(file.getPathForWin32Calls(), name);
|
||||
}
|
||||
|
||||
private final WindowsPath file;
|
||||
private final boolean followLinks;
|
||||
|
||||
WindowsUserDefinedFileAttributeView(WindowsPath file, boolean followLinks) {
|
||||
this.file = file;
|
||||
this.followLinks = followLinks;
|
||||
}
|
||||
|
||||
// enumerates the file streams using FindFirstStream/FindNextStream APIs.
|
||||
private List<String> listUsingStreamEnumeration() throws IOException {
|
||||
List<String> list = new ArrayList<>();
|
||||
try {
|
||||
FirstStream first = FindFirstStream(file.getPathForWin32Calls());
|
||||
if (first != null) {
|
||||
long handle = first.handle();
|
||||
try {
|
||||
// first stream is always ::$DATA for files
|
||||
String name = first.name();
|
||||
if (!name.equals("::$DATA")) {
|
||||
String[] segs = name.split(":");
|
||||
list.add(segs[1]);
|
||||
}
|
||||
while ((name = FindNextStream(handle)) != null) {
|
||||
String[] segs = name.split(":");
|
||||
list.add(segs[1]);
|
||||
}
|
||||
} finally {
|
||||
FindClose(handle);
|
||||
}
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
// enumerates the file streams by reading the stream headers using
|
||||
// BackupRead
|
||||
private List<String> listUsingBackupRead() throws IOException {
|
||||
long handle = -1L;
|
||||
try {
|
||||
int flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||||
if (!followLinks && file.getFileSystem().supportsLinks())
|
||||
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
|
||||
|
||||
handle = CreateFile(file.getPathForWin32Calls(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ, // no write as we depend on file size
|
||||
OPEN_EXISTING,
|
||||
flags);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
}
|
||||
|
||||
// buffer to read stream header and stream name.
|
||||
final int BUFFER_SIZE = 4096;
|
||||
NativeBuffer buffer = null;
|
||||
|
||||
// result with names of alternative data streams
|
||||
final List<String> list = new ArrayList<>();
|
||||
|
||||
try {
|
||||
buffer = NativeBuffers.getNativeBuffer(BUFFER_SIZE);
|
||||
long address = buffer.address();
|
||||
|
||||
/**
|
||||
* typedef struct _WIN32_STREAM_ID {
|
||||
* DWORD dwStreamId;
|
||||
* DWORD dwStreamAttributes;
|
||||
* LARGE_INTEGER Size;
|
||||
* DWORD dwStreamNameSize;
|
||||
* WCHAR cStreamName[ANYSIZE_ARRAY];
|
||||
* } WIN32_STREAM_ID;
|
||||
*/
|
||||
final int SIZEOF_STREAM_HEADER = 20;
|
||||
final int OFFSETOF_STREAM_ID = 0;
|
||||
final int OFFSETOF_STREAM_SIZE = 8;
|
||||
final int OFFSETOF_STREAM_NAME_SIZE = 16;
|
||||
|
||||
long context = 0L;
|
||||
try {
|
||||
for (;;) {
|
||||
// read stream header
|
||||
BackupResult result = BackupRead(handle, address,
|
||||
SIZEOF_STREAM_HEADER, false, context);
|
||||
context = result.context();
|
||||
if (result.bytesTransferred() == 0)
|
||||
break;
|
||||
|
||||
int streamId = unsafe.getInt(address + OFFSETOF_STREAM_ID);
|
||||
long streamSize = unsafe.getLong(address + OFFSETOF_STREAM_SIZE);
|
||||
int nameSize = unsafe.getInt(address + OFFSETOF_STREAM_NAME_SIZE);
|
||||
|
||||
// read stream name
|
||||
if (nameSize > 0) {
|
||||
result = BackupRead(handle, address, nameSize, false, context);
|
||||
if (result.bytesTransferred() != nameSize)
|
||||
break;
|
||||
}
|
||||
|
||||
// check for alternative data stream
|
||||
if (streamId == BACKUP_ALTERNATE_DATA) {
|
||||
char[] nameAsArray = new char[nameSize/2];
|
||||
unsafe.copyMemory(null, address, nameAsArray,
|
||||
Unsafe.ARRAY_CHAR_BASE_OFFSET, nameSize);
|
||||
|
||||
String[] segs = new String(nameAsArray).split(":");
|
||||
if (segs.length == 3)
|
||||
list.add(segs[1]);
|
||||
}
|
||||
|
||||
// sparse blocks not currently handled as documentation
|
||||
// is not sufficient on how the spase block can be skipped.
|
||||
if (streamId == BACKUP_SPARSE_BLOCK) {
|
||||
throw new IOException("Spare blocks not handled");
|
||||
}
|
||||
|
||||
// seek to end of stream
|
||||
if (streamSize > 0L) {
|
||||
BackupSeek(handle, streamSize, context);
|
||||
}
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
// failed to read or seek
|
||||
throw new IOException(x.errorString());
|
||||
} finally {
|
||||
// release context
|
||||
if (context != 0L) {
|
||||
try {
|
||||
BackupRead(handle, 0L, 0, true, context);
|
||||
} catch (WindowsException ignore) { }
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (buffer != null)
|
||||
buffer.release();
|
||||
CloseHandle(handle);
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> list() throws IOException {
|
||||
if (System.getSecurityManager() != null)
|
||||
checkAccess(file.getPathForPermissionCheck(), true, false);
|
||||
// use stream APIs on Windwos Server 2003 and newer
|
||||
if (file.getFileSystem().supportsStreamEnumeration()) {
|
||||
return listUsingStreamEnumeration();
|
||||
} else {
|
||||
return listUsingBackupRead();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size(String name) throws IOException {
|
||||
if (System.getSecurityManager() != null)
|
||||
checkAccess(file.getPathForPermissionCheck(), true, false);
|
||||
|
||||
// wrap with channel
|
||||
FileChannel fc = null;
|
||||
try {
|
||||
Set<OpenOption> opts = new HashSet<>();
|
||||
opts.add(READ);
|
||||
if (!followLinks)
|
||||
opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT);
|
||||
fc = WindowsChannelFactory
|
||||
.newFileChannel(join(file, name), null, opts, 0L);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name));
|
||||
}
|
||||
try {
|
||||
long size = fc.size();
|
||||
if (size > Integer.MAX_VALUE)
|
||||
throw new ArithmeticException("Stream too large");
|
||||
return (int)size;
|
||||
} finally {
|
||||
fc.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(String name, ByteBuffer dst) throws IOException {
|
||||
if (System.getSecurityManager() != null)
|
||||
checkAccess(file.getPathForPermissionCheck(), true, false);
|
||||
|
||||
// wrap with channel
|
||||
FileChannel fc = null;
|
||||
try {
|
||||
Set<OpenOption> opts = new HashSet<>();
|
||||
opts.add(READ);
|
||||
if (!followLinks)
|
||||
opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT);
|
||||
fc = WindowsChannelFactory
|
||||
.newFileChannel(join(file, name), null, opts, 0L);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name));
|
||||
}
|
||||
|
||||
// read to EOF (nothing we can do if I/O error occurs)
|
||||
try {
|
||||
if (fc.size() > dst.remaining())
|
||||
throw new IOException("Stream too large");
|
||||
int total = 0;
|
||||
while (dst.hasRemaining()) {
|
||||
int n = fc.read(dst);
|
||||
if (n < 0)
|
||||
break;
|
||||
total += n;
|
||||
}
|
||||
return total;
|
||||
} finally {
|
||||
fc.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(String name, ByteBuffer src) throws IOException {
|
||||
if (System.getSecurityManager() != null)
|
||||
checkAccess(file.getPathForPermissionCheck(), false, true);
|
||||
|
||||
/**
|
||||
* Creating a named stream will cause the unnamed stream to be created
|
||||
* if it doesn't already exist. To avoid this we open the unnamed stream
|
||||
* for reading and hope it isn't deleted/moved while we create or
|
||||
* replace the named stream. Opening the file without sharing options
|
||||
* may cause sharing violations with other programs that are accessing
|
||||
* the unnamed stream.
|
||||
*/
|
||||
long handle = -1L;
|
||||
try {
|
||||
int flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||||
if (!followLinks)
|
||||
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
|
||||
|
||||
handle = CreateFile(file.getPathForWin32Calls(),
|
||||
GENERIC_READ,
|
||||
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
|
||||
OPEN_EXISTING,
|
||||
flags);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
}
|
||||
try {
|
||||
Set<OpenOption> opts = new HashSet<>();
|
||||
if (!followLinks)
|
||||
opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT);
|
||||
opts.add(CREATE);
|
||||
opts.add(WRITE);
|
||||
opts.add(StandardOpenOption.TRUNCATE_EXISTING);
|
||||
FileChannel named = null;
|
||||
try {
|
||||
named = WindowsChannelFactory
|
||||
.newFileChannel(join(file, name), null, opts, 0L);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name));
|
||||
}
|
||||
// write value (nothing we can do if I/O error occurs)
|
||||
try {
|
||||
int rem = src.remaining();
|
||||
while (src.hasRemaining()) {
|
||||
named.write(src);
|
||||
}
|
||||
return rem;
|
||||
} finally {
|
||||
named.close();
|
||||
}
|
||||
} finally {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String name) throws IOException {
|
||||
if (System.getSecurityManager() != null)
|
||||
checkAccess(file.getPathForPermissionCheck(), false, true);
|
||||
|
||||
String path = WindowsLinkSupport.getFinalPath(file, followLinks);
|
||||
String toDelete = join(path, name);
|
||||
try {
|
||||
DeleteFile(toDelete);
|
||||
} catch (WindowsException x) {
|
||||
x.rethrowAsIOException(toDelete);
|
||||
}
|
||||
}
|
||||
}
|
||||
169
jdkSrc/jdk8/sun/nio/fs/WindowsUserPrincipals.java
Normal file
169
jdkSrc/jdk8/sun/nio/fs/WindowsUserPrincipals.java
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.attribute.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
|
||||
class WindowsUserPrincipals {
|
||||
private WindowsUserPrincipals() { }
|
||||
|
||||
static class User implements UserPrincipal {
|
||||
// String representation of SID
|
||||
private final String sidString;
|
||||
|
||||
// SID type
|
||||
private final int sidType;
|
||||
|
||||
// Account name (if available) or SID
|
||||
private final String accountName;
|
||||
|
||||
User(String sidString, int sidType, String accountName) {
|
||||
this.sidString = sidString;
|
||||
this.sidType = sidType;
|
||||
this.accountName = accountName;
|
||||
}
|
||||
|
||||
// package-private
|
||||
String sidString() {
|
||||
return sidString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return accountName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String type;
|
||||
switch (sidType) {
|
||||
case SidTypeUser : type = "User"; break;
|
||||
case SidTypeGroup : type = "Group"; break;
|
||||
case SidTypeDomain : type = "Domain"; break;
|
||||
case SidTypeAlias : type = "Alias"; break;
|
||||
case SidTypeWellKnownGroup : type = "Well-known group"; break;
|
||||
case SidTypeDeletedAccount : type = "Deleted"; break;
|
||||
case SidTypeInvalid : type = "Invalid"; break;
|
||||
case SidTypeComputer : type = "Computer"; break;
|
||||
default: type = "Unknown";
|
||||
}
|
||||
return accountName + " (" + type + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this)
|
||||
return true;
|
||||
if (!(obj instanceof WindowsUserPrincipals.User))
|
||||
return false;
|
||||
WindowsUserPrincipals.User other = (WindowsUserPrincipals.User)obj;
|
||||
return this.sidString.equals(other.sidString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return sidString.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
static class Group extends User implements GroupPrincipal {
|
||||
Group(String sidString, int sidType, String accountName) {
|
||||
super(sidString, sidType, accountName);
|
||||
}
|
||||
}
|
||||
|
||||
static UserPrincipal fromSid(long sidAddress) throws IOException {
|
||||
String sidString;
|
||||
try {
|
||||
sidString = ConvertSidToStringSid(sidAddress);
|
||||
if (sidString == null) {
|
||||
// pre-Windows XP system?
|
||||
throw new AssertionError();
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
throw new IOException("Unable to convert SID to String: " +
|
||||
x.errorString());
|
||||
}
|
||||
|
||||
// lookup account; if not available then use the SID as the name
|
||||
Account account = null;
|
||||
String name;
|
||||
try {
|
||||
account = LookupAccountSid(sidAddress);
|
||||
name = account.domain() + "\\" + account.name();
|
||||
} catch (WindowsException x) {
|
||||
name = sidString;
|
||||
}
|
||||
|
||||
int sidType = (account == null) ? SidTypeUnknown : account.use();
|
||||
if ((sidType == SidTypeGroup) ||
|
||||
(sidType == SidTypeWellKnownGroup) ||
|
||||
(sidType == SidTypeAlias)) // alias for local group
|
||||
{
|
||||
return new Group(sidString, sidType, name);
|
||||
} else {
|
||||
return new User(sidString, sidType, name);
|
||||
}
|
||||
}
|
||||
|
||||
static UserPrincipal lookup(String name) throws IOException {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(new RuntimePermission("lookupUserInformation"));
|
||||
}
|
||||
|
||||
// invoke LookupAccountName to get buffer size needed for SID
|
||||
int size = 0;
|
||||
try {
|
||||
size = LookupAccountName(name, 0L, 0);
|
||||
} catch (WindowsException x) {
|
||||
if (x.lastError() == ERROR_NONE_MAPPED)
|
||||
throw new UserPrincipalNotFoundException(name);
|
||||
throw new IOException(name + ": " + x.errorString());
|
||||
}
|
||||
assert size > 0;
|
||||
|
||||
// allocate buffer and re-invoke LookupAccountName get SID
|
||||
NativeBuffer sidBuffer = NativeBuffers.getNativeBuffer(size);
|
||||
try {
|
||||
int newSize = LookupAccountName(name, sidBuffer.address(), size);
|
||||
if (newSize != size) {
|
||||
// can this happen?
|
||||
throw new AssertionError("SID change during lookup");
|
||||
}
|
||||
|
||||
// return user principal
|
||||
return fromSid(sidBuffer.address());
|
||||
} catch (WindowsException x) {
|
||||
throw new IOException(name + ": " + x.errorString());
|
||||
} finally {
|
||||
sidBuffer.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
656
jdkSrc/jdk8/sun/nio/fs/WindowsWatchService.java
Normal file
656
jdkSrc/jdk8/sun/nio/fs/WindowsWatchService.java
Normal file
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.NotDirectoryException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardWatchEventKinds;
|
||||
import java.nio.file.WatchEvent;
|
||||
import java.nio.file.WatchKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sun.nio.file.ExtendedWatchEventModifier;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
import static sun.nio.fs.WindowsConstants.*;
|
||||
|
||||
/*
|
||||
* Win32 implementation of WatchService based on ReadDirectoryChangesW.
|
||||
*/
|
||||
|
||||
class WindowsWatchService
|
||||
extends AbstractWatchService
|
||||
{
|
||||
private final static int WAKEUP_COMPLETION_KEY = 0;
|
||||
|
||||
// background thread to service I/O completion port
|
||||
private final Poller poller;
|
||||
|
||||
/**
|
||||
* Creates an I/O completion port and a daemon thread to service it
|
||||
*/
|
||||
WindowsWatchService(WindowsFileSystem fs) throws IOException {
|
||||
// create I/O completion port
|
||||
long port = 0L;
|
||||
try {
|
||||
port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0);
|
||||
} catch (WindowsException x) {
|
||||
throw new IOException(x.getMessage());
|
||||
}
|
||||
|
||||
this.poller = new Poller(fs, this, port);
|
||||
this.poller.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
WatchKey register(Path path,
|
||||
WatchEvent.Kind<?>[] events,
|
||||
WatchEvent.Modifier... modifiers)
|
||||
throws IOException
|
||||
{
|
||||
// delegate to poller
|
||||
return poller.register(path, events, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
void implClose() throws IOException {
|
||||
// delegate to poller
|
||||
poller.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Windows implementation of WatchKey.
|
||||
*/
|
||||
private static class WindowsWatchKey extends AbstractWatchKey {
|
||||
// file key (used to detect existing registrations)
|
||||
private final FileKey fileKey;
|
||||
|
||||
// handle to directory
|
||||
private volatile long handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
// interest events
|
||||
private Set<? extends WatchEvent.Kind<?>> events;
|
||||
|
||||
// subtree
|
||||
private boolean watchSubtree;
|
||||
|
||||
// buffer for change events
|
||||
private NativeBuffer buffer;
|
||||
|
||||
// pointer to bytes returned (in buffer)
|
||||
private long countAddress;
|
||||
|
||||
// pointer to overlapped structure (in buffer)
|
||||
private long overlappedAddress;
|
||||
|
||||
// completion key (used to map I/O completion to WatchKey)
|
||||
private int completionKey;
|
||||
|
||||
// flag indicates that ReadDirectoryChangesW failed
|
||||
// and overlapped I/O operation wasn't started
|
||||
private boolean errorStartingOverlapped;
|
||||
|
||||
WindowsWatchKey(Path dir,
|
||||
AbstractWatchService watcher,
|
||||
FileKey fileKey)
|
||||
{
|
||||
super(dir, watcher);
|
||||
this.fileKey = fileKey;
|
||||
}
|
||||
|
||||
WindowsWatchKey init(long handle,
|
||||
Set<? extends WatchEvent.Kind<?>> events,
|
||||
boolean watchSubtree,
|
||||
NativeBuffer buffer,
|
||||
long countAddress,
|
||||
long overlappedAddress,
|
||||
int completionKey)
|
||||
{
|
||||
this.handle = handle;
|
||||
this.events = events;
|
||||
this.watchSubtree = watchSubtree;
|
||||
this.buffer = buffer;
|
||||
this.countAddress = countAddress;
|
||||
this.overlappedAddress = overlappedAddress;
|
||||
this.completionKey = completionKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
long handle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
Set<? extends WatchEvent.Kind<?>> events() {
|
||||
return events;
|
||||
}
|
||||
|
||||
void setEvents(Set<? extends WatchEvent.Kind<?>> events) {
|
||||
this.events = events;
|
||||
}
|
||||
|
||||
boolean watchSubtree() {
|
||||
return watchSubtree;
|
||||
}
|
||||
|
||||
NativeBuffer buffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
long countAddress() {
|
||||
return countAddress;
|
||||
}
|
||||
|
||||
long overlappedAddress() {
|
||||
return overlappedAddress;
|
||||
}
|
||||
|
||||
FileKey fileKey() {
|
||||
return fileKey;
|
||||
}
|
||||
|
||||
int completionKey() {
|
||||
return completionKey;
|
||||
}
|
||||
|
||||
void setErrorStartingOverlapped(boolean value) {
|
||||
errorStartingOverlapped = value;
|
||||
}
|
||||
|
||||
boolean isErrorStartingOverlapped() {
|
||||
return errorStartingOverlapped;
|
||||
}
|
||||
|
||||
// Invalidate the key, assumes that resources have been released
|
||||
void invalidate() {
|
||||
((WindowsWatchService)watcher()).poller.releaseResources(this);
|
||||
handle = INVALID_HANDLE_VALUE;
|
||||
buffer = null;
|
||||
countAddress = 0;
|
||||
overlappedAddress = 0;
|
||||
errorStartingOverlapped = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return handle != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
if (isValid()) {
|
||||
// delegate to poller
|
||||
((WindowsWatchService)watcher()).poller.cancel(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// file key to unique identify (open) directory
|
||||
private static class FileKey {
|
||||
private final int volSerialNumber;
|
||||
private final int fileIndexHigh;
|
||||
private final int fileIndexLow;
|
||||
|
||||
FileKey(int volSerialNumber, int fileIndexHigh, int fileIndexLow) {
|
||||
this.volSerialNumber = volSerialNumber;
|
||||
this.fileIndexHigh = fileIndexHigh;
|
||||
this.fileIndexLow = fileIndexLow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return volSerialNumber ^ fileIndexHigh ^ fileIndexLow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this)
|
||||
return true;
|
||||
if (!(obj instanceof FileKey))
|
||||
return false;
|
||||
FileKey other = (FileKey)obj;
|
||||
if (this.volSerialNumber != other.volSerialNumber) return false;
|
||||
if (this.fileIndexHigh != other.fileIndexHigh) return false;
|
||||
return this.fileIndexLow == other.fileIndexLow;
|
||||
}
|
||||
}
|
||||
|
||||
// all change events
|
||||
private static final int ALL_FILE_NOTIFY_EVENTS =
|
||||
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_CREATION |
|
||||
FILE_NOTIFY_CHANGE_SECURITY;
|
||||
|
||||
/**
|
||||
* Background thread to service I/O completion port.
|
||||
*/
|
||||
private static class Poller extends AbstractPoller {
|
||||
private final static Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
|
||||
/*
|
||||
* typedef struct _OVERLAPPED {
|
||||
* ULONG_PTR Internal;
|
||||
* ULONG_PTR InternalHigh;
|
||||
* union {
|
||||
* struct { DWORD Offset; DWORD OffsetHigh; };
|
||||
* PVOID Pointer;
|
||||
* };
|
||||
* HANDLE hEvent;
|
||||
* } OVERLAPPED;
|
||||
*/
|
||||
private static final short SIZEOF_DWORD = 4;
|
||||
private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit
|
||||
private static final short OFFSETOF_HEVENT =
|
||||
(UNSAFE.addressSize() == 4) ? (short) 16 : 24;
|
||||
|
||||
|
||||
/*
|
||||
* typedef struct _FILE_NOTIFY_INFORMATION {
|
||||
* DWORD NextEntryOffset;
|
||||
* DWORD Action;
|
||||
* DWORD FileNameLength;
|
||||
* WCHAR FileName[1];
|
||||
* } FileNameLength;
|
||||
*/
|
||||
private static final short OFFSETOF_NEXTENTRYOFFSET = 0;
|
||||
private static final short OFFSETOF_ACTION = 4;
|
||||
private static final short OFFSETOF_FILENAMELENGTH = 8;
|
||||
private static final short OFFSETOF_FILENAME = 12;
|
||||
|
||||
// size of per-directory buffer for events (FIXME - make this configurable)
|
||||
// Need to be less than 4*16384 = 65536. DWORD align.
|
||||
private static final int CHANGES_BUFFER_SIZE = 16 * 1024;
|
||||
|
||||
private final WindowsFileSystem fs;
|
||||
private final WindowsWatchService watcher;
|
||||
private final long port;
|
||||
|
||||
// maps completion key to WatchKey
|
||||
private final Map<Integer, WindowsWatchKey> ck2key;
|
||||
|
||||
// maps file key to WatchKey
|
||||
private final Map<FileKey, WindowsWatchKey> fk2key;
|
||||
|
||||
// unique completion key for each directory
|
||||
// native completion key capacity is 64 bits on Win64.
|
||||
private int lastCompletionKey;
|
||||
|
||||
Poller(WindowsFileSystem fs, WindowsWatchService watcher, long port) {
|
||||
this.fs = fs;
|
||||
this.watcher = watcher;
|
||||
this.port = port;
|
||||
this.ck2key = new HashMap<>();
|
||||
this.fk2key = new HashMap<>();
|
||||
this.lastCompletionKey = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wakeup() throws IOException {
|
||||
try {
|
||||
PostQueuedCompletionStatus(port, WAKEUP_COMPLETION_KEY);
|
||||
} catch (WindowsException x) {
|
||||
throw new IOException(x.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a directory for changes as follows:
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the outstanding I/O operation on the directory
|
||||
* associated with the given key and releases the associated
|
||||
* resources.
|
||||
*/
|
||||
private void releaseResources(WindowsWatchKey key) {
|
||||
if (!key.isErrorStartingOverlapped()) {
|
||||
try {
|
||||
CancelIo(key.handle());
|
||||
GetOverlappedResult(key.handle(), key.overlappedAddress());
|
||||
} catch (WindowsException expected) {
|
||||
// expected as I/O operation has been cancelled
|
||||
}
|
||||
}
|
||||
CloseHandle(key.handle());
|
||||
closeAttachedEvent(key.overlappedAddress());
|
||||
key.buffer().cleaner().clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an unnamed event and set it as the hEvent field
|
||||
* in the given OVERLAPPED structure
|
||||
*/
|
||||
private void createAndAttachEvent(long ov) throws WindowsException {
|
||||
long hEvent = CreateEvent(false, false);
|
||||
UNSAFE.putAddress(ov + OFFSETOF_HEVENT, hEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the event attached to the given OVERLAPPED structure. A
|
||||
* no-op if there isn't an event attached.
|
||||
*/
|
||||
private void closeAttachedEvent(long ov) {
|
||||
long hEvent = UNSAFE.getAddress(ov + OFFSETOF_HEVENT);
|
||||
if (hEvent != 0 && hEvent != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(hEvent);
|
||||
}
|
||||
|
||||
// cancel single key
|
||||
@Override
|
||||
void implCancelKey(WatchKey obj) {
|
||||
WindowsWatchKey key = (WindowsWatchKey)obj;
|
||||
if (key.isValid()) {
|
||||
fk2key.remove(key.fileKey());
|
||||
ck2key.remove(key.completionKey());
|
||||
key.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
// close watch service
|
||||
@Override
|
||||
void implCloseAll() {
|
||||
// cancel all keys
|
||||
ck2key.values().forEach(WindowsWatchKey::invalidate);
|
||||
|
||||
fk2key.clear();
|
||||
ck2key.clear();
|
||||
|
||||
// close I/O completion port
|
||||
CloseHandle(port);
|
||||
}
|
||||
|
||||
// Translate file change action into watch event
|
||||
private WatchEvent.Kind<?> translateActionToEvent(int action) {
|
||||
switch (action) {
|
||||
case FILE_ACTION_MODIFIED :
|
||||
return StandardWatchEventKinds.ENTRY_MODIFY;
|
||||
|
||||
case FILE_ACTION_ADDED :
|
||||
case FILE_ACTION_RENAMED_NEW_NAME :
|
||||
return StandardWatchEventKinds.ENTRY_CREATE;
|
||||
|
||||
case FILE_ACTION_REMOVED :
|
||||
case FILE_ACTION_RENAMED_OLD_NAME :
|
||||
return StandardWatchEventKinds.ENTRY_DELETE;
|
||||
|
||||
default :
|
||||
return null; // action not recognized
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Poller main loop
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
for (;;) {
|
||||
CompletionStatus info;
|
||||
try {
|
||||
info = GetQueuedCompletionStatus(port);
|
||||
} catch (WindowsException x) {
|
||||
// this should not happen
|
||||
x.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
// wakeup
|
||||
if (info.completionKey() == WAKEUP_COMPLETION_KEY) {
|
||||
boolean shutdown = processRequests();
|
||||
if (shutdown) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// map completionKey to get WatchKey
|
||||
WindowsWatchKey key = ck2key.get((int)info.completionKey());
|
||||
if (key == null) {
|
||||
// We get here when a registration is changed. In that case
|
||||
// the directory is closed which causes an event with the
|
||||
// old completion key.
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean criticalError = false;
|
||||
int errorCode = info.error();
|
||||
int messageSize = info.bytesTransferred();
|
||||
if (errorCode == ERROR_NOTIFY_ENUM_DIR) {
|
||||
// buffer overflow
|
||||
key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
|
||||
} else if (errorCode != 0 && errorCode != ERROR_MORE_DATA) {
|
||||
// ReadDirectoryChangesW failed
|
||||
criticalError = true;
|
||||
} else {
|
||||
// ERROR_MORE_DATA is a warning about incomplite
|
||||
// data transfer over TCP/UDP stack. For the case
|
||||
// [messageSize] is zero in the most of cases.
|
||||
|
||||
if (messageSize > 0) {
|
||||
// process non-empty events.
|
||||
processEvents(key, messageSize);
|
||||
} else if (errorCode == 0) {
|
||||
// insufficient buffer size
|
||||
// not described, but can happen.
|
||||
key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
|
||||
}
|
||||
|
||||
// start read for next batch of changes
|
||||
try {
|
||||
ReadDirectoryChangesW(key.handle(),
|
||||
key.buffer().address(),
|
||||
CHANGES_BUFFER_SIZE,
|
||||
key.watchSubtree(),
|
||||
ALL_FILE_NOTIFY_EVENTS,
|
||||
key.countAddress(),
|
||||
key.overlappedAddress());
|
||||
} catch (WindowsException x) {
|
||||
// no choice but to cancel key
|
||||
criticalError = true;
|
||||
key.setErrorStartingOverlapped(true);
|
||||
}
|
||||
}
|
||||
if (criticalError) {
|
||||
implCancelKey(key);
|
||||
key.signal();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user