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