1 /* 2 * Copyright (c) 2008, 2011, 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.channels.SeekableByteChannel; 31 import java.util.*; 32 import java.util.concurrent.TimeUnit; 33 import java.io.IOException; 34 35 import dalvik.system.CloseGuard; 36 37 import static sun.nio.fs.UnixNativeDispatcher.*; 38 import static sun.nio.fs.UnixConstants.*; 39 40 /** 41 * Unix implementation of SecureDirectoryStream. 42 */ 43 44 class UnixSecureDirectoryStream 45 implements SecureDirectoryStream<Path> 46 { 47 private final UnixDirectoryStream ds; 48 private final int dfd; 49 50 // Android-changed: Add CloseGuard support. 51 private final CloseGuard guard = CloseGuard.get(); 52 UnixSecureDirectoryStream(UnixPath dir, long dp, int dfd, DirectoryStream.Filter<? super Path> filter)53 UnixSecureDirectoryStream(UnixPath dir, 54 long dp, 55 int dfd, 56 DirectoryStream.Filter<? super Path> filter) 57 { 58 this.ds = new UnixDirectoryStream(dir, dp, filter); 59 this.dfd = dfd; 60 // Android-changed: Add CloseGuard support. 61 if (dfd != -1) { 62 guard.open("close"); 63 } 64 } 65 66 @Override close()67 public void close() 68 throws IOException 69 { 70 ds.writeLock().lock(); 71 try { 72 if (ds.closeImpl()) { 73 UnixNativeDispatcher.close(dfd); 74 } 75 } finally { 76 ds.writeLock().unlock(); 77 } 78 // Android-changed: Add CloseGuard support. 79 guard.close(); 80 } 81 82 @Override iterator()83 public Iterator<Path> iterator() { 84 return ds.iterator(this); 85 } 86 getName(Path obj)87 private UnixPath getName(Path obj) { 88 if (obj == null) 89 throw new NullPointerException(); 90 if (!(obj instanceof UnixPath)) 91 throw new ProviderMismatchException(); 92 return (UnixPath)obj; 93 } 94 95 /** 96 * Opens sub-directory in this directory 97 */ 98 @Override newDirectoryStream(Path obj, LinkOption... options)99 public SecureDirectoryStream<Path> newDirectoryStream(Path obj, 100 LinkOption... options) 101 throws IOException 102 { 103 UnixPath file = getName(obj); 104 UnixPath child = ds.directory().resolve(file); 105 boolean followLinks = Util.followLinks(options); 106 107 // permission check using name resolved against original path of directory 108 SecurityManager sm = System.getSecurityManager(); 109 if (sm != null) { 110 child.checkRead(); 111 } 112 113 ds.readLock().lock(); 114 try { 115 if (!ds.isOpen()) 116 throw new ClosedDirectoryStreamException(); 117 118 // open directory and create new secure directory stream 119 int newdfd1 = -1; 120 int newdfd2 = -1; 121 long ptr = 0L; 122 try { 123 int flags = O_RDONLY; 124 if (!followLinks) 125 flags |= O_NOFOLLOW; 126 newdfd1 = openat(dfd, file.asByteArray(), flags , 0); 127 newdfd2 = dup(newdfd1); 128 ptr = fdopendir(newdfd1); 129 } catch (UnixException x) { 130 if (newdfd1 != -1) 131 UnixNativeDispatcher.close(newdfd1); 132 if (newdfd2 != -1) 133 UnixNativeDispatcher.close(newdfd2); 134 if (x.errno() == UnixConstants.ENOTDIR) 135 throw new NotDirectoryException(file.toString()); 136 x.rethrowAsIOException(file); 137 } 138 return new UnixSecureDirectoryStream(child, ptr, newdfd2, null); 139 } finally { 140 ds.readLock().unlock(); 141 } 142 } 143 144 /** 145 * Opens file in this directory 146 */ 147 @Override newByteChannel(Path obj, Set<? extends OpenOption> options, FileAttribute<?>... attrs)148 public SeekableByteChannel newByteChannel(Path obj, 149 Set<? extends OpenOption> options, 150 FileAttribute<?>... attrs) 151 throws IOException 152 { 153 UnixPath file = getName(obj); 154 155 int mode = UnixFileModeAttribute 156 .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs); 157 158 // path for permission check 159 String pathToCheck = ds.directory().resolve(file).getPathForPermissionCheck(); 160 161 ds.readLock().lock(); 162 try { 163 if (!ds.isOpen()) 164 throw new ClosedDirectoryStreamException(); 165 try { 166 return UnixChannelFactory.newFileChannel(dfd, file, pathToCheck, options, mode); 167 } catch (UnixException x) { 168 x.rethrowAsIOException(file); 169 return null; // keep compiler happy 170 } 171 } finally { 172 ds.readLock().unlock(); 173 } 174 } 175 176 /** 177 * Deletes file/directory in this directory. Works in a race-free manner 178 * when invoked with flags. 179 */ implDelete(Path obj, boolean haveFlags, int flags)180 private void implDelete(Path obj, boolean haveFlags, int flags) 181 throws IOException 182 { 183 UnixPath file = getName(obj); 184 185 // permission check using name resolved against original path of directory 186 SecurityManager sm = System.getSecurityManager(); 187 if (sm != null) { 188 ds.directory().resolve(file).checkDelete(); 189 } 190 191 ds.readLock().lock(); 192 try { 193 if (!ds.isOpen()) 194 throw new ClosedDirectoryStreamException(); 195 196 if (!haveFlags) { 197 // need file attribute to know if file is directory. This creates 198 // a race in that the file may be replaced by a directory or a 199 // directory replaced by a file between the time we query the 200 // file type and unlink it. 201 UnixFileAttributes attrs = null; 202 try { 203 attrs = UnixFileAttributes.get(dfd, file, false); 204 } catch (UnixException x) { 205 x.rethrowAsIOException(file); 206 } 207 flags = (attrs.isDirectory()) ? AT_REMOVEDIR : 0; 208 } 209 210 try { 211 unlinkat(dfd, file.asByteArray(), flags); 212 } catch (UnixException x) { 213 if ((flags & AT_REMOVEDIR) != 0) { 214 if (x.errno() == EEXIST || x.errno() == ENOTEMPTY) { 215 throw new DirectoryNotEmptyException(null); 216 } 217 } 218 x.rethrowAsIOException(file); 219 } 220 } finally { 221 ds.readLock().unlock(); 222 } 223 } 224 225 @Override deleteFile(Path file)226 public void deleteFile(Path file) throws IOException { 227 implDelete(file, true, 0); 228 } 229 230 @Override deleteDirectory(Path dir)231 public void deleteDirectory(Path dir) throws IOException { 232 implDelete(dir, true, AT_REMOVEDIR); 233 } 234 235 /** 236 * Rename/move file in this directory to another (open) directory 237 */ 238 @Override move(Path fromObj, SecureDirectoryStream<Path> dir, Path toObj)239 public void move(Path fromObj, SecureDirectoryStream<Path> dir, Path toObj) 240 throws IOException 241 { 242 UnixPath from = getName(fromObj); 243 UnixPath to = getName(toObj); 244 if (dir == null) 245 throw new NullPointerException(); 246 if (!(dir instanceof UnixSecureDirectoryStream)) 247 throw new ProviderMismatchException(); 248 UnixSecureDirectoryStream that = (UnixSecureDirectoryStream)dir; 249 250 // permission check 251 SecurityManager sm = System.getSecurityManager(); 252 if (sm != null) { 253 this.ds.directory().resolve(from).checkWrite(); 254 that.ds.directory().resolve(to).checkWrite(); 255 } 256 257 // lock ordering doesn't matter 258 this.ds.readLock().lock(); 259 try { 260 that.ds.readLock().lock(); 261 try { 262 if (!this.ds.isOpen() || !that.ds.isOpen()) 263 throw new ClosedDirectoryStreamException(); 264 try { 265 renameat(this.dfd, from.asByteArray(), that.dfd, to.asByteArray()); 266 } catch (UnixException x) { 267 if (x.errno() == EXDEV) { 268 throw new AtomicMoveNotSupportedException( 269 from.toString(), to.toString(), x.errorString()); 270 } 271 x.rethrowAsIOException(from, to); 272 } 273 } finally { 274 that.ds.readLock().unlock(); 275 } 276 } finally { 277 this.ds.readLock().unlock(); 278 } 279 } 280 281 @SuppressWarnings("unchecked") getFileAttributeViewImpl(UnixPath file, Class<V> type, boolean followLinks)282 private <V extends FileAttributeView> V getFileAttributeViewImpl(UnixPath file, 283 Class<V> type, 284 boolean followLinks) 285 { 286 if (type == null) 287 throw new NullPointerException(); 288 Class<?> c = type; 289 if (c == BasicFileAttributeView.class) { 290 return (V) new BasicFileAttributeViewImpl(file, followLinks); 291 } 292 if (c == PosixFileAttributeView.class || c == FileOwnerAttributeView.class) { 293 return (V) new PosixFileAttributeViewImpl(file, followLinks); 294 } 295 // TBD - should also support AclFileAttributeView 296 return (V) null; 297 } 298 299 /** 300 * Returns file attribute view bound to this directory 301 */ 302 @Override getFileAttributeView(Class<V> type)303 public <V extends FileAttributeView> V getFileAttributeView(Class<V> type) { 304 return getFileAttributeViewImpl(null, type, false); 305 } 306 307 /** 308 * Returns file attribute view bound to dfd/filename. 309 */ 310 @Override getFileAttributeView(Path obj, Class<V> type, LinkOption... options)311 public <V extends FileAttributeView> V getFileAttributeView(Path obj, 312 Class<V> type, 313 LinkOption... options) 314 { 315 UnixPath file = getName(obj); 316 boolean followLinks = Util.followLinks(options); 317 return getFileAttributeViewImpl(file, type, followLinks); 318 } 319 320 /** 321 * A BasicFileAttributeView implementation that using a dfd/name pair. 322 */ 323 private class BasicFileAttributeViewImpl 324 implements BasicFileAttributeView 325 { 326 final UnixPath file; 327 final boolean followLinks; 328 BasicFileAttributeViewImpl(UnixPath file, boolean followLinks)329 BasicFileAttributeViewImpl(UnixPath file, boolean followLinks) 330 { 331 this.file = file; 332 this.followLinks = followLinks; 333 } 334 open()335 int open() throws IOException { 336 int oflags = O_RDONLY; 337 if (!followLinks) 338 oflags |= O_NOFOLLOW; 339 try { 340 return openat(dfd, file.asByteArray(), oflags, 0); 341 } catch (UnixException x) { 342 x.rethrowAsIOException(file); 343 return -1; // keep compiler happy 344 } 345 } 346 checkWriteAccess()347 private void checkWriteAccess() { 348 SecurityManager sm = System.getSecurityManager(); 349 if (sm != null) { 350 if (file == null) { 351 ds.directory().checkWrite(); 352 } else { 353 ds.directory().resolve(file).checkWrite(); 354 } 355 } 356 } 357 358 @Override name()359 public String name() { 360 return "basic"; 361 } 362 363 @Override readAttributes()364 public BasicFileAttributes readAttributes() throws IOException { 365 ds.readLock().lock(); 366 try { 367 if (!ds.isOpen()) 368 throw new ClosedDirectoryStreamException(); 369 370 SecurityManager sm = System.getSecurityManager(); 371 if (sm != null) { 372 if (file == null) { 373 ds.directory().checkRead(); 374 } else { 375 ds.directory().resolve(file).checkRead(); 376 } 377 } 378 try { 379 UnixFileAttributes attrs = (file == null) ? 380 UnixFileAttributes.get(dfd) : 381 UnixFileAttributes.get(dfd, file, followLinks); 382 383 // SECURITY: must return as BasicFileAttribute 384 return attrs.asBasicFileAttributes(); 385 } catch (UnixException x) { 386 x.rethrowAsIOException(file); 387 return null; // keep compiler happy 388 } 389 } finally { 390 ds.readLock().unlock(); 391 } 392 } 393 394 @Override setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime)395 public void setTimes(FileTime lastModifiedTime, 396 FileTime lastAccessTime, 397 FileTime createTime) // ignore 398 throws IOException 399 { 400 checkWriteAccess(); 401 402 ds.readLock().lock(); 403 try { 404 if (!ds.isOpen()) 405 throw new ClosedDirectoryStreamException(); 406 407 int fd = (file == null) ? dfd : open(); 408 try { 409 // if not changing both attributes then need existing attributes 410 if (lastModifiedTime == null || lastAccessTime == null) { 411 try { 412 UnixFileAttributes attrs = UnixFileAttributes.get(fd); 413 if (lastModifiedTime == null) 414 lastModifiedTime = attrs.lastModifiedTime(); 415 if (lastAccessTime == null) 416 lastAccessTime = attrs.lastAccessTime(); 417 } catch (UnixException x) { 418 x.rethrowAsIOException(file); 419 } 420 } 421 // update times 422 try { 423 futimes(fd, 424 lastAccessTime.to(TimeUnit.MICROSECONDS), 425 lastModifiedTime.to(TimeUnit.MICROSECONDS)); 426 } catch (UnixException x) { 427 x.rethrowAsIOException(file); 428 } 429 } finally { 430 if (file != null) 431 UnixNativeDispatcher.close(fd); 432 } 433 } finally { 434 ds.readLock().unlock(); 435 } 436 } 437 } 438 439 /** 440 * A PosixFileAttributeView implementation that using a dfd/name pair. 441 */ 442 private class PosixFileAttributeViewImpl 443 extends BasicFileAttributeViewImpl implements PosixFileAttributeView 444 { PosixFileAttributeViewImpl(UnixPath file, boolean followLinks)445 PosixFileAttributeViewImpl(UnixPath file, boolean followLinks) { 446 super(file, followLinks); 447 } 448 checkWriteAndUserAccess()449 private void checkWriteAndUserAccess() { 450 SecurityManager sm = System.getSecurityManager(); 451 if (sm != null) { 452 super.checkWriteAccess(); 453 sm.checkPermission(new RuntimePermission("accessUserInformation")); 454 } 455 } 456 457 @Override name()458 public String name() { 459 return "posix"; 460 } 461 462 @Override readAttributes()463 public PosixFileAttributes readAttributes() throws IOException { 464 SecurityManager sm = System.getSecurityManager(); 465 if (sm != null) { 466 if (file == null) 467 ds.directory().checkRead(); 468 else 469 ds.directory().resolve(file).checkRead(); 470 sm.checkPermission(new RuntimePermission("accessUserInformation")); 471 } 472 473 ds.readLock().lock(); 474 try { 475 if (!ds.isOpen()) 476 throw new ClosedDirectoryStreamException(); 477 478 try { 479 UnixFileAttributes attrs = (file == null) ? 480 UnixFileAttributes.get(dfd) : 481 UnixFileAttributes.get(dfd, file, followLinks); 482 return attrs; 483 } catch (UnixException x) { 484 x.rethrowAsIOException(file); 485 return null; // keep compiler happy 486 } 487 } finally { 488 ds.readLock().unlock(); 489 } 490 } 491 492 @Override setPermissions(Set<PosixFilePermission> perms)493 public void setPermissions(Set<PosixFilePermission> perms) 494 throws IOException 495 { 496 // permission check 497 checkWriteAndUserAccess(); 498 499 ds.readLock().lock(); 500 try { 501 if (!ds.isOpen()) 502 throw new ClosedDirectoryStreamException(); 503 504 int fd = (file == null) ? dfd : open(); 505 try { 506 fchmod(fd, UnixFileModeAttribute.toUnixMode(perms)); 507 } catch (UnixException x) { 508 x.rethrowAsIOException(file); 509 } finally { 510 if (file != null && fd >= 0) 511 UnixNativeDispatcher.close(fd); 512 } 513 } finally { 514 ds.readLock().unlock(); 515 } 516 } 517 setOwners(int uid, int gid)518 private void setOwners(int uid, int gid) throws IOException { 519 // permission check 520 checkWriteAndUserAccess(); 521 522 ds.readLock().lock(); 523 try { 524 if (!ds.isOpen()) 525 throw new ClosedDirectoryStreamException(); 526 527 int fd = (file == null) ? dfd : open(); 528 try { 529 fchown(fd, uid, gid); 530 } catch (UnixException x) { 531 x.rethrowAsIOException(file); 532 } finally { 533 if (file != null && fd >= 0) 534 UnixNativeDispatcher.close(fd); 535 } 536 } finally { 537 ds.readLock().unlock(); 538 } 539 } 540 541 @Override getOwner()542 public UserPrincipal getOwner() throws IOException { 543 return readAttributes().owner(); 544 } 545 546 @Override setOwner(UserPrincipal owner)547 public void setOwner(UserPrincipal owner) 548 throws IOException 549 { 550 if (!(owner instanceof UnixUserPrincipals.User)) 551 throw new ProviderMismatchException(); 552 if (owner instanceof UnixUserPrincipals.Group) 553 throw new IOException("'owner' parameter can't be a group"); 554 int uid = ((UnixUserPrincipals.User)owner).uid(); 555 setOwners(uid, -1); 556 } 557 558 @Override setGroup(GroupPrincipal group)559 public void setGroup(GroupPrincipal group) 560 throws IOException 561 { 562 if (!(group instanceof UnixUserPrincipals.Group)) 563 throw new ProviderMismatchException(); 564 int gid = ((UnixUserPrincipals.Group)group).gid(); 565 setOwners(-1, gid); 566 } 567 } 568 569 /** 570 * Cleans up if the user forgets to close it. 571 */ 572 // Android-changed: Add CloseGuard support. finalize()573 protected void finalize() throws IOException { 574 if (guard != null) { 575 guard.warnIfOpen(); 576 } 577 578 close(); 579 } 580 } 581