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