001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 package org.apache.hadoop.fs; 019 020 021 import java.io.FileNotFoundException; 022 import java.io.IOException; 023 import java.lang.reflect.Constructor; 024 import java.net.URI; 025 import java.net.URISyntaxException; 026 import java.util.ArrayList; 027 import java.util.EnumSet; 028 import java.util.HashMap; 029 import java.util.List; 030 import java.util.Map; 031 import java.util.NoSuchElementException; 032 import java.util.StringTokenizer; 033 import java.util.concurrent.ConcurrentHashMap; 034 035 import org.apache.commons.logging.Log; 036 import org.apache.commons.logging.LogFactory; 037 import org.apache.hadoop.HadoopIllegalArgumentException; 038 import org.apache.hadoop.classification.InterfaceAudience; 039 import org.apache.hadoop.classification.InterfaceStability; 040 import org.apache.hadoop.conf.Configuration; 041 import org.apache.hadoop.fs.FileSystem.Statistics; 042 import org.apache.hadoop.fs.Options.CreateOpts; 043 import org.apache.hadoop.fs.Options.Rename; 044 import org.apache.hadoop.fs.permission.FsPermission; 045 import org.apache.hadoop.fs.InvalidPathException; 046 import org.apache.hadoop.security.AccessControlException; 047 import org.apache.hadoop.security.SecurityUtil; 048 import org.apache.hadoop.security.token.Token; 049 import org.apache.hadoop.util.Progressable; 050 051 /** 052 * This class provides an interface for implementors of a Hadoop file system 053 * (analogous to the VFS of Unix). Applications do not access this class; 054 * instead they access files across all file systems using {@link FileContext}. 055 * 056 * Pathnames passed to AbstractFileSystem can be fully qualified URI that 057 * matches the "this" file system (ie same scheme and authority) 058 * or a Slash-relative name that is assumed to be relative 059 * to the root of the "this" file system . 060 */ 061 @InterfaceAudience.Public 062 @InterfaceStability.Evolving /*Evolving for a release,to be changed to Stable */ 063 public abstract class AbstractFileSystem { 064 static final Log LOG = LogFactory.getLog(AbstractFileSystem.class); 065 066 /** Recording statistics per a file system class. */ 067 private static final Map<URI, Statistics> 068 STATISTICS_TABLE = new HashMap<URI, Statistics>(); 069 070 /** Cache of constructors for each file system class. */ 071 private static final Map<Class<?>, Constructor<?>> CONSTRUCTOR_CACHE = 072 new ConcurrentHashMap<Class<?>, Constructor<?>>(); 073 074 private static final Class<?>[] URI_CONFIG_ARGS = 075 new Class[]{URI.class, Configuration.class}; 076 077 /** The statistics for this file system. */ 078 protected Statistics statistics; 079 080 private final URI myUri; 081 082 public Statistics getStatistics() { 083 return statistics; 084 } 085 086 /** 087 * Prohibits names which contain a ".", "..", ":" or "/" 088 */ 089 private static boolean isValidName(String src) { 090 // Check for ".." "." ":" "/" 091 StringTokenizer tokens = new StringTokenizer(src, Path.SEPARATOR); 092 while(tokens.hasMoreTokens()) { 093 String element = tokens.nextToken(); 094 if (element.equals("target/generated-sources") || 095 element.equals(".") || 096 (element.indexOf(":") >= 0)) { 097 return false; 098 } 099 } 100 return true; 101 } 102 103 /** 104 * Create an object for the given class and initialize it from conf. 105 * @param theClass class of which an object is created 106 * @param conf Configuration 107 * @return a new object 108 */ 109 @SuppressWarnings("unchecked") 110 static <T> T newInstance(Class<T> theClass, 111 URI uri, Configuration conf) { 112 T result; 113 try { 114 Constructor<T> meth = (Constructor<T>) CONSTRUCTOR_CACHE.get(theClass); 115 if (meth == null) { 116 meth = theClass.getDeclaredConstructor(URI_CONFIG_ARGS); 117 meth.setAccessible(true); 118 CONSTRUCTOR_CACHE.put(theClass, meth); 119 } 120 result = meth.newInstance(uri, conf); 121 } catch (Exception e) { 122 throw new RuntimeException(e); 123 } 124 return result; 125 } 126 127 /** 128 * Create a file system instance for the specified uri using the conf. The 129 * conf is used to find the class name that implements the file system. The 130 * conf is also passed to the file system for its configuration. 131 * 132 * @param uri URI of the file system 133 * @param conf Configuration for the file system 134 * 135 * @return Returns the file system for the given URI 136 * 137 * @throws UnsupportedFileSystemException file system for <code>uri</code> is 138 * not found 139 */ 140 public static AbstractFileSystem createFileSystem(URI uri, Configuration conf) 141 throws UnsupportedFileSystemException { 142 Class<?> clazz = conf.getClass("fs.AbstractFileSystem." + 143 uri.getScheme() + ".impl", null); 144 if (clazz == null) { 145 throw new UnsupportedFileSystemException( 146 "No AbstractFileSystem for scheme: " + uri.getScheme()); 147 } 148 return (AbstractFileSystem) newInstance(clazz, uri, conf); 149 } 150 151 /** 152 * Get the statistics for a particular file system. 153 * 154 * @param uri 155 * used as key to lookup STATISTICS_TABLE. Only scheme and authority 156 * part of the uri are used. 157 * @return a statistics object 158 */ 159 protected static synchronized Statistics getStatistics(URI uri) { 160 String scheme = uri.getScheme(); 161 if (scheme == null) { 162 throw new IllegalArgumentException("Scheme not defined in the uri: " 163 + uri); 164 } 165 URI baseUri = getBaseUri(uri); 166 Statistics result = STATISTICS_TABLE.get(baseUri); 167 if (result == null) { 168 result = new Statistics(scheme); 169 STATISTICS_TABLE.put(baseUri, result); 170 } 171 return result; 172 } 173 174 private static URI getBaseUri(URI uri) { 175 String scheme = uri.getScheme(); 176 String authority = uri.getAuthority(); 177 String baseUriString = scheme + "://"; 178 if (authority != null) { 179 baseUriString = baseUriString + authority; 180 } else { 181 baseUriString = baseUriString + "/"; 182 } 183 return URI.create(baseUriString); 184 } 185 186 public static synchronized void clearStatistics() { 187 for(Statistics stat: STATISTICS_TABLE.values()) { 188 stat.reset(); 189 } 190 } 191 192 /** 193 * Prints statistics for all file systems. 194 */ 195 public static synchronized void printStatistics() { 196 for (Map.Entry<URI, Statistics> pair : STATISTICS_TABLE.entrySet()) { 197 System.out.println(" FileSystem " + pair.getKey().getScheme() + "://" 198 + pair.getKey().getAuthority() + ": " + pair.getValue()); 199 } 200 } 201 202 protected static synchronized Map<URI, Statistics> getAllStatistics() { 203 Map<URI, Statistics> statsMap = new HashMap<URI, Statistics>( 204 STATISTICS_TABLE.size()); 205 for (Map.Entry<URI, Statistics> pair : STATISTICS_TABLE.entrySet()) { 206 URI key = pair.getKey(); 207 Statistics value = pair.getValue(); 208 Statistics newStatsObj = new Statistics(value); 209 statsMap.put(URI.create(key.toString()), newStatsObj); 210 } 211 return statsMap; 212 } 213 214 /** 215 * The main factory method for creating a file system. Get a file system for 216 * the URI's scheme and authority. The scheme of the <code>uri</code> 217 * determines a configuration property name, 218 * <tt>fs.AbstractFileSystem.<i>scheme</i>.impl</tt> whose value names the 219 * AbstractFileSystem class. 220 * 221 * The entire URI and conf is passed to the AbstractFileSystem factory method. 222 * 223 * @param uri for the file system to be created. 224 * @param conf which is passed to the file system impl. 225 * 226 * @return file system for the given URI. 227 * 228 * @throws UnsupportedFileSystemException if the file system for 229 * <code>uri</code> is not supported. 230 */ 231 public static AbstractFileSystem get(final URI uri, final Configuration conf) 232 throws UnsupportedFileSystemException { 233 return createFileSystem(uri, conf); 234 } 235 236 /** 237 * Constructor to be called by subclasses. 238 * 239 * @param uri for this file system. 240 * @param supportedScheme the scheme supported by the implementor 241 * @param authorityNeeded if true then theURI must have authority, if false 242 * then the URI must have null authority. 243 * 244 * @throws URISyntaxException <code>uri</code> has syntax error 245 */ 246 public AbstractFileSystem(final URI uri, final String supportedScheme, 247 final boolean authorityNeeded, final int defaultPort) 248 throws URISyntaxException { 249 myUri = getUri(uri, supportedScheme, authorityNeeded, defaultPort); 250 statistics = getStatistics(uri); 251 } 252 253 /** 254 * Check that the Uri's scheme matches 255 * @param uri 256 * @param supportedScheme 257 */ 258 public void checkScheme(URI uri, String supportedScheme) { 259 String scheme = uri.getScheme(); 260 if (scheme == null) { 261 throw new HadoopIllegalArgumentException("Uri without scheme: " + uri); 262 } 263 if (!scheme.equals(supportedScheme)) { 264 throw new HadoopIllegalArgumentException("Uri scheme " + uri 265 + " does not match the scheme " + supportedScheme); 266 } 267 } 268 269 /** 270 * Get the URI for the file system based on the given URI. The path, query 271 * part of the given URI is stripped out and default file system port is used 272 * to form the URI. 273 * 274 * @param uri FileSystem URI. 275 * @param authorityNeeded if true authority cannot be null in the URI. If 276 * false authority must be null. 277 * @param defaultPort default port to use if port is not specified in the URI. 278 * 279 * @return URI of the file system 280 * 281 * @throws URISyntaxException <code>uri</code> has syntax error 282 */ 283 private URI getUri(URI uri, String supportedScheme, 284 boolean authorityNeeded, int defaultPort) throws URISyntaxException { 285 checkScheme(uri, supportedScheme); 286 // A file system implementation that requires authority must always 287 // specify default port 288 if (defaultPort < 0 && authorityNeeded) { 289 throw new HadoopIllegalArgumentException( 290 "FileSystem implementation error - default port " + defaultPort 291 + " is not valid"); 292 } 293 String authority = uri.getAuthority(); 294 if (authority == null) { 295 if (authorityNeeded) { 296 throw new HadoopIllegalArgumentException("Uri without authority: " + uri); 297 } else { 298 return new URI(supportedScheme + ":///"); 299 } 300 } 301 // authority is non null - AuthorityNeeded may be true or false. 302 int port = uri.getPort(); 303 port = (port == -1 ? defaultPort : port); 304 if (port == -1) { // no port supplied and default port is not specified 305 return new URI(supportedScheme, authority, "/", null); 306 } 307 return new URI(supportedScheme + "://" + uri.getHost() + ":" + port); 308 } 309 310 /** 311 * The default port of this file system. 312 * 313 * @return default port of this file system's Uri scheme 314 * A uri with a port of -1 => default port; 315 */ 316 public abstract int getUriDefaultPort(); 317 318 /** 319 * Returns a URI whose scheme and authority identify this FileSystem. 320 * 321 * @return the uri of this file system. 322 */ 323 public URI getUri() { 324 return myUri; 325 } 326 327 /** 328 * Check that a Path belongs to this FileSystem. 329 * 330 * If the path is fully qualified URI, then its scheme and authority 331 * matches that of this file system. Otherwise the path must be 332 * slash-relative name. 333 * 334 * @throws InvalidPathException if the path is invalid 335 */ 336 public void checkPath(Path path) { 337 URI uri = path.toUri(); 338 String thatScheme = uri.getScheme(); 339 String thatAuthority = uri.getAuthority(); 340 if (thatScheme == null) { 341 if (thatAuthority == null) { 342 if (path.isUriPathAbsolute()) { 343 return; 344 } 345 throw new InvalidPathException("relative paths not allowed:" + 346 path); 347 } else { 348 throw new InvalidPathException( 349 "Path without scheme with non-null authority:" + path); 350 } 351 } 352 String thisScheme = this.getUri().getScheme(); 353 String thisHost = this.getUri().getHost(); 354 String thatHost = uri.getHost(); 355 356 // Schemes and hosts must match. 357 // Allow for null Authority for file:/// 358 if (!thisScheme.equalsIgnoreCase(thatScheme) || 359 (thisHost != null && 360 !thisHost.equalsIgnoreCase(thatHost)) || 361 (thisHost == null && thatHost != null)) { 362 throw new InvalidPathException("Wrong FS: " + path + ", expected: " 363 + this.getUri()); 364 } 365 366 // Ports must match, unless this FS instance is using the default port, in 367 // which case the port may be omitted from the given URI 368 int thisPort = this.getUri().getPort(); 369 int thatPort = uri.getPort(); 370 if (thatPort == -1) { // -1 => defaultPort of Uri scheme 371 thatPort = this.getUriDefaultPort(); 372 } 373 if (thisPort != thatPort) { 374 throw new InvalidPathException("Wrong FS: " + path + ", expected: " 375 + this.getUri()); 376 } 377 } 378 379 /** 380 * Get the path-part of a pathname. Checks that URI matches this file system 381 * and that the path-part is a valid name. 382 * 383 * @param p path 384 * 385 * @return path-part of the Path p 386 */ 387 public String getUriPath(final Path p) { 388 checkPath(p); 389 String s = p.toUri().getPath(); 390 if (!isValidName(s)) { 391 throw new InvalidPathException("Path part " + s + " from URI " + p 392 + " is not a valid filename."); 393 } 394 return s; 395 } 396 397 /** 398 * Make the path fully qualified to this file system 399 * @param path 400 * @return the qualified path 401 */ 402 public Path makeQualified(Path path) { 403 checkPath(path); 404 return path.makeQualified(this.getUri(), null); 405 } 406 407 /** 408 * Some file systems like LocalFileSystem have an initial workingDir 409 * that is used as the starting workingDir. For other file systems 410 * like HDFS there is no built in notion of an initial workingDir. 411 * 412 * @return the initial workingDir if the file system has such a notion 413 * otherwise return a null. 414 */ 415 public Path getInitialWorkingDirectory() { 416 return null; 417 } 418 419 /** 420 * Return the current user's home directory in this file system. 421 * The default implementation returns "/user/$USER/". 422 * 423 * @return current user's home directory. 424 */ 425 public Path getHomeDirectory() { 426 return new Path("/user/"+System.getProperty("user.name")).makeQualified( 427 getUri(), null); 428 } 429 430 /** 431 * Return a set of server default configuration values. 432 * 433 * @return server default configuration values 434 * 435 * @throws IOException an I/O error occurred 436 */ 437 public abstract FsServerDefaults getServerDefaults() throws IOException; 438 439 /** 440 * Return the fully-qualified path of path f resolving the path 441 * through any internal symlinks or mount point 442 * @param p path to be resolved 443 * @return fully qualified path 444 * @throws FileNotFoundException, AccessControlException, IOException 445 * UnresolvedLinkException if symbolic link on path cannot be resolved 446 * internally 447 */ 448 public Path resolvePath(final Path p) throws FileNotFoundException, 449 UnresolvedLinkException, AccessControlException, IOException { 450 checkPath(p); 451 return getFileStatus(p).getPath(); // default impl is to return the path 452 } 453 454 /** 455 * The specification of this method matches that of 456 * {@link FileContext#create(Path, EnumSet, Options.CreateOpts...)} except 457 * that the Path f must be fully qualified and the permission is absolute 458 * (i.e. umask has been applied). 459 */ 460 public final FSDataOutputStream create(final Path f, 461 final EnumSet<CreateFlag> createFlag, Options.CreateOpts... opts) 462 throws AccessControlException, FileAlreadyExistsException, 463 FileNotFoundException, ParentNotDirectoryException, 464 UnsupportedFileSystemException, UnresolvedLinkException, IOException { 465 checkPath(f); 466 int bufferSize = -1; 467 short replication = -1; 468 long blockSize = -1; 469 int bytesPerChecksum = -1; 470 FsPermission permission = null; 471 Progressable progress = null; 472 Boolean createParent = null; 473 474 for (CreateOpts iOpt : opts) { 475 if (CreateOpts.BlockSize.class.isInstance(iOpt)) { 476 if (blockSize != -1) { 477 throw new HadoopIllegalArgumentException( 478 "BlockSize option is set multiple times"); 479 } 480 blockSize = ((CreateOpts.BlockSize) iOpt).getValue(); 481 } else if (CreateOpts.BufferSize.class.isInstance(iOpt)) { 482 if (bufferSize != -1) { 483 throw new HadoopIllegalArgumentException( 484 "BufferSize option is set multiple times"); 485 } 486 bufferSize = ((CreateOpts.BufferSize) iOpt).getValue(); 487 } else if (CreateOpts.ReplicationFactor.class.isInstance(iOpt)) { 488 if (replication != -1) { 489 throw new HadoopIllegalArgumentException( 490 "ReplicationFactor option is set multiple times"); 491 } 492 replication = ((CreateOpts.ReplicationFactor) iOpt).getValue(); 493 } else if (CreateOpts.BytesPerChecksum.class.isInstance(iOpt)) { 494 if (bytesPerChecksum != -1) { 495 throw new HadoopIllegalArgumentException( 496 "BytesPerChecksum option is set multiple times"); 497 } 498 bytesPerChecksum = ((CreateOpts.BytesPerChecksum) iOpt).getValue(); 499 } else if (CreateOpts.Perms.class.isInstance(iOpt)) { 500 if (permission != null) { 501 throw new HadoopIllegalArgumentException( 502 "Perms option is set multiple times"); 503 } 504 permission = ((CreateOpts.Perms) iOpt).getValue(); 505 } else if (CreateOpts.Progress.class.isInstance(iOpt)) { 506 if (progress != null) { 507 throw new HadoopIllegalArgumentException( 508 "Progress option is set multiple times"); 509 } 510 progress = ((CreateOpts.Progress) iOpt).getValue(); 511 } else if (CreateOpts.CreateParent.class.isInstance(iOpt)) { 512 if (createParent != null) { 513 throw new HadoopIllegalArgumentException( 514 "CreateParent option is set multiple times"); 515 } 516 createParent = ((CreateOpts.CreateParent) iOpt).getValue(); 517 } else { 518 throw new HadoopIllegalArgumentException("Unkown CreateOpts of type " + 519 iOpt.getClass().getName()); 520 } 521 } 522 if (permission == null) { 523 throw new HadoopIllegalArgumentException("no permission supplied"); 524 } 525 526 527 FsServerDefaults ssDef = getServerDefaults(); 528 if (ssDef.getBlockSize() % ssDef.getBytesPerChecksum() != 0) { 529 throw new IOException("Internal error: default blockSize is" + 530 " not a multiple of default bytesPerChecksum "); 531 } 532 533 if (blockSize == -1) { 534 blockSize = ssDef.getBlockSize(); 535 } 536 if (bytesPerChecksum == -1) { 537 bytesPerChecksum = ssDef.getBytesPerChecksum(); 538 } 539 if (bufferSize == -1) { 540 bufferSize = ssDef.getFileBufferSize(); 541 } 542 if (replication == -1) { 543 replication = ssDef.getReplication(); 544 } 545 if (createParent == null) { 546 createParent = false; 547 } 548 549 if (blockSize % bytesPerChecksum != 0) { 550 throw new HadoopIllegalArgumentException( 551 "blockSize should be a multiple of checksumsize"); 552 } 553 554 return this.createInternal(f, createFlag, permission, bufferSize, 555 replication, blockSize, progress, bytesPerChecksum, createParent); 556 } 557 558 /** 559 * The specification of this method matches that of 560 * {@link #create(Path, EnumSet, Options.CreateOpts...)} except that the opts 561 * have been declared explicitly. 562 */ 563 public abstract FSDataOutputStream createInternal(Path f, 564 EnumSet<CreateFlag> flag, FsPermission absolutePermission, 565 int bufferSize, short replication, long blockSize, Progressable progress, 566 int bytesPerChecksum, boolean createParent) 567 throws AccessControlException, FileAlreadyExistsException, 568 FileNotFoundException, ParentNotDirectoryException, 569 UnsupportedFileSystemException, UnresolvedLinkException, IOException; 570 571 /** 572 * The specification of this method matches that of 573 * {@link FileContext#mkdir(Path, FsPermission, boolean)} except that the Path 574 * f must be fully qualified and the permission is absolute (i.e. 575 * umask has been applied). 576 */ 577 public abstract void mkdir(final Path dir, final FsPermission permission, 578 final boolean createParent) throws AccessControlException, 579 FileAlreadyExistsException, FileNotFoundException, 580 UnresolvedLinkException, IOException; 581 582 /** 583 * The specification of this method matches that of 584 * {@link FileContext#delete(Path, boolean)} except that Path f must be for 585 * this file system. 586 */ 587 public abstract boolean delete(final Path f, final boolean recursive) 588 throws AccessControlException, FileNotFoundException, 589 UnresolvedLinkException, IOException; 590 591 /** 592 * The specification of this method matches that of 593 * {@link FileContext#open(Path)} except that Path f must be for this 594 * file system. 595 */ 596 public FSDataInputStream open(final Path f) throws AccessControlException, 597 FileNotFoundException, UnresolvedLinkException, IOException { 598 return open(f, getServerDefaults().getFileBufferSize()); 599 } 600 601 /** 602 * The specification of this method matches that of 603 * {@link FileContext#open(Path, int)} except that Path f must be for this 604 * file system. 605 */ 606 public abstract FSDataInputStream open(final Path f, int bufferSize) 607 throws AccessControlException, FileNotFoundException, 608 UnresolvedLinkException, IOException; 609 610 /** 611 * The specification of this method matches that of 612 * {@link FileContext#setReplication(Path, short)} except that Path f must be 613 * for this file system. 614 */ 615 public abstract boolean setReplication(final Path f, 616 final short replication) throws AccessControlException, 617 FileNotFoundException, UnresolvedLinkException, IOException; 618 619 /** 620 * The specification of this method matches that of 621 * {@link FileContext#rename(Path, Path, Options.Rename...)} except that Path 622 * f must be for this file system. 623 */ 624 public final void rename(final Path src, final Path dst, 625 final Options.Rename... options) throws AccessControlException, 626 FileAlreadyExistsException, FileNotFoundException, 627 ParentNotDirectoryException, UnresolvedLinkException, IOException { 628 boolean overwrite = false; 629 if (null != options) { 630 for (Rename option : options) { 631 if (option == Rename.OVERWRITE) { 632 overwrite = true; 633 } 634 } 635 } 636 renameInternal(src, dst, overwrite); 637 } 638 639 /** 640 * The specification of this method matches that of 641 * {@link FileContext#rename(Path, Path, Options.Rename...)} except that Path 642 * f must be for this file system and NO OVERWRITE is performed. 643 * 644 * File systems that do not have a built in overwrite need implement only this 645 * method and can take advantage of the default impl of the other 646 * {@link #renameInternal(Path, Path, boolean)} 647 */ 648 public abstract void renameInternal(final Path src, final Path dst) 649 throws AccessControlException, FileAlreadyExistsException, 650 FileNotFoundException, ParentNotDirectoryException, 651 UnresolvedLinkException, IOException; 652 653 /** 654 * The specification of this method matches that of 655 * {@link FileContext#rename(Path, Path, Options.Rename...)} except that Path 656 * f must be for this file system. 657 */ 658 public void renameInternal(final Path src, final Path dst, 659 boolean overwrite) throws AccessControlException, 660 FileAlreadyExistsException, FileNotFoundException, 661 ParentNotDirectoryException, UnresolvedLinkException, IOException { 662 // Default implementation deals with overwrite in a non-atomic way 663 final FileStatus srcStatus = getFileLinkStatus(src); 664 665 FileStatus dstStatus; 666 try { 667 dstStatus = getFileLinkStatus(dst); 668 } catch (IOException e) { 669 dstStatus = null; 670 } 671 if (dstStatus != null) { 672 if (dst.equals(src)) { 673 throw new FileAlreadyExistsException( 674 "The source "+src+" and destination "+dst+" are the same"); 675 } 676 if (srcStatus.isSymlink() && dst.equals(srcStatus.getSymlink())) { 677 throw new FileAlreadyExistsException( 678 "Cannot rename symlink "+src+" to its target "+dst); 679 } 680 // It's OK to rename a file to a symlink and vice versa 681 if (srcStatus.isDirectory() != dstStatus.isDirectory()) { 682 throw new IOException("Source " + src + " and destination " + dst 683 + " must both be directories"); 684 } 685 if (!overwrite) { 686 throw new FileAlreadyExistsException("Rename destination " + dst 687 + " already exists."); 688 } 689 // Delete the destination that is a file or an empty directory 690 if (dstStatus.isDirectory()) { 691 RemoteIterator<FileStatus> list = listStatusIterator(dst); 692 if (list != null && list.hasNext()) { 693 throw new IOException( 694 "Rename cannot overwrite non empty destination directory " + dst); 695 } 696 } 697 delete(dst, false); 698 } else { 699 final Path parent = dst.getParent(); 700 final FileStatus parentStatus = getFileStatus(parent); 701 if (parentStatus.isFile()) { 702 throw new ParentNotDirectoryException("Rename destination parent " 703 + parent + " is a file."); 704 } 705 } 706 renameInternal(src, dst); 707 } 708 709 /** 710 * Returns true if the file system supports symlinks, false otherwise. 711 */ 712 public boolean supportsSymlinks() { 713 return false; 714 } 715 716 /** 717 * The specification of this method matches that of 718 * {@link FileContext#createSymlink(Path, Path, boolean)}; 719 */ 720 public void createSymlink(final Path target, final Path link, 721 final boolean createParent) throws IOException, UnresolvedLinkException { 722 throw new IOException("File system does not support symlinks"); 723 } 724 725 /** 726 * The specification of this method matches that of 727 * {@link FileContext#getLinkTarget(Path)}; 728 */ 729 public Path getLinkTarget(final Path f) throws IOException { 730 /* We should never get here. Any file system that threw an 731 * UnresolvedLinkException, causing this function to be called, 732 * needs to override this method. 733 */ 734 throw new AssertionError(); 735 } 736 737 /** 738 * The specification of this method matches that of 739 * {@link FileContext#setPermission(Path, FsPermission)} except that Path f 740 * must be for this file system. 741 */ 742 public abstract void setPermission(final Path f, 743 final FsPermission permission) throws AccessControlException, 744 FileNotFoundException, UnresolvedLinkException, IOException; 745 746 /** 747 * The specification of this method matches that of 748 * {@link FileContext#setOwner(Path, String, String)} except that Path f must 749 * be for this file system. 750 */ 751 public abstract void setOwner(final Path f, final String username, 752 final String groupname) throws AccessControlException, 753 FileNotFoundException, UnresolvedLinkException, IOException; 754 755 /** 756 * The specification of this method matches that of 757 * {@link FileContext#setTimes(Path, long, long)} except that Path f must be 758 * for this file system. 759 */ 760 public abstract void setTimes(final Path f, final long mtime, 761 final long atime) throws AccessControlException, FileNotFoundException, 762 UnresolvedLinkException, IOException; 763 764 /** 765 * The specification of this method matches that of 766 * {@link FileContext#getFileChecksum(Path)} except that Path f must be for 767 * this file system. 768 */ 769 public abstract FileChecksum getFileChecksum(final Path f) 770 throws AccessControlException, FileNotFoundException, 771 UnresolvedLinkException, IOException; 772 773 /** 774 * The specification of this method matches that of 775 * {@link FileContext#getFileStatus(Path)} 776 * except that an UnresolvedLinkException may be thrown if a symlink is 777 * encountered in the path. 778 */ 779 public abstract FileStatus getFileStatus(final Path f) 780 throws AccessControlException, FileNotFoundException, 781 UnresolvedLinkException, IOException; 782 783 /** 784 * The specification of this method matches that of 785 * {@link FileContext#getFileLinkStatus(Path)} 786 * except that an UnresolvedLinkException may be thrown if a symlink is 787 * encountered in the path leading up to the final path component. 788 * If the file system does not support symlinks then the behavior is 789 * equivalent to {@link AbstractFileSystem#getFileStatus(Path)}. 790 */ 791 public FileStatus getFileLinkStatus(final Path f) 792 throws AccessControlException, FileNotFoundException, 793 UnsupportedFileSystemException, IOException { 794 return getFileStatus(f); 795 } 796 797 /** 798 * The specification of this method matches that of 799 * {@link FileContext#getFileBlockLocations(Path, long, long)} except that 800 * Path f must be for this file system. 801 */ 802 public abstract BlockLocation[] getFileBlockLocations(final Path f, 803 final long start, final long len) throws AccessControlException, 804 FileNotFoundException, UnresolvedLinkException, IOException; 805 806 /** 807 * The specification of this method matches that of 808 * {@link FileContext#getFsStatus(Path)} except that Path f must be for this 809 * file system. 810 */ 811 public FsStatus getFsStatus(final Path f) throws AccessControlException, 812 FileNotFoundException, UnresolvedLinkException, IOException { 813 // default impl gets FsStatus of root 814 return getFsStatus(); 815 } 816 817 /** 818 * The specification of this method matches that of 819 * {@link FileContext#getFsStatus(Path)}. 820 */ 821 public abstract FsStatus getFsStatus() throws AccessControlException, 822 FileNotFoundException, IOException; 823 824 /** 825 * The specification of this method matches that of 826 * {@link FileContext#listStatus(Path)} except that Path f must be for this 827 * file system. 828 */ 829 public RemoteIterator<FileStatus> listStatusIterator(final Path f) 830 throws AccessControlException, FileNotFoundException, 831 UnresolvedLinkException, IOException { 832 return new RemoteIterator<FileStatus>() { 833 private int i = 0; 834 private FileStatus[] statusList = listStatus(f); 835 836 @Override 837 public boolean hasNext() { 838 return i < statusList.length; 839 } 840 841 @Override 842 public FileStatus next() { 843 if (!hasNext()) { 844 throw new NoSuchElementException(); 845 } 846 return statusList[i++]; 847 } 848 }; 849 } 850 851 /** 852 * The specification of this method matches that of 853 * {@link FileContext#listLocatedStatus(Path)} except that Path f 854 * must be for this file system. 855 */ 856 public RemoteIterator<LocatedFileStatus> listLocatedStatus(final Path f) 857 throws AccessControlException, FileNotFoundException, 858 UnresolvedLinkException, IOException { 859 return new RemoteIterator<LocatedFileStatus>() { 860 private RemoteIterator<FileStatus> itor = listStatusIterator(f); 861 862 @Override 863 public boolean hasNext() throws IOException { 864 return itor.hasNext(); 865 } 866 867 @Override 868 public LocatedFileStatus next() throws IOException { 869 if (!hasNext()) { 870 throw new NoSuchElementException("No more entry in " + f); 871 } 872 FileStatus result = itor.next(); 873 BlockLocation[] locs = null; 874 if (result.isFile()) { 875 locs = getFileBlockLocations( 876 result.getPath(), 0, result.getLen()); 877 } 878 return new LocatedFileStatus(result, locs); 879 } 880 }; 881 } 882 883 /** 884 * The specification of this method matches that of 885 * {@link FileContext.Util#listStatus(Path)} except that Path f must be 886 * for this file system. 887 */ 888 public abstract FileStatus[] listStatus(final Path f) 889 throws AccessControlException, FileNotFoundException, 890 UnresolvedLinkException, IOException; 891 892 /** 893 * @return an iterator over the corrupt files under the given path 894 * (may contain duplicates if a file has more than one corrupt block) 895 * @throws IOException 896 */ 897 public RemoteIterator<Path> listCorruptFileBlocks(Path path) 898 throws IOException { 899 throw new UnsupportedOperationException(getClass().getCanonicalName() + 900 " does not support" + 901 " listCorruptFileBlocks"); 902 } 903 904 /** 905 * The specification of this method matches that of 906 * {@link FileContext#setVerifyChecksum(boolean, Path)} except that Path f 907 * must be for this file system. 908 */ 909 public abstract void setVerifyChecksum(final boolean verifyChecksum) 910 throws AccessControlException, IOException; 911 912 /** 913 * Get a canonical name for this file system. 914 * @return a URI string that uniquely identifies this file system 915 */ 916 public String getCanonicalServiceName() { 917 return SecurityUtil.buildDTServiceName(getUri(), getUriDefaultPort()); 918 } 919 920 /** 921 * Get one or more delegation tokens associated with the filesystem. Normally 922 * a file system returns a single delegation token. A file system that manages 923 * multiple file systems underneath, could return set of delegation tokens for 924 * all the file systems it manages 925 * 926 * @param renewer the account name that is allowed to renew the token. 927 * @return List of delegation tokens. 928 * If delegation tokens not supported then return a list of size zero. 929 * @throws IOException 930 */ 931 @InterfaceAudience.LimitedPrivate( { "HDFS", "MapReduce" }) 932 public List<Token<?>> getDelegationTokens(String renewer) throws IOException { 933 return new ArrayList<Token<?>>(0); 934 } 935 936 @Override //Object 937 public int hashCode() { 938 return myUri.hashCode(); 939 } 940 941 @Override //Object 942 public boolean equals(Object other) { 943 if (other == null || !(other instanceof AbstractFileSystem)) { 944 return false; 945 } 946 return myUri.equals(((AbstractFileSystem) other).myUri); 947 } 948 }