1 /* 2 * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.nio.fs; 27 28 import java.nio.file.*; 29 import java.nio.file.attribute.*; 30 import java.nio.file.spi.*; 31 import java.io.IOException; 32 import java.util.*; 33 import java.util.regex.Pattern; 34 import sun.security.action.GetPropertyAction; 35 36 /** 37 * Base implementation of FileSystem for Unix-like implementations. 38 */ 39 40 abstract class UnixFileSystem 41 extends FileSystem 42 { 43 private final UnixFileSystemProvider provider; 44 private final byte[] defaultDirectory; 45 private final boolean needToResolveAgainstDefaultDirectory; 46 private final UnixPath rootDirectory; 47 48 // package-private 49 UnixFileSystem(UnixFileSystemProvider provider, String dir) { 50 this.provider = provider; 51 this.defaultDirectory = Util.toBytes(UnixPath.normalizeAndCheck(dir)); 52 if (this.defaultDirectory[0] != '/') { 53 throw new RuntimeException("default directory must be absolute"); 54 } 55 56 // if process-wide chdir is allowed or default directory is not the 57 // process working directory then paths must be resolved against the 58 // default directory. 59 String propValue = GetPropertyAction 60 .privilegedGetProperty("sun.nio.fs.chdirAllowed", "false"); 61 boolean chdirAllowed = (propValue.length() == 0) ? 62 true : Boolean.valueOf(propValue); 63 if (chdirAllowed) { 64 this.needToResolveAgainstDefaultDirectory = true; 65 } else { 66 byte[] cwd = UnixNativeDispatcher.getcwd(); 67 boolean defaultIsCwd = (cwd.length == defaultDirectory.length); 68 if (defaultIsCwd) { 69 for (int i=0; i<cwd.length; i++) { 70 if (cwd[i] != defaultDirectory[i]) { 71 defaultIsCwd = false; 72 break; 73 } 74 } 75 } 76 this.needToResolveAgainstDefaultDirectory = !defaultIsCwd; 77 } 78 79 // the root directory 80 this.rootDirectory = new UnixPath(this, "/"); 81 } 82 83 // package-private 84 byte[] defaultDirectory() { 85 return defaultDirectory; 86 } 87 88 boolean needToResolveAgainstDefaultDirectory() { 89 return needToResolveAgainstDefaultDirectory; 90 } 91 92 UnixPath rootDirectory() { 93 return rootDirectory; 94 } 95 96 boolean isSolaris() { 97 return false; 98 } 99 100 static List<String> standardFileAttributeViews() { 101 return Arrays.asList("basic", "posix", "unix", "owner"); 102 } 103 104 @Override 105 public final FileSystemProvider provider() { 106 return provider; 107 } 108 109 @Override 110 public final String getSeparator() { 111 return "/"; 112 } 113 114 @Override 115 public final boolean isOpen() { 116 return true; 117 } 118 119 @Override 120 public final boolean isReadOnly() { 121 return false; 122 } 123 124 @Override 125 public final void close() throws IOException { 126 throw new UnsupportedOperationException(); 127 } 128 129 /** 130 * Copies non-POSIX attributes from the source to target file. 131 * 132 * Copying a file preserving attributes, or moving a file, will preserve 133 * the file owner/group/permissions/timestamps but it does not preserve 134 * other non-POSIX attributes. This method is invoked by the 135 * copy or move operation to preserve these attributes. It should copy 136 * extended attributes, ACLs, or other attributes. 137 * 138 * @param sfd 139 * Open file descriptor to source file 140 * @param tfd 141 * Open file descriptor to target file 142 */ 143 void copyNonPosixAttributes(int sfd, int tfd) { 144 // no-op by default 145 } 146 147 /** 148 * Unix systems only have a single root directory (/) 149 */ 150 @Override 151 public final Iterable<Path> getRootDirectories() { 152 final List<Path> allowedList = 153 Collections.unmodifiableList(Arrays.asList((Path)rootDirectory)); 154 return new Iterable<>() { 155 public Iterator<Path> iterator() { 156 try { 157 SecurityManager sm = System.getSecurityManager(); 158 if (sm != null) 159 sm.checkRead(rootDirectory.toString()); 160 return allowedList.iterator(); 161 } catch (SecurityException x) { 162 List<Path> disallowed = Collections.emptyList(); 163 return disallowed.iterator(); 164 } 165 } 166 }; 167 } 168 169 /** 170 * Returns object to iterate over entries in mounttab or equivalent 171 */ 172 abstract Iterable<UnixMountEntry> getMountEntries(); 173 174 /** 175 * Returns a FileStore to represent the file system for the given mount 176 * mount. 177 */ 178 abstract FileStore getFileStore(UnixMountEntry entry) throws IOException; 179 180 /** 181 * Iterator returned by getFileStores method. 182 */ 183 private class FileStoreIterator implements Iterator<FileStore> { 184 private final Iterator<UnixMountEntry> entries; 185 private FileStore next; 186 187 FileStoreIterator() { 188 this.entries = getMountEntries().iterator(); 189 } 190 191 private FileStore readNext() { 192 assert Thread.holdsLock(this); 193 for (;;) { 194 if (!entries.hasNext()) 195 return null; 196 UnixMountEntry entry = entries.next(); 197 198 // skip entries with the "ignore" option 199 if (entry.isIgnored()) 200 continue; 201 202 // check permission to read mount point 203 SecurityManager sm = System.getSecurityManager(); 204 if (sm != null) { 205 try { 206 sm.checkRead(Util.toString(entry.dir())); 207 } catch (SecurityException x) { 208 continue; 209 } 210 } 211 try { 212 return getFileStore(entry); 213 } catch (IOException ignore) { 214 // ignore as per spec 215 } 216 } 217 } 218 219 @Override 220 public synchronized boolean hasNext() { 221 if (next != null) 222 return true; 223 next = readNext(); 224 return next != null; 225 } 226 227 @Override 228 public synchronized FileStore next() { 229 if (next == null) 230 next = readNext(); 231 if (next == null) { 232 throw new NoSuchElementException(); 233 } else { 234 FileStore result = next; 235 next = null; 236 return result; 237 } 238 } 239 240 @Override 241 public void remove() { 242 throw new UnsupportedOperationException(); 243 } 244 } 245 246 @Override 247 public final Iterable<FileStore> getFileStores() { 248 SecurityManager sm = System.getSecurityManager(); 249 if (sm != null) { 250 try { 251 sm.checkPermission(new RuntimePermission("getFileStoreAttributes")); 252 } catch (SecurityException se) { 253 return Collections.emptyList(); 254 } 255 } 256 return new Iterable<>() { 257 public Iterator<FileStore> iterator() { 258 return new FileStoreIterator(); 259 } 260 }; 261 } 262 263 @Override 264 public final Path getPath(String first, String... more) { 265 String path; 266 if (more.length == 0) { 267 path = first; 268 } else { 269 StringBuilder sb = new StringBuilder(); 270 sb.append(first); 271 for (String segment: more) { 272 if (segment.length() > 0) { 273 if (sb.length() > 0) 274 sb.append('/'); 275 sb.append(segment); 276 } 277 } 278 path = sb.toString(); 279 } 280 return new UnixPath(this, path); 281 } 282 283 @Override 284 public PathMatcher getPathMatcher(String syntaxAndInput) { 285 int pos = syntaxAndInput.indexOf(':'); 286 if (pos <= 0 || pos == syntaxAndInput.length()) 287 throw new IllegalArgumentException(); 288 String syntax = syntaxAndInput.substring(0, pos); 289 String input = syntaxAndInput.substring(pos+1); 290 291 String expr; 292 if (syntax.equalsIgnoreCase(GLOB_SYNTAX)) { 293 expr = Globs.toUnixRegexPattern(input); 294 } else { 295 if (syntax.equalsIgnoreCase(REGEX_SYNTAX)) { 296 expr = input; 297 } else { 298 throw new UnsupportedOperationException("Syntax '" + syntax + 299 "' not recognized"); 300 } 301 } 302 303 // return matcher 304 final Pattern pattern = compilePathMatchPattern(expr); 305 306 return new PathMatcher() { 307 @Override 308 public boolean matches(Path path) { 309 return pattern.matcher(path.toString()).matches(); 310 } 311 }; 312 } 313 314 private static final String GLOB_SYNTAX = "glob"; 315 private static final String REGEX_SYNTAX = "regex"; 316 317 @Override 318 public final UserPrincipalLookupService getUserPrincipalLookupService() { 319 return LookupService.instance; 320 } 321 322 private static class LookupService { 323 static final UserPrincipalLookupService instance = 324 new UserPrincipalLookupService() { 325 @Override 326 public UserPrincipal lookupPrincipalByName(String name) 327 throws IOException 328 { 329 return UnixUserPrincipals.lookupUser(name); 330 } 331 332 @Override 333 public GroupPrincipal lookupPrincipalByGroupName(String group) 334 throws IOException 335 { 336 return UnixUserPrincipals.lookupGroup(group); 337 } 338 }; 339 } 340 341 // Override if the platform has different path match requirement, such as 342 // case insensitive or Unicode canonical equal on MacOSX 343 Pattern compilePathMatchPattern(String expr) { 344 return Pattern.compile(expr); 345 } 346 347 // Override if the platform uses different Unicode normalization form 348 // for native file path. For example on MacOSX, the native path is stored 349 // in Unicode NFD form. 350 char[] normalizeNativePath(char[] path) { 351 return path; 352 } 353 354 // Override if the native file path use non-NFC form. For example on MacOSX, 355 // the native path is stored in Unicode NFD form, the path need to be 356 // normalized back to NFC before passed back to Java level. 357 String normalizeJavaPath(String path) { 358 return path; 359 } 360 }