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 extends 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 = file.getFileSystem().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 = file.getFileSystem().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 ds.directory().resolve(file).checkWrite(); 340 } 341 } 342 343 @Override 344 public String name() { 345 return "basic"; 346 } 347 348 @Override 349 public BasicFileAttributes readAttributes() throws IOException { 350 ds.readLock().lock(); 351 try { 352 if (!ds.isOpen()) 353 throw new ClosedDirectoryStreamException(); 354 355 SecurityManager sm = System.getSecurityManager(); 356 if (sm != null) { 357 if (file == null) { 358 ds.directory().checkRead(); 359 } else { 360 ds.directory().resolve(file).checkRead(); 361 } 362 } 363 try { 364 UnixFileAttributes attrs = (file == null) ? 365 UnixFileAttributes.get(dfd) : 366 UnixFileAttributes.get(dfd, file, followLinks); 367 368 // SECURITY: must return as BasicFileAttribute 369 return attrs.asBasicFileAttributes(); 370 } catch (UnixException x) { 371 x.rethrowAsIOException(file); 372 return null; // keep compiler happy 373 } 374 } finally { 375 ds.readLock().unlock(); 376 } 377 } 378 379 @Override 380 public void setTimes(FileTime lastModifiedTime, 381 FileTime lastAccessTime, 382 FileTime createTime) // ignore 383 throws IOException 384 { 385 checkWriteAccess(); 386 387 ds.readLock().lock(); 388 try { 389 if (!ds.isOpen()) 390 throw new ClosedDirectoryStreamException(); 391 392 int fd = (file == null) ? dfd : open(); 393 try { 394 // if not changing both attributes then need existing attributes 395 if (lastModifiedTime == null || lastAccessTime == null) { 396 try { 397 UnixFileAttributes attrs = UnixFileAttributes.get(fd); 398 if (lastModifiedTime == null) 399 lastModifiedTime = attrs.lastModifiedTime(); 400 if (lastAccessTime == null) 401 lastAccessTime = attrs.lastAccessTime(); 402 } catch (UnixException x) { 403 x.rethrowAsIOException(file); 404 } 405 } 406 // update times 407 try { 408 futimes(fd, 409 lastAccessTime.to(TimeUnit.MICROSECONDS), 410 lastModifiedTime.to(TimeUnit.MICROSECONDS)); 411 } catch (UnixException x) { 412 x.rethrowAsIOException(file); 413 } 414 } finally { 415 if (file != null) 416 UnixNativeDispatcher.close(fd); 417 } 418 } finally { 419 ds.readLock().unlock(); 420 } 421 } 422 } 423 424 /** 425 * A PosixFileAttributeView implementation that using a dfd/name pair. 426 */ 427 private class PosixFileAttributeViewImpl 428 extends BasicFileAttributeViewImpl implements PosixFileAttributeView 429 { 430 PosixFileAttributeViewImpl(UnixPath file, boolean followLinks) { 431 super(file, followLinks); 432 } 433 434 private void checkWriteAndUserAccess() { 435 SecurityManager sm = System.getSecurityManager(); 436 if (sm != null) { 437 super.checkWriteAccess(); 438 sm.checkPermission(new RuntimePermission("accessUserInformation")); 439 } 440 } 441 442 @Override 443 public String name() { 444 return "posix"; 445 } 446 447 @Override 448 public PosixFileAttributes readAttributes() throws IOException { 449 SecurityManager sm = System.getSecurityManager(); 450 if (sm != null) { 451 if (file == null) 452 ds.directory().checkRead(); 453 else 454 ds.directory().resolve(file).checkRead(); 455 sm.checkPermission(new RuntimePermission("accessUserInformation")); 456 } 457 458 ds.readLock().lock(); 459 try { 460 if (!ds.isOpen()) 461 throw new ClosedDirectoryStreamException(); 462 463 try { 464 UnixFileAttributes attrs = (file == null) ? 465 UnixFileAttributes.get(dfd) : 466 UnixFileAttributes.get(dfd, file, followLinks); 467 return attrs; 468 } catch (UnixException x) { 469 x.rethrowAsIOException(file); 470 return null; // keep compiler happy 471 } 472 } finally { 473 ds.readLock().unlock(); 474 } 475 } 476 477 @Override 478 public void setPermissions(Set<PosixFilePermission> perms) 479 throws IOException 480 { 481 // permission check 482 checkWriteAndUserAccess(); 483 484 ds.readLock().lock(); 485 try { 486 if (!ds.isOpen()) 487 throw new ClosedDirectoryStreamException(); 488 489 int fd = (file == null) ? dfd : open(); 490 try { 491 fchmod(fd, UnixFileModeAttribute.toUnixMode(perms)); 492 } catch (UnixException x) { 493 x.rethrowAsIOException(file); 494 } finally { 495 if (file != null && fd >= 0) 496 UnixNativeDispatcher.close(fd); 497 } 498 } finally { 499 ds.readLock().unlock(); 500 } 501 } 502 503 private void setOwners(int uid, int gid) throws IOException { 504 // permission check 505 checkWriteAndUserAccess(); 506 507 ds.readLock().lock(); 508 try { 509 if (!ds.isOpen()) 510 throw new ClosedDirectoryStreamException(); 511 512 int fd = (file == null) ? dfd : open(); 513 try { 514 fchown(fd, uid, gid); 515 } catch (UnixException x) { 516 x.rethrowAsIOException(file); 517 } finally { 518 if (file != null && fd >= 0) 519 UnixNativeDispatcher.close(fd); 520 } 521 } finally { 522 ds.readLock().unlock(); 523 } 524 } 525 526 @Override 527 public UserPrincipal getOwner() throws IOException { 528 return readAttributes().owner(); 529 } 530 531 @Override 532 public void setOwner(UserPrincipal owner) 533 throws IOException 534 { 535 if (!(owner instanceof UnixUserPrincipals.User)) 536 throw new ProviderMismatchException(); 537 if (owner instanceof UnixUserPrincipals.Group) 538 throw new IOException("'owner' parameter can't be a group"); 539 int uid = ((UnixUserPrincipals.User)owner).uid(); 540 setOwners(uid, -1); 541 } 542 543 @Override 544 public void setGroup(GroupPrincipal group) 545 throws IOException 546 { 547 if (!(group instanceof UnixUserPrincipals.Group)) 548 throw new ProviderMismatchException(); 549 int gid = ((UnixUserPrincipals.Group)group).gid(); 550 setOwners(-1, gid); 551 } 552 } 553 }