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 */ 018package org.apache.hadoop.fs; 019 020import java.io.Closeable; 021import java.io.FileNotFoundException; 022import java.io.IOException; 023import java.lang.ref.PhantomReference; 024import java.lang.ref.ReferenceQueue; 025import java.net.URI; 026import java.net.URISyntaxException; 027import java.security.PrivilegedExceptionAction; 028import java.util.ArrayList; 029import java.util.Arrays; 030import java.util.Collection; 031import java.util.EnumSet; 032import java.util.HashMap; 033import java.util.HashSet; 034import java.util.IdentityHashMap; 035import java.util.Iterator; 036import java.util.List; 037import java.util.Map; 038import java.util.NoSuchElementException; 039import java.util.ServiceLoader; 040import java.util.Set; 041import java.util.Stack; 042import java.util.TreeSet; 043import java.util.concurrent.atomic.AtomicLong; 044 045import org.apache.commons.logging.Log; 046import org.apache.commons.logging.LogFactory; 047import org.apache.hadoop.classification.InterfaceAudience; 048import org.apache.hadoop.classification.InterfaceStability; 049import org.apache.hadoop.conf.Configuration; 050import org.apache.hadoop.conf.Configured; 051import org.apache.hadoop.fs.Options.ChecksumOpt; 052import org.apache.hadoop.fs.Options.Rename; 053import org.apache.hadoop.fs.permission.AclEntry; 054import org.apache.hadoop.fs.permission.AclStatus; 055import org.apache.hadoop.fs.permission.FsAction; 056import org.apache.hadoop.fs.permission.FsPermission; 057import org.apache.hadoop.io.MultipleIOException; 058import org.apache.hadoop.io.Text; 059import org.apache.hadoop.net.NetUtils; 060import org.apache.hadoop.security.AccessControlException; 061import org.apache.hadoop.security.Credentials; 062import org.apache.hadoop.security.SecurityUtil; 063import org.apache.hadoop.security.UserGroupInformation; 064import org.apache.hadoop.security.token.Token; 065import org.apache.hadoop.util.DataChecksum; 066import org.apache.hadoop.util.Progressable; 067import org.apache.hadoop.util.ReflectionUtils; 068import org.apache.hadoop.util.ShutdownHookManager; 069import org.apache.htrace.core.Tracer; 070import org.apache.htrace.core.TraceScope; 071 072import com.google.common.annotations.VisibleForTesting; 073 074/**************************************************************** 075 * An abstract base class for a fairly generic filesystem. It 076 * may be implemented as a distributed filesystem, or as a "local" 077 * one that reflects the locally-connected disk. The local version 078 * exists for small Hadoop instances and for testing. 079 * 080 * <p> 081 * 082 * All user code that may potentially use the Hadoop Distributed 083 * File System should be written to use a FileSystem object. The 084 * Hadoop DFS is a multi-machine system that appears as a single 085 * disk. It's useful because of its fault tolerance and potentially 086 * very large capacity. 087 * 088 * <p> 089 * The local implementation is {@link LocalFileSystem} and distributed 090 * implementation is DistributedFileSystem. 091 *****************************************************************/ 092@InterfaceAudience.Public 093@InterfaceStability.Stable 094public abstract class FileSystem extends Configured implements Closeable { 095 public static final String FS_DEFAULT_NAME_KEY = 096 CommonConfigurationKeys.FS_DEFAULT_NAME_KEY; 097 public static final String DEFAULT_FS = 098 CommonConfigurationKeys.FS_DEFAULT_NAME_DEFAULT; 099 100 public static final Log LOG = LogFactory.getLog(FileSystem.class); 101 102 /** 103 * Priority of the FileSystem shutdown hook. 104 */ 105 public static final int SHUTDOWN_HOOK_PRIORITY = 10; 106 107 public static final String TRASH_PREFIX = ".Trash"; 108 109 /** FileSystem cache */ 110 static final Cache CACHE = new Cache(); 111 112 /** The key this instance is stored under in the cache. */ 113 private Cache.Key key; 114 115 /** Recording statistics per a FileSystem class */ 116 private static final Map<Class<? extends FileSystem>, Statistics> 117 statisticsTable = 118 new IdentityHashMap<Class<? extends FileSystem>, Statistics>(); 119 120 /** 121 * The statistics for this file system. 122 */ 123 protected Statistics statistics; 124 125 /** 126 * A cache of files that should be deleted when filsystem is closed 127 * or the JVM is exited. 128 */ 129 private Set<Path> deleteOnExit = new TreeSet<Path>(); 130 131 boolean resolveSymlinks; 132 133 private Tracer tracer; 134 135 protected final Tracer getTracer() { 136 return tracer; 137 } 138 139 /** 140 * This method adds a file system for testing so that we can find it later. It 141 * is only for testing. 142 * @param uri the uri to store it under 143 * @param conf the configuration to store it under 144 * @param fs the file system to store 145 * @throws IOException 146 */ 147 static void addFileSystemForTesting(URI uri, Configuration conf, 148 FileSystem fs) throws IOException { 149 CACHE.map.put(new Cache.Key(uri, conf), fs); 150 } 151 152 /** 153 * Get a filesystem instance based on the uri, the passed 154 * configuration and the user 155 * @param uri of the filesystem 156 * @param conf the configuration to use 157 * @param user to perform the get as 158 * @return the filesystem instance 159 * @throws IOException 160 * @throws InterruptedException 161 */ 162 public static FileSystem get(final URI uri, final Configuration conf, 163 final String user) throws IOException, InterruptedException { 164 String ticketCachePath = 165 conf.get(CommonConfigurationKeys.KERBEROS_TICKET_CACHE_PATH); 166 UserGroupInformation ugi = 167 UserGroupInformation.getBestUGI(ticketCachePath, user); 168 return ugi.doAs(new PrivilegedExceptionAction<FileSystem>() { 169 @Override 170 public FileSystem run() throws IOException { 171 return get(uri, conf); 172 } 173 }); 174 } 175 176 /** 177 * Returns the configured filesystem implementation. 178 * @param conf the configuration to use 179 */ 180 public static FileSystem get(Configuration conf) throws IOException { 181 return get(getDefaultUri(conf), conf); 182 } 183 184 /** Get the default filesystem URI from a configuration. 185 * @param conf the configuration to use 186 * @return the uri of the default filesystem 187 */ 188 public static URI getDefaultUri(Configuration conf) { 189 return URI.create(fixName(conf.get(FS_DEFAULT_NAME_KEY, DEFAULT_FS))); 190 } 191 192 /** Set the default filesystem URI in a configuration. 193 * @param conf the configuration to alter 194 * @param uri the new default filesystem uri 195 */ 196 public static void setDefaultUri(Configuration conf, URI uri) { 197 conf.set(FS_DEFAULT_NAME_KEY, uri.toString()); 198 } 199 200 /** Set the default filesystem URI in a configuration. 201 * @param conf the configuration to alter 202 * @param uri the new default filesystem uri 203 */ 204 public static void setDefaultUri(Configuration conf, String uri) { 205 setDefaultUri(conf, URI.create(fixName(uri))); 206 } 207 208 /** Called after a new FileSystem instance is constructed. 209 * @param name a uri whose authority section names the host, port, etc. 210 * for this FileSystem 211 * @param conf the configuration 212 */ 213 public void initialize(URI name, Configuration conf) throws IOException { 214 statistics = getStatistics(name.getScheme(), getClass()); 215 resolveSymlinks = conf.getBoolean( 216 CommonConfigurationKeys.FS_CLIENT_RESOLVE_REMOTE_SYMLINKS_KEY, 217 CommonConfigurationKeys.FS_CLIENT_RESOLVE_REMOTE_SYMLINKS_DEFAULT); 218 } 219 220 /** 221 * Return the protocol scheme for the FileSystem. 222 * <p/> 223 * This implementation throws an <code>UnsupportedOperationException</code>. 224 * 225 * @return the protocol scheme for the FileSystem. 226 */ 227 public String getScheme() { 228 throw new UnsupportedOperationException("Not implemented by the " + getClass().getSimpleName() + " FileSystem implementation"); 229 } 230 231 /** Returns a URI whose scheme and authority identify this FileSystem.*/ 232 public abstract URI getUri(); 233 234 /** 235 * Return a canonicalized form of this FileSystem's URI. 236 * 237 * The default implementation simply calls {@link #canonicalizeUri(URI)} 238 * on the filesystem's own URI, so subclasses typically only need to 239 * implement that method. 240 * 241 * @see #canonicalizeUri(URI) 242 */ 243 protected URI getCanonicalUri() { 244 return canonicalizeUri(getUri()); 245 } 246 247 /** 248 * Canonicalize the given URI. 249 * 250 * This is filesystem-dependent, but may for example consist of 251 * canonicalizing the hostname using DNS and adding the default 252 * port if not specified. 253 * 254 * The default implementation simply fills in the default port if 255 * not specified and if the filesystem has a default port. 256 * 257 * @return URI 258 * @see NetUtils#getCanonicalUri(URI, int) 259 */ 260 protected URI canonicalizeUri(URI uri) { 261 if (uri.getPort() == -1 && getDefaultPort() > 0) { 262 // reconstruct the uri with the default port set 263 try { 264 uri = new URI(uri.getScheme(), uri.getUserInfo(), 265 uri.getHost(), getDefaultPort(), 266 uri.getPath(), uri.getQuery(), uri.getFragment()); 267 } catch (URISyntaxException e) { 268 // Should never happen! 269 throw new AssertionError("Valid URI became unparseable: " + 270 uri); 271 } 272 } 273 274 return uri; 275 } 276 277 /** 278 * Get the default port for this file system. 279 * @return the default port or 0 if there isn't one 280 */ 281 protected int getDefaultPort() { 282 return 0; 283 } 284 285 protected static FileSystem getFSofPath(final Path absOrFqPath, 286 final Configuration conf) 287 throws UnsupportedFileSystemException, IOException { 288 absOrFqPath.checkNotSchemeWithRelative(); 289 absOrFqPath.checkNotRelative(); 290 291 // Uses the default file system if not fully qualified 292 return get(absOrFqPath.toUri(), conf); 293 } 294 295 /** 296 * Get a canonical service name for this file system. The token cache is 297 * the only user of the canonical service name, and uses it to lookup this 298 * filesystem's service tokens. 299 * If file system provides a token of its own then it must have a canonical 300 * name, otherwise canonical name can be null. 301 * 302 * Default Impl: If the file system has child file systems 303 * (such as an embedded file system) then it is assumed that the fs has no 304 * tokens of its own and hence returns a null name; otherwise a service 305 * name is built using Uri and port. 306 * 307 * @return a service string that uniquely identifies this file system, null 308 * if the filesystem does not implement tokens 309 * @see SecurityUtil#buildDTServiceName(URI, int) 310 */ 311 @InterfaceAudience.LimitedPrivate({ "HDFS", "MapReduce" }) 312 public String getCanonicalServiceName() { 313 return (getChildFileSystems() == null) 314 ? SecurityUtil.buildDTServiceName(getUri(), getDefaultPort()) 315 : null; 316 } 317 318 /** @deprecated call #getUri() instead.*/ 319 @Deprecated 320 public String getName() { return getUri().toString(); } 321 322 /** @deprecated call #get(URI,Configuration) instead. */ 323 @Deprecated 324 public static FileSystem getNamed(String name, Configuration conf) 325 throws IOException { 326 return get(URI.create(fixName(name)), conf); 327 } 328 329 /** Update old-format filesystem names, for back-compatibility. This should 330 * eventually be replaced with a checkName() method that throws an exception 331 * for old-format names. */ 332 private static String fixName(String name) { 333 // convert old-format name to new-format name 334 if (name.equals("local")) { // "local" is now "file:///". 335 LOG.warn("\"local\" is a deprecated filesystem name." 336 +" Use \"file:///\" instead."); 337 name = "file:///"; 338 } else if (name.indexOf('/')==-1) { // unqualified is "hdfs://" 339 LOG.warn("\""+name+"\" is a deprecated filesystem name." 340 +" Use \"hdfs://"+name+"/\" instead."); 341 name = "hdfs://"+name; 342 } 343 return name; 344 } 345 346 /** 347 * Get the local file system. 348 * @param conf the configuration to configure the file system with 349 * @return a LocalFileSystem 350 */ 351 public static LocalFileSystem getLocal(Configuration conf) 352 throws IOException { 353 return (LocalFileSystem)get(LocalFileSystem.NAME, conf); 354 } 355 356 /** Returns the FileSystem for this URI's scheme and authority. The scheme 357 * of the URI determines a configuration property name, 358 * <tt>fs.<i>scheme</i>.class</tt> whose value names the FileSystem class. 359 * The entire URI is passed to the FileSystem instance's initialize method. 360 */ 361 public static FileSystem get(URI uri, Configuration conf) throws IOException { 362 String scheme = uri.getScheme(); 363 String authority = uri.getAuthority(); 364 365 if (scheme == null && authority == null) { // use default FS 366 return get(conf); 367 } 368 369 if (scheme != null && authority == null) { // no authority 370 URI defaultUri = getDefaultUri(conf); 371 if (scheme.equals(defaultUri.getScheme()) // if scheme matches default 372 && defaultUri.getAuthority() != null) { // & default has authority 373 return get(defaultUri, conf); // return default 374 } 375 } 376 377 String disableCacheName = String.format("fs.%s.impl.disable.cache", scheme); 378 if (conf.getBoolean(disableCacheName, false)) { 379 return createFileSystem(uri, conf); 380 } 381 382 return CACHE.get(uri, conf); 383 } 384 385 /** 386 * Returns the FileSystem for this URI's scheme and authority and the 387 * passed user. Internally invokes {@link #newInstance(URI, Configuration)} 388 * @param uri of the filesystem 389 * @param conf the configuration to use 390 * @param user to perform the get as 391 * @return filesystem instance 392 * @throws IOException 393 * @throws InterruptedException 394 */ 395 public static FileSystem newInstance(final URI uri, final Configuration conf, 396 final String user) throws IOException, InterruptedException { 397 String ticketCachePath = 398 conf.get(CommonConfigurationKeys.KERBEROS_TICKET_CACHE_PATH); 399 UserGroupInformation ugi = 400 UserGroupInformation.getBestUGI(ticketCachePath, user); 401 return ugi.doAs(new PrivilegedExceptionAction<FileSystem>() { 402 @Override 403 public FileSystem run() throws IOException { 404 return newInstance(uri,conf); 405 } 406 }); 407 } 408 /** Returns the FileSystem for this URI's scheme and authority. The scheme 409 * of the URI determines a configuration property name, 410 * <tt>fs.<i>scheme</i>.class</tt> whose value names the FileSystem class. 411 * The entire URI is passed to the FileSystem instance's initialize method. 412 * This always returns a new FileSystem object. 413 */ 414 public static FileSystem newInstance(URI uri, Configuration conf) throws IOException { 415 String scheme = uri.getScheme(); 416 String authority = uri.getAuthority(); 417 418 if (scheme == null) { // no scheme: use default FS 419 return newInstance(conf); 420 } 421 422 if (authority == null) { // no authority 423 URI defaultUri = getDefaultUri(conf); 424 if (scheme.equals(defaultUri.getScheme()) // if scheme matches default 425 && defaultUri.getAuthority() != null) { // & default has authority 426 return newInstance(defaultUri, conf); // return default 427 } 428 } 429 return CACHE.getUnique(uri, conf); 430 } 431 432 /** Returns a unique configured filesystem implementation. 433 * This always returns a new FileSystem object. 434 * @param conf the configuration to use 435 */ 436 public static FileSystem newInstance(Configuration conf) throws IOException { 437 return newInstance(getDefaultUri(conf), conf); 438 } 439 440 /** 441 * Get a unique local file system object 442 * @param conf the configuration to configure the file system with 443 * @return a LocalFileSystem 444 * This always returns a new FileSystem object. 445 */ 446 public static LocalFileSystem newInstanceLocal(Configuration conf) 447 throws IOException { 448 return (LocalFileSystem)newInstance(LocalFileSystem.NAME, conf); 449 } 450 451 /** 452 * Close all cached filesystems. Be sure those filesystems are not 453 * used anymore. 454 * 455 * @throws IOException 456 */ 457 public static void closeAll() throws IOException { 458 CACHE.closeAll(); 459 } 460 461 /** 462 * Close all cached filesystems for a given UGI. Be sure those filesystems 463 * are not used anymore. 464 * @param ugi user group info to close 465 * @throws IOException 466 */ 467 public static void closeAllForUGI(UserGroupInformation ugi) 468 throws IOException { 469 CACHE.closeAll(ugi); 470 } 471 472 /** 473 * Make sure that a path specifies a FileSystem. 474 * @param path to use 475 */ 476 public Path makeQualified(Path path) { 477 checkPath(path); 478 return path.makeQualified(this.getUri(), this.getWorkingDirectory()); 479 } 480 481 /** 482 * Get a new delegation token for this file system. 483 * This is an internal method that should have been declared protected 484 * but wasn't historically. 485 * Callers should use {@link #addDelegationTokens(String, Credentials)} 486 * 487 * @param renewer the account name that is allowed to renew the token. 488 * @return a new delegation token 489 * @throws IOException 490 */ 491 @InterfaceAudience.Private() 492 public Token<?> getDelegationToken(String renewer) throws IOException { 493 return null; 494 } 495 496 /** 497 * Obtain all delegation tokens used by this FileSystem that are not 498 * already present in the given Credentials. Existing tokens will neither 499 * be verified as valid nor having the given renewer. Missing tokens will 500 * be acquired and added to the given Credentials. 501 * 502 * Default Impl: works for simple fs with its own token 503 * and also for an embedded fs whose tokens are those of its 504 * children file system (i.e. the embedded fs has not tokens of its 505 * own). 506 * 507 * @param renewer the user allowed to renew the delegation tokens 508 * @param credentials cache in which to add new delegation tokens 509 * @return list of new delegation tokens 510 * @throws IOException 511 */ 512 @InterfaceAudience.LimitedPrivate({ "HDFS", "MapReduce" }) 513 public Token<?>[] addDelegationTokens( 514 final String renewer, Credentials credentials) throws IOException { 515 if (credentials == null) { 516 credentials = new Credentials(); 517 } 518 final List<Token<?>> tokens = new ArrayList<Token<?>>(); 519 collectDelegationTokens(renewer, credentials, tokens); 520 return tokens.toArray(new Token<?>[tokens.size()]); 521 } 522 523 /** 524 * Recursively obtain the tokens for this FileSystem and all descended 525 * FileSystems as determined by getChildFileSystems(). 526 * @param renewer the user allowed to renew the delegation tokens 527 * @param credentials cache in which to add the new delegation tokens 528 * @param tokens list in which to add acquired tokens 529 * @throws IOException 530 */ 531 private void collectDelegationTokens(final String renewer, 532 final Credentials credentials, 533 final List<Token<?>> tokens) 534 throws IOException { 535 final String serviceName = getCanonicalServiceName(); 536 // Collect token of the this filesystem and then of its embedded children 537 if (serviceName != null) { // fs has token, grab it 538 final Text service = new Text(serviceName); 539 Token<?> token = credentials.getToken(service); 540 if (token == null) { 541 token = getDelegationToken(renewer); 542 if (token != null) { 543 tokens.add(token); 544 credentials.addToken(service, token); 545 } 546 } 547 } 548 // Now collect the tokens from the children 549 final FileSystem[] children = getChildFileSystems(); 550 if (children != null) { 551 for (final FileSystem fs : children) { 552 fs.collectDelegationTokens(renewer, credentials, tokens); 553 } 554 } 555 } 556 557 /** 558 * Get all the immediate child FileSystems embedded in this FileSystem. 559 * It does not recurse and get grand children. If a FileSystem 560 * has multiple child FileSystems, then it should return a unique list 561 * of those FileSystems. Default is to return null to signify no children. 562 * 563 * @return FileSystems used by this FileSystem 564 */ 565 @InterfaceAudience.LimitedPrivate({ "HDFS" }) 566 @VisibleForTesting 567 public FileSystem[] getChildFileSystems() { 568 return null; 569 } 570 571 /** create a file with the provided permission 572 * The permission of the file is set to be the provided permission as in 573 * setPermission, not permission&~umask 574 * 575 * It is implemented using two RPCs. It is understood that it is inefficient, 576 * but the implementation is thread-safe. The other option is to change the 577 * value of umask in configuration to be 0, but it is not thread-safe. 578 * 579 * @param fs file system handle 580 * @param file the name of the file to be created 581 * @param permission the permission of the file 582 * @return an output stream 583 * @throws IOException 584 */ 585 public static FSDataOutputStream create(FileSystem fs, 586 Path file, FsPermission permission) throws IOException { 587 // create the file with default permission 588 FSDataOutputStream out = fs.create(file); 589 // set its permission to the supplied one 590 fs.setPermission(file, permission); 591 return out; 592 } 593 594 /** create a directory with the provided permission 595 * The permission of the directory is set to be the provided permission as in 596 * setPermission, not permission&~umask 597 * 598 * @see #create(FileSystem, Path, FsPermission) 599 * 600 * @param fs file system handle 601 * @param dir the name of the directory to be created 602 * @param permission the permission of the directory 603 * @return true if the directory creation succeeds; false otherwise 604 * @throws IOException 605 */ 606 public static boolean mkdirs(FileSystem fs, Path dir, FsPermission permission) 607 throws IOException { 608 // create the directory using the default permission 609 boolean result = fs.mkdirs(dir); 610 // set its permission to be the supplied one 611 fs.setPermission(dir, permission); 612 return result; 613 } 614 615 /////////////////////////////////////////////////////////////// 616 // FileSystem 617 /////////////////////////////////////////////////////////////// 618 619 protected FileSystem() { 620 super(null); 621 } 622 623 /** 624 * Check that a Path belongs to this FileSystem. 625 * @param path to check 626 */ 627 protected void checkPath(Path path) { 628 URI uri = path.toUri(); 629 String thatScheme = uri.getScheme(); 630 if (thatScheme == null) // fs is relative 631 return; 632 URI thisUri = getCanonicalUri(); 633 String thisScheme = thisUri.getScheme(); 634 //authority and scheme are not case sensitive 635 if (thisScheme.equalsIgnoreCase(thatScheme)) {// schemes match 636 String thisAuthority = thisUri.getAuthority(); 637 String thatAuthority = uri.getAuthority(); 638 if (thatAuthority == null && // path's authority is null 639 thisAuthority != null) { // fs has an authority 640 URI defaultUri = getDefaultUri(getConf()); 641 if (thisScheme.equalsIgnoreCase(defaultUri.getScheme())) { 642 uri = defaultUri; // schemes match, so use this uri instead 643 } else { 644 uri = null; // can't determine auth of the path 645 } 646 } 647 if (uri != null) { 648 // canonicalize uri before comparing with this fs 649 uri = canonicalizeUri(uri); 650 thatAuthority = uri.getAuthority(); 651 if (thisAuthority == thatAuthority || // authorities match 652 (thisAuthority != null && 653 thisAuthority.equalsIgnoreCase(thatAuthority))) 654 return; 655 } 656 } 657 throw new IllegalArgumentException("Wrong FS: "+path+ 658 ", expected: "+this.getUri()); 659 } 660 661 /** 662 * Return an array containing hostnames, offset and size of 663 * portions of the given file. For a nonexistent 664 * file or regions, null will be returned. 665 * 666 * This call is most helpful with DFS, where it returns 667 * hostnames of machines that contain the given file. 668 * 669 * The FileSystem will simply return an elt containing 'localhost'. 670 * 671 * @param file FilesStatus to get data from 672 * @param start offset into the given file 673 * @param len length for which to get locations for 674 */ 675 public BlockLocation[] getFileBlockLocations(FileStatus file, 676 long start, long len) throws IOException { 677 if (file == null) { 678 return null; 679 } 680 681 if (start < 0 || len < 0) { 682 throw new IllegalArgumentException("Invalid start or len parameter"); 683 } 684 685 if (file.getLen() <= start) { 686 return new BlockLocation[0]; 687 688 } 689 String[] name = { "localhost:50010" }; 690 String[] host = { "localhost" }; 691 return new BlockLocation[] { 692 new BlockLocation(name, host, 0, file.getLen()) }; 693 } 694 695 696 /** 697 * Return an array containing hostnames, offset and size of 698 * portions of the given file. For a nonexistent 699 * file or regions, null will be returned. 700 * 701 * This call is most helpful with DFS, where it returns 702 * hostnames of machines that contain the given file. 703 * 704 * The FileSystem will simply return an elt containing 'localhost'. 705 * 706 * @param p path is used to identify an FS since an FS could have 707 * another FS that it could be delegating the call to 708 * @param start offset into the given file 709 * @param len length for which to get locations for 710 */ 711 public BlockLocation[] getFileBlockLocations(Path p, 712 long start, long len) throws IOException { 713 if (p == null) { 714 throw new NullPointerException(); 715 } 716 FileStatus file = getFileStatus(p); 717 return getFileBlockLocations(file, start, len); 718 } 719 720 /** 721 * Return a set of server default configuration values 722 * @return server default configuration values 723 * @throws IOException 724 * @deprecated use {@link #getServerDefaults(Path)} instead 725 */ 726 @Deprecated 727 public FsServerDefaults getServerDefaults() throws IOException { 728 Configuration conf = getConf(); 729 // CRC32 is chosen as default as it is available in all 730 // releases that support checksum. 731 // The client trash configuration is ignored. 732 return new FsServerDefaults(getDefaultBlockSize(), 733 conf.getInt("io.bytes.per.checksum", 512), 734 64 * 1024, 735 getDefaultReplication(), 736 conf.getInt("io.file.buffer.size", 4096), 737 false, 738 CommonConfigurationKeysPublic.FS_TRASH_INTERVAL_DEFAULT, 739 DataChecksum.Type.CRC32); 740 } 741 742 /** 743 * Return a set of server default configuration values 744 * @param p path is used to identify an FS since an FS could have 745 * another FS that it could be delegating the call to 746 * @return server default configuration values 747 * @throws IOException 748 */ 749 public FsServerDefaults getServerDefaults(Path p) throws IOException { 750 return getServerDefaults(); 751 } 752 753 /** 754 * Return the fully-qualified path of path f resolving the path 755 * through any symlinks or mount point 756 * @param p path to be resolved 757 * @return fully qualified path 758 * @throws FileNotFoundException 759 */ 760 public Path resolvePath(final Path p) throws IOException { 761 checkPath(p); 762 return getFileStatus(p).getPath(); 763 } 764 765 /** 766 * Opens an FSDataInputStream at the indicated Path. 767 * @param f the file name to open 768 * @param bufferSize the size of the buffer to be used. 769 */ 770 public abstract FSDataInputStream open(Path f, int bufferSize) 771 throws IOException; 772 773 /** 774 * Opens an FSDataInputStream at the indicated Path. 775 * @param f the file to open 776 */ 777 public FSDataInputStream open(Path f) throws IOException { 778 return open(f, getConf().getInt("io.file.buffer.size", 4096)); 779 } 780 781 /** 782 * Create an FSDataOutputStream at the indicated Path. 783 * Files are overwritten by default. 784 * @param f the file to create 785 */ 786 public FSDataOutputStream create(Path f) throws IOException { 787 return create(f, true); 788 } 789 790 /** 791 * Create an FSDataOutputStream at the indicated Path. 792 * @param f the file to create 793 * @param overwrite if a file with this name already exists, then if true, 794 * the file will be overwritten, and if false an exception will be thrown. 795 */ 796 public FSDataOutputStream create(Path f, boolean overwrite) 797 throws IOException { 798 return create(f, overwrite, 799 getConf().getInt("io.file.buffer.size", 4096), 800 getDefaultReplication(f), 801 getDefaultBlockSize(f)); 802 } 803 804 /** 805 * Create an FSDataOutputStream at the indicated Path with write-progress 806 * reporting. 807 * Files are overwritten by default. 808 * @param f the file to create 809 * @param progress to report progress 810 */ 811 public FSDataOutputStream create(Path f, Progressable progress) 812 throws IOException { 813 return create(f, true, 814 getConf().getInt("io.file.buffer.size", 4096), 815 getDefaultReplication(f), 816 getDefaultBlockSize(f), progress); 817 } 818 819 /** 820 * Create an FSDataOutputStream at the indicated Path. 821 * Files are overwritten by default. 822 * @param f the file to create 823 * @param replication the replication factor 824 */ 825 public FSDataOutputStream create(Path f, short replication) 826 throws IOException { 827 return create(f, true, 828 getConf().getInt("io.file.buffer.size", 4096), 829 replication, 830 getDefaultBlockSize(f)); 831 } 832 833 /** 834 * Create an FSDataOutputStream at the indicated Path with write-progress 835 * reporting. 836 * Files are overwritten by default. 837 * @param f the file to create 838 * @param replication the replication factor 839 * @param progress to report progress 840 */ 841 public FSDataOutputStream create(Path f, short replication, 842 Progressable progress) throws IOException { 843 return create(f, true, 844 getConf().getInt( 845 CommonConfigurationKeysPublic.IO_FILE_BUFFER_SIZE_KEY, 846 CommonConfigurationKeysPublic.IO_FILE_BUFFER_SIZE_DEFAULT), 847 replication, 848 getDefaultBlockSize(f), progress); 849 } 850 851 852 /** 853 * Create an FSDataOutputStream at the indicated Path. 854 * @param f the file name to create 855 * @param overwrite if a file with this name already exists, then if true, 856 * the file will be overwritten, and if false an error will be thrown. 857 * @param bufferSize the size of the buffer to be used. 858 */ 859 public FSDataOutputStream create(Path f, 860 boolean overwrite, 861 int bufferSize 862 ) throws IOException { 863 return create(f, overwrite, bufferSize, 864 getDefaultReplication(f), 865 getDefaultBlockSize(f)); 866 } 867 868 /** 869 * Create an FSDataOutputStream at the indicated Path with write-progress 870 * reporting. 871 * @param f the path of the file to open 872 * @param overwrite if a file with this name already exists, then if true, 873 * the file will be overwritten, and if false an error will be thrown. 874 * @param bufferSize the size of the buffer to be used. 875 */ 876 public FSDataOutputStream create(Path f, 877 boolean overwrite, 878 int bufferSize, 879 Progressable progress 880 ) throws IOException { 881 return create(f, overwrite, bufferSize, 882 getDefaultReplication(f), 883 getDefaultBlockSize(f), progress); 884 } 885 886 887 /** 888 * Create an FSDataOutputStream at the indicated Path. 889 * @param f the file name to open 890 * @param overwrite if a file with this name already exists, then if true, 891 * the file will be overwritten, and if false an error will be thrown. 892 * @param bufferSize the size of the buffer to be used. 893 * @param replication required block replication for the file. 894 */ 895 public FSDataOutputStream create(Path f, 896 boolean overwrite, 897 int bufferSize, 898 short replication, 899 long blockSize 900 ) throws IOException { 901 return create(f, overwrite, bufferSize, replication, blockSize, null); 902 } 903 904 /** 905 * Create an FSDataOutputStream at the indicated Path with write-progress 906 * reporting. 907 * @param f the file name to open 908 * @param overwrite if a file with this name already exists, then if true, 909 * the file will be overwritten, and if false an error will be thrown. 910 * @param bufferSize the size of the buffer to be used. 911 * @param replication required block replication for the file. 912 */ 913 public FSDataOutputStream create(Path f, 914 boolean overwrite, 915 int bufferSize, 916 short replication, 917 long blockSize, 918 Progressable progress 919 ) throws IOException { 920 return this.create(f, FsPermission.getFileDefault().applyUMask( 921 FsPermission.getUMask(getConf())), overwrite, bufferSize, 922 replication, blockSize, progress); 923 } 924 925 /** 926 * Create an FSDataOutputStream at the indicated Path with write-progress 927 * reporting. 928 * @param f the file name to open 929 * @param permission 930 * @param overwrite if a file with this name already exists, then if true, 931 * the file will be overwritten, and if false an error will be thrown. 932 * @param bufferSize the size of the buffer to be used. 933 * @param replication required block replication for the file. 934 * @param blockSize 935 * @param progress 936 * @throws IOException 937 * @see #setPermission(Path, FsPermission) 938 */ 939 public abstract FSDataOutputStream create(Path f, 940 FsPermission permission, 941 boolean overwrite, 942 int bufferSize, 943 short replication, 944 long blockSize, 945 Progressable progress) throws IOException; 946 947 /** 948 * Create an FSDataOutputStream at the indicated Path with write-progress 949 * reporting. 950 * @param f the file name to open 951 * @param permission 952 * @param flags {@link CreateFlag}s to use for this stream. 953 * @param bufferSize the size of the buffer to be used. 954 * @param replication required block replication for the file. 955 * @param blockSize 956 * @param progress 957 * @throws IOException 958 * @see #setPermission(Path, FsPermission) 959 */ 960 public FSDataOutputStream create(Path f, 961 FsPermission permission, 962 EnumSet<CreateFlag> flags, 963 int bufferSize, 964 short replication, 965 long blockSize, 966 Progressable progress) throws IOException { 967 return create(f, permission, flags, bufferSize, replication, 968 blockSize, progress, null); 969 } 970 971 /** 972 * Create an FSDataOutputStream at the indicated Path with a custom 973 * checksum option 974 * @param f the file name to open 975 * @param permission 976 * @param flags {@link CreateFlag}s to use for this stream. 977 * @param bufferSize the size of the buffer to be used. 978 * @param replication required block replication for the file. 979 * @param blockSize 980 * @param progress 981 * @param checksumOpt checksum parameter. If null, the values 982 * found in conf will be used. 983 * @throws IOException 984 * @see #setPermission(Path, FsPermission) 985 */ 986 public FSDataOutputStream create(Path f, 987 FsPermission permission, 988 EnumSet<CreateFlag> flags, 989 int bufferSize, 990 short replication, 991 long blockSize, 992 Progressable progress, 993 ChecksumOpt checksumOpt) throws IOException { 994 // Checksum options are ignored by default. The file systems that 995 // implement checksum need to override this method. The full 996 // support is currently only available in DFS. 997 return create(f, permission, flags.contains(CreateFlag.OVERWRITE), 998 bufferSize, replication, blockSize, progress); 999 } 1000 1001 /*. 1002 * This create has been added to support the FileContext that processes 1003 * the permission 1004 * with umask before calling this method. 1005 * This a temporary method added to support the transition from FileSystem 1006 * to FileContext for user applications. 1007 */ 1008 @Deprecated 1009 protected FSDataOutputStream primitiveCreate(Path f, 1010 FsPermission absolutePermission, EnumSet<CreateFlag> flag, int bufferSize, 1011 short replication, long blockSize, Progressable progress, 1012 ChecksumOpt checksumOpt) throws IOException { 1013 1014 boolean pathExists = exists(f); 1015 CreateFlag.validate(f, pathExists, flag); 1016 1017 // Default impl assumes that permissions do not matter and 1018 // nor does the bytesPerChecksum hence 1019 // calling the regular create is good enough. 1020 // FSs that implement permissions should override this. 1021 1022 if (pathExists && flag.contains(CreateFlag.APPEND)) { 1023 return append(f, bufferSize, progress); 1024 } 1025 1026 return this.create(f, absolutePermission, 1027 flag.contains(CreateFlag.OVERWRITE), bufferSize, replication, 1028 blockSize, progress); 1029 } 1030 1031 /** 1032 * This version of the mkdirs method assumes that the permission is absolute. 1033 * It has been added to support the FileContext that processes the permission 1034 * with umask before calling this method. 1035 * This a temporary method added to support the transition from FileSystem 1036 * to FileContext for user applications. 1037 */ 1038 @Deprecated 1039 protected boolean primitiveMkdir(Path f, FsPermission absolutePermission) 1040 throws IOException { 1041 // Default impl is to assume that permissions do not matter and hence 1042 // calling the regular mkdirs is good enough. 1043 // FSs that implement permissions should override this. 1044 return this.mkdirs(f, absolutePermission); 1045 } 1046 1047 1048 /** 1049 * This version of the mkdirs method assumes that the permission is absolute. 1050 * It has been added to support the FileContext that processes the permission 1051 * with umask before calling this method. 1052 * This a temporary method added to support the transition from FileSystem 1053 * to FileContext for user applications. 1054 */ 1055 @Deprecated 1056 protected void primitiveMkdir(Path f, FsPermission absolutePermission, 1057 boolean createParent) 1058 throws IOException { 1059 1060 if (!createParent) { // parent must exist. 1061 // since the this.mkdirs makes parent dirs automatically 1062 // we must throw exception if parent does not exist. 1063 final FileStatus stat = getFileStatus(f.getParent()); 1064 if (stat == null) { 1065 throw new FileNotFoundException("Missing parent:" + f); 1066 } 1067 if (!stat.isDirectory()) { 1068 throw new ParentNotDirectoryException("parent is not a dir"); 1069 } 1070 // parent does exist - go ahead with mkdir of leaf 1071 } 1072 // Default impl is to assume that permissions do not matter and hence 1073 // calling the regular mkdirs is good enough. 1074 // FSs that implement permissions should override this. 1075 if (!this.mkdirs(f, absolutePermission)) { 1076 throw new IOException("mkdir of "+ f + " failed"); 1077 } 1078 } 1079 1080 /** 1081 * Opens an FSDataOutputStream at the indicated Path with write-progress 1082 * reporting. Same as create(), except fails if parent directory doesn't 1083 * already exist. 1084 * @param f the file name to open 1085 * @param overwrite if a file with this name already exists, then if true, 1086 * the file will be overwritten, and if false an error will be thrown. 1087 * @param bufferSize the size of the buffer to be used. 1088 * @param replication required block replication for the file. 1089 * @param blockSize 1090 * @param progress 1091 * @throws IOException 1092 * @see #setPermission(Path, FsPermission) 1093 * @deprecated API only for 0.20-append 1094 */ 1095 @Deprecated 1096 public FSDataOutputStream createNonRecursive(Path f, 1097 boolean overwrite, 1098 int bufferSize, short replication, long blockSize, 1099 Progressable progress) throws IOException { 1100 return this.createNonRecursive(f, FsPermission.getFileDefault(), 1101 overwrite, bufferSize, replication, blockSize, progress); 1102 } 1103 1104 /** 1105 * Opens an FSDataOutputStream at the indicated Path with write-progress 1106 * reporting. Same as create(), except fails if parent directory doesn't 1107 * already exist. 1108 * @param f the file name to open 1109 * @param permission 1110 * @param overwrite if a file with this name already exists, then if true, 1111 * the file will be overwritten, and if false an error will be thrown. 1112 * @param bufferSize the size of the buffer to be used. 1113 * @param replication required block replication for the file. 1114 * @param blockSize 1115 * @param progress 1116 * @throws IOException 1117 * @see #setPermission(Path, FsPermission) 1118 * @deprecated API only for 0.20-append 1119 */ 1120 @Deprecated 1121 public FSDataOutputStream createNonRecursive(Path f, FsPermission permission, 1122 boolean overwrite, int bufferSize, short replication, long blockSize, 1123 Progressable progress) throws IOException { 1124 return createNonRecursive(f, permission, 1125 overwrite ? EnumSet.of(CreateFlag.CREATE, CreateFlag.OVERWRITE) 1126 : EnumSet.of(CreateFlag.CREATE), bufferSize, 1127 replication, blockSize, progress); 1128 } 1129 1130 /** 1131 * Opens an FSDataOutputStream at the indicated Path with write-progress 1132 * reporting. Same as create(), except fails if parent directory doesn't 1133 * already exist. 1134 * @param f the file name to open 1135 * @param permission 1136 * @param flags {@link CreateFlag}s to use for this stream. 1137 * @param bufferSize the size of the buffer to be used. 1138 * @param replication required block replication for the file. 1139 * @param blockSize 1140 * @param progress 1141 * @throws IOException 1142 * @see #setPermission(Path, FsPermission) 1143 * @deprecated API only for 0.20-append 1144 */ 1145 @Deprecated 1146 public FSDataOutputStream createNonRecursive(Path f, FsPermission permission, 1147 EnumSet<CreateFlag> flags, int bufferSize, short replication, long blockSize, 1148 Progressable progress) throws IOException { 1149 throw new IOException("createNonRecursive unsupported for this filesystem " 1150 + this.getClass()); 1151 } 1152 1153 /** 1154 * Creates the given Path as a brand-new zero-length file. If 1155 * create fails, or if it already existed, return false. 1156 * 1157 * @param f path to use for create 1158 */ 1159 public boolean createNewFile(Path f) throws IOException { 1160 if (exists(f)) { 1161 return false; 1162 } else { 1163 create(f, false, getConf().getInt("io.file.buffer.size", 4096)).close(); 1164 return true; 1165 } 1166 } 1167 1168 /** 1169 * Append to an existing file (optional operation). 1170 * Same as append(f, getConf().getInt("io.file.buffer.size", 4096), null) 1171 * @param f the existing file to be appended. 1172 * @throws IOException 1173 */ 1174 public FSDataOutputStream append(Path f) throws IOException { 1175 return append(f, getConf().getInt("io.file.buffer.size", 4096), null); 1176 } 1177 /** 1178 * Append to an existing file (optional operation). 1179 * Same as append(f, bufferSize, null). 1180 * @param f the existing file to be appended. 1181 * @param bufferSize the size of the buffer to be used. 1182 * @throws IOException 1183 */ 1184 public FSDataOutputStream append(Path f, int bufferSize) throws IOException { 1185 return append(f, bufferSize, null); 1186 } 1187 1188 /** 1189 * Append to an existing file (optional operation). 1190 * @param f the existing file to be appended. 1191 * @param bufferSize the size of the buffer to be used. 1192 * @param progress for reporting progress if it is not null. 1193 * @throws IOException 1194 */ 1195 public abstract FSDataOutputStream append(Path f, int bufferSize, 1196 Progressable progress) throws IOException; 1197 1198 /** 1199 * Concat existing files together. 1200 * @param trg the path to the target destination. 1201 * @param psrcs the paths to the sources to use for the concatenation. 1202 * @throws IOException 1203 */ 1204 public void concat(final Path trg, final Path [] psrcs) throws IOException { 1205 throw new UnsupportedOperationException("Not implemented by the " + 1206 getClass().getSimpleName() + " FileSystem implementation"); 1207 } 1208 1209 /** 1210 * Get replication. 1211 * 1212 * @deprecated Use getFileStatus() instead 1213 * @param src file name 1214 * @return file replication 1215 * @throws IOException 1216 */ 1217 @Deprecated 1218 public short getReplication(Path src) throws IOException { 1219 return getFileStatus(src).getReplication(); 1220 } 1221 1222 /** 1223 * Set replication for an existing file. 1224 * 1225 * @param src file name 1226 * @param replication new replication 1227 * @throws IOException 1228 * @return true if successful; 1229 * false if file does not exist or is a directory 1230 */ 1231 public boolean setReplication(Path src, short replication) 1232 throws IOException { 1233 return true; 1234 } 1235 1236 /** 1237 * Renames Path src to Path dst. Can take place on local fs 1238 * or remote DFS. 1239 * @param src path to be renamed 1240 * @param dst new path after rename 1241 * @throws IOException on failure 1242 * @return true if rename is successful 1243 */ 1244 public abstract boolean rename(Path src, Path dst) throws IOException; 1245 1246 /** 1247 * Renames Path src to Path dst 1248 * <ul> 1249 * <li 1250 * <li>Fails if src is a file and dst is a directory. 1251 * <li>Fails if src is a directory and dst is a file. 1252 * <li>Fails if the parent of dst does not exist or is a file. 1253 * </ul> 1254 * <p> 1255 * If OVERWRITE option is not passed as an argument, rename fails 1256 * if the dst already exists. 1257 * <p> 1258 * If OVERWRITE option is passed as an argument, rename overwrites 1259 * the dst if it is a file or an empty directory. Rename fails if dst is 1260 * a non-empty directory. 1261 * <p> 1262 * Note that atomicity of rename is dependent on the file system 1263 * implementation. Please refer to the file system documentation for 1264 * details. This default implementation is non atomic. 1265 * <p> 1266 * This method is deprecated since it is a temporary method added to 1267 * support the transition from FileSystem to FileContext for user 1268 * applications. 1269 * 1270 * @param src path to be renamed 1271 * @param dst new path after rename 1272 * @throws IOException on failure 1273 */ 1274 @Deprecated 1275 protected void rename(final Path src, final Path dst, 1276 final Rename... options) throws IOException { 1277 // Default implementation 1278 final FileStatus srcStatus = getFileLinkStatus(src); 1279 if (srcStatus == null) { 1280 throw new FileNotFoundException("rename source " + src + " not found."); 1281 } 1282 1283 boolean overwrite = false; 1284 if (null != options) { 1285 for (Rename option : options) { 1286 if (option == Rename.OVERWRITE) { 1287 overwrite = true; 1288 } 1289 } 1290 } 1291 1292 FileStatus dstStatus; 1293 try { 1294 dstStatus = getFileLinkStatus(dst); 1295 } catch (IOException e) { 1296 dstStatus = null; 1297 } 1298 if (dstStatus != null) { 1299 if (srcStatus.isDirectory() != dstStatus.isDirectory()) { 1300 throw new IOException("Source " + src + " Destination " + dst 1301 + " both should be either file or directory"); 1302 } 1303 if (!overwrite) { 1304 throw new FileAlreadyExistsException("rename destination " + dst 1305 + " already exists."); 1306 } 1307 // Delete the destination that is a file or an empty directory 1308 if (dstStatus.isDirectory()) { 1309 FileStatus[] list = listStatus(dst); 1310 if (list != null && list.length != 0) { 1311 throw new IOException( 1312 "rename cannot overwrite non empty destination directory " + dst); 1313 } 1314 } 1315 delete(dst, false); 1316 } else { 1317 final Path parent = dst.getParent(); 1318 final FileStatus parentStatus = getFileStatus(parent); 1319 if (parentStatus == null) { 1320 throw new FileNotFoundException("rename destination parent " + parent 1321 + " not found."); 1322 } 1323 if (!parentStatus.isDirectory()) { 1324 throw new ParentNotDirectoryException("rename destination parent " + parent 1325 + " is a file."); 1326 } 1327 } 1328 if (!rename(src, dst)) { 1329 throw new IOException("rename from " + src + " to " + dst + " failed."); 1330 } 1331 } 1332 1333 /** 1334 * Delete a file 1335 * @deprecated Use {@link #delete(Path, boolean)} instead. 1336 */ 1337 @Deprecated 1338 public boolean delete(Path f) throws IOException { 1339 return delete(f, true); 1340 } 1341 1342 /** Delete a file. 1343 * 1344 * @param f the path to delete. 1345 * @param recursive if path is a directory and set to 1346 * true, the directory is deleted else throws an exception. In 1347 * case of a file the recursive can be set to either true or false. 1348 * @return true if delete is successful else false. 1349 * @throws IOException 1350 */ 1351 public abstract boolean delete(Path f, boolean recursive) throws IOException; 1352 1353 /** 1354 * Mark a path to be deleted when FileSystem is closed. 1355 * When the JVM shuts down, 1356 * all FileSystem objects will be closed automatically. 1357 * Then, 1358 * the marked path will be deleted as a result of closing the FileSystem. 1359 * 1360 * The path has to exist in the file system. 1361 * 1362 * @param f the path to delete. 1363 * @return true if deleteOnExit is successful, otherwise false. 1364 * @throws IOException 1365 */ 1366 public boolean deleteOnExit(Path f) throws IOException { 1367 if (!exists(f)) { 1368 return false; 1369 } 1370 synchronized (deleteOnExit) { 1371 deleteOnExit.add(f); 1372 } 1373 return true; 1374 } 1375 1376 /** 1377 * Cancel the deletion of the path when the FileSystem is closed 1378 * @param f the path to cancel deletion 1379 */ 1380 public boolean cancelDeleteOnExit(Path f) { 1381 synchronized (deleteOnExit) { 1382 return deleteOnExit.remove(f); 1383 } 1384 } 1385 1386 /** 1387 * Delete all files that were marked as delete-on-exit. This recursively 1388 * deletes all files in the specified paths. 1389 */ 1390 protected void processDeleteOnExit() { 1391 synchronized (deleteOnExit) { 1392 for (Iterator<Path> iter = deleteOnExit.iterator(); iter.hasNext();) { 1393 Path path = iter.next(); 1394 try { 1395 if (exists(path)) { 1396 delete(path, true); 1397 } 1398 } 1399 catch (IOException e) { 1400 LOG.info("Ignoring failure to deleteOnExit for path " + path); 1401 } 1402 iter.remove(); 1403 } 1404 } 1405 } 1406 1407 /** Check if exists. 1408 * @param f source file 1409 */ 1410 public boolean exists(Path f) throws IOException { 1411 try { 1412 return getFileStatus(f) != null; 1413 } catch (FileNotFoundException e) { 1414 return false; 1415 } 1416 } 1417 1418 /** True iff the named path is a directory. 1419 * Note: Avoid using this method. Instead reuse the FileStatus 1420 * returned by getFileStatus() or listStatus() methods. 1421 * @param f path to check 1422 */ 1423 public boolean isDirectory(Path f) throws IOException { 1424 try { 1425 return getFileStatus(f).isDirectory(); 1426 } catch (FileNotFoundException e) { 1427 return false; // f does not exist 1428 } 1429 } 1430 1431 /** True iff the named path is a regular file. 1432 * Note: Avoid using this method. Instead reuse the FileStatus 1433 * returned by getFileStatus() or listStatus() methods. 1434 * @param f path to check 1435 */ 1436 public boolean isFile(Path f) throws IOException { 1437 try { 1438 return getFileStatus(f).isFile(); 1439 } catch (FileNotFoundException e) { 1440 return false; // f does not exist 1441 } 1442 } 1443 1444 /** The number of bytes in a file. */ 1445 /** @deprecated Use getFileStatus() instead */ 1446 @Deprecated 1447 public long getLength(Path f) throws IOException { 1448 return getFileStatus(f).getLen(); 1449 } 1450 1451 /** Return the {@link ContentSummary} of a given {@link Path}. 1452 * @param f path to use 1453 */ 1454 public ContentSummary getContentSummary(Path f) throws IOException { 1455 FileStatus status = getFileStatus(f); 1456 if (status.isFile()) { 1457 // f is a file 1458 return new ContentSummary(status.getLen(), 1, 0); 1459 } 1460 // f is a directory 1461 long[] summary = {0, 0, 1}; 1462 for(FileStatus s : listStatus(f)) { 1463 ContentSummary c = s.isDirectory() ? getContentSummary(s.getPath()) : 1464 new ContentSummary(s.getLen(), 1, 0); 1465 summary[0] += c.getLength(); 1466 summary[1] += c.getFileCount(); 1467 summary[2] += c.getDirectoryCount(); 1468 } 1469 return new ContentSummary(summary[0], summary[1], summary[2]); 1470 } 1471 1472 final private static PathFilter DEFAULT_FILTER = new PathFilter() { 1473 @Override 1474 public boolean accept(Path file) { 1475 return true; 1476 } 1477 }; 1478 1479 /** 1480 * List the statuses of the files/directories in the given path if the path is 1481 * a directory. 1482 * 1483 * @param f given path 1484 * @return the statuses of the files/directories in the given patch 1485 * @throws FileNotFoundException when the path does not exist; 1486 * IOException see specific implementation 1487 */ 1488 public abstract FileStatus[] listStatus(Path f) throws FileNotFoundException, 1489 IOException; 1490 1491 /* 1492 * Filter files/directories in the given path using the user-supplied path 1493 * filter. Results are added to the given array <code>results</code>. 1494 */ 1495 private void listStatus(ArrayList<FileStatus> results, Path f, 1496 PathFilter filter) throws FileNotFoundException, IOException { 1497 FileStatus listing[] = listStatus(f); 1498 if (listing == null) { 1499 throw new IOException("Error accessing " + f); 1500 } 1501 1502 for (int i = 0; i < listing.length; i++) { 1503 if (filter.accept(listing[i].getPath())) { 1504 results.add(listing[i]); 1505 } 1506 } 1507 } 1508 1509 /** 1510 * @return an iterator over the corrupt files under the given path 1511 * (may contain duplicates if a file has more than one corrupt block) 1512 * @throws IOException 1513 */ 1514 public RemoteIterator<Path> listCorruptFileBlocks(Path path) 1515 throws IOException { 1516 throw new UnsupportedOperationException(getClass().getCanonicalName() + 1517 " does not support" + 1518 " listCorruptFileBlocks"); 1519 } 1520 1521 /** 1522 * Filter files/directories in the given path using the user-supplied path 1523 * filter. 1524 * 1525 * @param f 1526 * a path name 1527 * @param filter 1528 * the user-supplied path filter 1529 * @return an array of FileStatus objects for the files under the given path 1530 * after applying the filter 1531 * @throws FileNotFoundException when the path does not exist; 1532 * IOException see specific implementation 1533 */ 1534 public FileStatus[] listStatus(Path f, PathFilter filter) 1535 throws FileNotFoundException, IOException { 1536 ArrayList<FileStatus> results = new ArrayList<FileStatus>(); 1537 listStatus(results, f, filter); 1538 return results.toArray(new FileStatus[results.size()]); 1539 } 1540 1541 /** 1542 * Filter files/directories in the given list of paths using default 1543 * path filter. 1544 * 1545 * @param files 1546 * a list of paths 1547 * @return a list of statuses for the files under the given paths after 1548 * applying the filter default Path filter 1549 * @throws FileNotFoundException when the path does not exist; 1550 * IOException see specific implementation 1551 */ 1552 public FileStatus[] listStatus(Path[] files) 1553 throws FileNotFoundException, IOException { 1554 return listStatus(files, DEFAULT_FILTER); 1555 } 1556 1557 /** 1558 * Filter files/directories in the given list of paths using user-supplied 1559 * path filter. 1560 * 1561 * @param files 1562 * a list of paths 1563 * @param filter 1564 * the user-supplied path filter 1565 * @return a list of statuses for the files under the given paths after 1566 * applying the filter 1567 * @throws FileNotFoundException when the path does not exist; 1568 * IOException see specific implementation 1569 */ 1570 public FileStatus[] listStatus(Path[] files, PathFilter filter) 1571 throws FileNotFoundException, IOException { 1572 ArrayList<FileStatus> results = new ArrayList<FileStatus>(); 1573 for (int i = 0; i < files.length; i++) { 1574 listStatus(results, files[i], filter); 1575 } 1576 return results.toArray(new FileStatus[results.size()]); 1577 } 1578 1579 /** 1580 * <p>Return all the files that match filePattern and are not checksum 1581 * files. Results are sorted by their names. 1582 * 1583 * <p> 1584 * A filename pattern is composed of <i>regular</i> characters and 1585 * <i>special pattern matching</i> characters, which are: 1586 * 1587 * <dl> 1588 * <dd> 1589 * <dl> 1590 * <p> 1591 * <dt> <tt> ? </tt> 1592 * <dd> Matches any single character. 1593 * 1594 * <p> 1595 * <dt> <tt> * </tt> 1596 * <dd> Matches zero or more characters. 1597 * 1598 * <p> 1599 * <dt> <tt> [<i>abc</i>] </tt> 1600 * <dd> Matches a single character from character set 1601 * <tt>{<i>a,b,c</i>}</tt>. 1602 * 1603 * <p> 1604 * <dt> <tt> [<i>a</i>-<i>b</i>] </tt> 1605 * <dd> Matches a single character from the character range 1606 * <tt>{<i>a...b</i>}</tt>. Note that character <tt><i>a</i></tt> must be 1607 * lexicographically less than or equal to character <tt><i>b</i></tt>. 1608 * 1609 * <p> 1610 * <dt> <tt> [^<i>a</i>] </tt> 1611 * <dd> Matches a single character that is not from character set or range 1612 * <tt>{<i>a</i>}</tt>. Note that the <tt>^</tt> character must occur 1613 * immediately to the right of the opening bracket. 1614 * 1615 * <p> 1616 * <dt> <tt> \<i>c</i> </tt> 1617 * <dd> Removes (escapes) any special meaning of character <i>c</i>. 1618 * 1619 * <p> 1620 * <dt> <tt> {ab,cd} </tt> 1621 * <dd> Matches a string from the string set <tt>{<i>ab, cd</i>} </tt> 1622 * 1623 * <p> 1624 * <dt> <tt> {ab,c{de,fh}} </tt> 1625 * <dd> Matches a string from the string set <tt>{<i>ab, cde, cfh</i>}</tt> 1626 * 1627 * </dl> 1628 * </dd> 1629 * </dl> 1630 * 1631 * @param pathPattern a regular expression specifying a pth pattern 1632 1633 * @return an array of paths that match the path pattern 1634 * @throws IOException 1635 */ 1636 public FileStatus[] globStatus(Path pathPattern) throws IOException { 1637 return new Globber(this, pathPattern, DEFAULT_FILTER).glob(); 1638 } 1639 1640 /** 1641 * Return an array of FileStatus objects whose path names match pathPattern 1642 * and is accepted by the user-supplied path filter. Results are sorted by 1643 * their path names. 1644 * Return null if pathPattern has no glob and the path does not exist. 1645 * Return an empty array if pathPattern has a glob and no path matches it. 1646 * 1647 * @param pathPattern 1648 * a regular expression specifying the path pattern 1649 * @param filter 1650 * a user-supplied path filter 1651 * @return an array of FileStatus objects 1652 * @throws IOException if any I/O error occurs when fetching file status 1653 */ 1654 public FileStatus[] globStatus(Path pathPattern, PathFilter filter) 1655 throws IOException { 1656 return new Globber(this, pathPattern, filter).glob(); 1657 } 1658 1659 /** 1660 * List the statuses of the files/directories in the given path if the path is 1661 * a directory. 1662 * Return the file's status and block locations If the path is a file. 1663 * 1664 * If a returned status is a file, it contains the file's block locations. 1665 * 1666 * @param f is the path 1667 * 1668 * @return an iterator that traverses statuses of the files/directories 1669 * in the given path 1670 * 1671 * @throws FileNotFoundException If <code>f</code> does not exist 1672 * @throws IOException If an I/O error occurred 1673 */ 1674 public RemoteIterator<LocatedFileStatus> listLocatedStatus(final Path f) 1675 throws FileNotFoundException, IOException { 1676 return listLocatedStatus(f, DEFAULT_FILTER); 1677 } 1678 1679 /** 1680 * Listing a directory 1681 * The returned results include its block location if it is a file 1682 * The results are filtered by the given path filter 1683 * @param f a path 1684 * @param filter a path filter 1685 * @return an iterator that traverses statuses of the files/directories 1686 * in the given path 1687 * @throws FileNotFoundException if <code>f</code> does not exist 1688 * @throws IOException if any I/O error occurred 1689 */ 1690 protected RemoteIterator<LocatedFileStatus> listLocatedStatus(final Path f, 1691 final PathFilter filter) 1692 throws FileNotFoundException, IOException { 1693 return new RemoteIterator<LocatedFileStatus>() { 1694 private final FileStatus[] stats = listStatus(f, filter); 1695 private int i = 0; 1696 1697 @Override 1698 public boolean hasNext() { 1699 return i<stats.length; 1700 } 1701 1702 @Override 1703 public LocatedFileStatus next() throws IOException { 1704 if (!hasNext()) { 1705 throw new NoSuchElementException("No more entry in " + f); 1706 } 1707 FileStatus result = stats[i++]; 1708 // for files, use getBlockLocations(FileStatus, int, int) to avoid 1709 // calling getFileStatus(Path) to load the FileStatus again 1710 BlockLocation[] locs = result.isFile() ? 1711 getFileBlockLocations(result, 0, result.getLen()) : 1712 null; 1713 return new LocatedFileStatus(result, locs); 1714 } 1715 }; 1716 } 1717 1718 /** 1719 * Returns a remote iterator so that followup calls are made on demand 1720 * while consuming the entries. Each file system implementation should 1721 * override this method and provide a more efficient implementation, if 1722 * possible. 1723 * 1724 * @param p target path 1725 * @return remote iterator 1726 */ 1727 public RemoteIterator<FileStatus> listStatusIterator(final Path p) 1728 throws FileNotFoundException, IOException { 1729 return new RemoteIterator<FileStatus>() { 1730 private final FileStatus[] stats = listStatus(p); 1731 private int i = 0; 1732 1733 @Override 1734 public boolean hasNext() { 1735 return i<stats.length; 1736 } 1737 1738 @Override 1739 public FileStatus next() throws IOException { 1740 if (!hasNext()) { 1741 throw new NoSuchElementException("No more entry in " + p); 1742 } 1743 return stats[i++]; 1744 } 1745 }; 1746 } 1747 1748 /** 1749 * List the statuses and block locations of the files in the given path. 1750 * 1751 * If the path is a directory, 1752 * if recursive is false, returns files in the directory; 1753 * if recursive is true, return files in the subtree rooted at the path. 1754 * If the path is a file, return the file's status and block locations. 1755 * 1756 * @param f is the path 1757 * @param recursive if the subdirectories need to be traversed recursively 1758 * 1759 * @return an iterator that traverses statuses of the files 1760 * 1761 * @throws FileNotFoundException when the path does not exist; 1762 * IOException see specific implementation 1763 */ 1764 public RemoteIterator<LocatedFileStatus> listFiles( 1765 final Path f, final boolean recursive) 1766 throws FileNotFoundException, IOException { 1767 return new RemoteIterator<LocatedFileStatus>() { 1768 private Stack<RemoteIterator<LocatedFileStatus>> itors = 1769 new Stack<RemoteIterator<LocatedFileStatus>>(); 1770 private RemoteIterator<LocatedFileStatus> curItor = 1771 listLocatedStatus(f); 1772 private LocatedFileStatus curFile; 1773 1774 @Override 1775 public boolean hasNext() throws IOException { 1776 while (curFile == null) { 1777 if (curItor.hasNext()) { 1778 handleFileStat(curItor.next()); 1779 } else if (!itors.empty()) { 1780 curItor = itors.pop(); 1781 } else { 1782 return false; 1783 } 1784 } 1785 return true; 1786 } 1787 1788 /** 1789 * Process the input stat. 1790 * If it is a file, return the file stat. 1791 * If it is a directory, traverse the directory if recursive is true; 1792 * ignore it if recursive is false. 1793 * @param stat input status 1794 * @throws IOException if any IO error occurs 1795 */ 1796 private void handleFileStat(LocatedFileStatus stat) throws IOException { 1797 if (stat.isFile()) { // file 1798 curFile = stat; 1799 } else if (recursive) { // directory 1800 itors.push(curItor); 1801 curItor = listLocatedStatus(stat.getPath()); 1802 } 1803 } 1804 1805 @Override 1806 public LocatedFileStatus next() throws IOException { 1807 if (hasNext()) { 1808 LocatedFileStatus result = curFile; 1809 curFile = null; 1810 return result; 1811 } 1812 throw new java.util.NoSuchElementException("No more entry in " + f); 1813 } 1814 }; 1815 } 1816 1817 /** Return the current user's home directory in this filesystem. 1818 * The default implementation returns "/user/$USER/". 1819 */ 1820 public Path getHomeDirectory() { 1821 return this.makeQualified( 1822 new Path("/user/"+System.getProperty("user.name"))); 1823 } 1824 1825 1826 /** 1827 * Set the current working directory for the given file system. All relative 1828 * paths will be resolved relative to it. 1829 * 1830 * @param new_dir 1831 */ 1832 public abstract void setWorkingDirectory(Path new_dir); 1833 1834 /** 1835 * Get the current working directory for the given file system 1836 * @return the directory pathname 1837 */ 1838 public abstract Path getWorkingDirectory(); 1839 1840 1841 /** 1842 * Note: with the new FilesContext class, getWorkingDirectory() 1843 * will be removed. 1844 * The working directory is implemented in FilesContext. 1845 * 1846 * Some file systems like LocalFileSystem have an initial workingDir 1847 * that we use as the starting workingDir. For other file systems 1848 * like HDFS there is no built in notion of an initial workingDir. 1849 * 1850 * @return if there is built in notion of workingDir then it 1851 * is returned; else a null is returned. 1852 */ 1853 protected Path getInitialWorkingDirectory() { 1854 return null; 1855 } 1856 1857 /** 1858 * Call {@link #mkdirs(Path, FsPermission)} with default permission. 1859 */ 1860 public boolean mkdirs(Path f) throws IOException { 1861 return mkdirs(f, FsPermission.getDirDefault()); 1862 } 1863 1864 /** 1865 * Make the given file and all non-existent parents into 1866 * directories. Has the semantics of Unix 'mkdir -p'. 1867 * Existence of the directory hierarchy is not an error. 1868 * @param f path to create 1869 * @param permission to apply to f 1870 */ 1871 public abstract boolean mkdirs(Path f, FsPermission permission 1872 ) throws IOException; 1873 1874 /** 1875 * The src file is on the local disk. Add it to FS at 1876 * the given dst name and the source is kept intact afterwards 1877 * @param src path 1878 * @param dst path 1879 */ 1880 public void copyFromLocalFile(Path src, Path dst) 1881 throws IOException { 1882 copyFromLocalFile(false, src, dst); 1883 } 1884 1885 /** 1886 * The src files is on the local disk. Add it to FS at 1887 * the given dst name, removing the source afterwards. 1888 * @param srcs path 1889 * @param dst path 1890 */ 1891 public void moveFromLocalFile(Path[] srcs, Path dst) 1892 throws IOException { 1893 copyFromLocalFile(true, true, srcs, dst); 1894 } 1895 1896 /** 1897 * The src file is on the local disk. Add it to FS at 1898 * the given dst name, removing the source afterwards. 1899 * @param src path 1900 * @param dst path 1901 */ 1902 public void moveFromLocalFile(Path src, Path dst) 1903 throws IOException { 1904 copyFromLocalFile(true, src, dst); 1905 } 1906 1907 /** 1908 * The src file is on the local disk. Add it to FS at 1909 * the given dst name. 1910 * delSrc indicates if the source should be removed 1911 * @param delSrc whether to delete the src 1912 * @param src path 1913 * @param dst path 1914 */ 1915 public void copyFromLocalFile(boolean delSrc, Path src, Path dst) 1916 throws IOException { 1917 copyFromLocalFile(delSrc, true, src, dst); 1918 } 1919 1920 /** 1921 * The src files are on the local disk. Add it to FS at 1922 * the given dst name. 1923 * delSrc indicates if the source should be removed 1924 * @param delSrc whether to delete the src 1925 * @param overwrite whether to overwrite an existing file 1926 * @param srcs array of paths which are source 1927 * @param dst path 1928 */ 1929 public void copyFromLocalFile(boolean delSrc, boolean overwrite, 1930 Path[] srcs, Path dst) 1931 throws IOException { 1932 Configuration conf = getConf(); 1933 FileUtil.copy(getLocal(conf), srcs, this, dst, delSrc, overwrite, conf); 1934 } 1935 1936 /** 1937 * The src file is on the local disk. Add it to FS at 1938 * the given dst name. 1939 * delSrc indicates if the source should be removed 1940 * @param delSrc whether to delete the src 1941 * @param overwrite whether to overwrite an existing file 1942 * @param src path 1943 * @param dst path 1944 */ 1945 public void copyFromLocalFile(boolean delSrc, boolean overwrite, 1946 Path src, Path dst) 1947 throws IOException { 1948 Configuration conf = getConf(); 1949 FileUtil.copy(getLocal(conf), src, this, dst, delSrc, overwrite, conf); 1950 } 1951 1952 /** 1953 * The src file is under FS, and the dst is on the local disk. 1954 * Copy it from FS control to the local dst name. 1955 * @param src path 1956 * @param dst path 1957 */ 1958 public void copyToLocalFile(Path src, Path dst) throws IOException { 1959 copyToLocalFile(false, src, dst); 1960 } 1961 1962 /** 1963 * The src file is under FS, and the dst is on the local disk. 1964 * Copy it from FS control to the local dst name. 1965 * Remove the source afterwards 1966 * @param src path 1967 * @param dst path 1968 */ 1969 public void moveToLocalFile(Path src, Path dst) throws IOException { 1970 copyToLocalFile(true, src, dst); 1971 } 1972 1973 /** 1974 * The src file is under FS, and the dst is on the local disk. 1975 * Copy it from FS control to the local dst name. 1976 * delSrc indicates if the src will be removed or not. 1977 * @param delSrc whether to delete the src 1978 * @param src path 1979 * @param dst path 1980 */ 1981 public void copyToLocalFile(boolean delSrc, Path src, Path dst) 1982 throws IOException { 1983 copyToLocalFile(delSrc, src, dst, false); 1984 } 1985 1986 /** 1987 * The src file is under FS, and the dst is on the local disk. Copy it from FS 1988 * control to the local dst name. delSrc indicates if the src will be removed 1989 * or not. useRawLocalFileSystem indicates whether to use RawLocalFileSystem 1990 * as local file system or not. RawLocalFileSystem is non crc file system.So, 1991 * It will not create any crc files at local. 1992 * 1993 * @param delSrc 1994 * whether to delete the src 1995 * @param src 1996 * path 1997 * @param dst 1998 * path 1999 * @param useRawLocalFileSystem 2000 * whether to use RawLocalFileSystem as local file system or not. 2001 * 2002 * @throws IOException 2003 * - if any IO error 2004 */ 2005 public void copyToLocalFile(boolean delSrc, Path src, Path dst, 2006 boolean useRawLocalFileSystem) throws IOException { 2007 Configuration conf = getConf(); 2008 FileSystem local = null; 2009 if (useRawLocalFileSystem) { 2010 local = getLocal(conf).getRawFileSystem(); 2011 } else { 2012 local = getLocal(conf); 2013 } 2014 FileUtil.copy(this, src, local, dst, delSrc, conf); 2015 } 2016 2017 /** 2018 * Returns a local File that the user can write output to. The caller 2019 * provides both the eventual FS target name and the local working 2020 * file. If the FS is local, we write directly into the target. If 2021 * the FS is remote, we write into the tmp local area. 2022 * @param fsOutputFile path of output file 2023 * @param tmpLocalFile path of local tmp file 2024 */ 2025 public Path startLocalOutput(Path fsOutputFile, Path tmpLocalFile) 2026 throws IOException { 2027 return tmpLocalFile; 2028 } 2029 2030 /** 2031 * Called when we're all done writing to the target. A local FS will 2032 * do nothing, because we've written to exactly the right place. A remote 2033 * FS will copy the contents of tmpLocalFile to the correct target at 2034 * fsOutputFile. 2035 * @param fsOutputFile path of output file 2036 * @param tmpLocalFile path to local tmp file 2037 */ 2038 public void completeLocalOutput(Path fsOutputFile, Path tmpLocalFile) 2039 throws IOException { 2040 moveFromLocalFile(tmpLocalFile, fsOutputFile); 2041 } 2042 2043 /** 2044 * No more filesystem operations are needed. Will 2045 * release any held locks. 2046 */ 2047 @Override 2048 public void close() throws IOException { 2049 // delete all files that were marked as delete-on-exit. 2050 processDeleteOnExit(); 2051 CACHE.remove(this.key, this); 2052 } 2053 2054 /** Return the total size of all files in the filesystem.*/ 2055 public long getUsed() throws IOException{ 2056 long used = 0; 2057 FileStatus[] files = listStatus(new Path("/")); 2058 for(FileStatus file:files){ 2059 used += file.getLen(); 2060 } 2061 return used; 2062 } 2063 2064 /** 2065 * Get the block size for a particular file. 2066 * @param f the filename 2067 * @return the number of bytes in a block 2068 */ 2069 /** @deprecated Use getFileStatus() instead */ 2070 @Deprecated 2071 public long getBlockSize(Path f) throws IOException { 2072 return getFileStatus(f).getBlockSize(); 2073 } 2074 2075 /** 2076 * Return the number of bytes that large input files should be optimally 2077 * be split into to minimize i/o time. 2078 * @deprecated use {@link #getDefaultBlockSize(Path)} instead 2079 */ 2080 @Deprecated 2081 public long getDefaultBlockSize() { 2082 // default to 32MB: large enough to minimize the impact of seeks 2083 return getConf().getLong("fs.local.block.size", 32 * 1024 * 1024); 2084 } 2085 2086 /** Return the number of bytes that large input files should be optimally 2087 * be split into to minimize i/o time. The given path will be used to 2088 * locate the actual filesystem. The full path does not have to exist. 2089 * @param f path of file 2090 * @return the default block size for the path's filesystem 2091 */ 2092 public long getDefaultBlockSize(Path f) { 2093 return getDefaultBlockSize(); 2094 } 2095 2096 /** 2097 * Get the default replication. 2098 * @deprecated use {@link #getDefaultReplication(Path)} instead 2099 */ 2100 @Deprecated 2101 public short getDefaultReplication() { return 1; } 2102 2103 /** 2104 * Get the default replication for a path. The given path will be used to 2105 * locate the actual filesystem. The full path does not have to exist. 2106 * @param path of the file 2107 * @return default replication for the path's filesystem 2108 */ 2109 public short getDefaultReplication(Path path) { 2110 return getDefaultReplication(); 2111 } 2112 2113 /** 2114 * Return a file status object that represents the path. 2115 * @param f The path we want information from 2116 * @return a FileStatus object 2117 * @throws FileNotFoundException when the path does not exist; 2118 * IOException see specific implementation 2119 */ 2120 public abstract FileStatus getFileStatus(Path f) throws IOException; 2121 2122 /** 2123 * Checks if the user can access a path. The mode specifies which access 2124 * checks to perform. If the requested permissions are granted, then the 2125 * method returns normally. If access is denied, then the method throws an 2126 * {@link AccessControlException}. 2127 * <p/> 2128 * The default implementation of this method calls {@link #getFileStatus(Path)} 2129 * and checks the returned permissions against the requested permissions. 2130 * Note that the getFileStatus call will be subject to authorization checks. 2131 * Typically, this requires search (execute) permissions on each directory in 2132 * the path's prefix, but this is implementation-defined. Any file system 2133 * that provides a richer authorization model (such as ACLs) may override the 2134 * default implementation so that it checks against that model instead. 2135 * <p> 2136 * In general, applications should avoid using this method, due to the risk of 2137 * time-of-check/time-of-use race conditions. The permissions on a file may 2138 * change immediately after the access call returns. Most applications should 2139 * prefer running specific file system actions as the desired user represented 2140 * by a {@link UserGroupInformation}. 2141 * 2142 * @param path Path to check 2143 * @param mode type of access to check 2144 * @throws AccessControlException if access is denied 2145 * @throws FileNotFoundException if the path does not exist 2146 * @throws IOException see specific implementation 2147 */ 2148 @InterfaceAudience.LimitedPrivate({"HDFS", "Hive"}) 2149 public void access(Path path, FsAction mode) throws AccessControlException, 2150 FileNotFoundException, IOException { 2151 checkAccessPermissions(this.getFileStatus(path), mode); 2152 } 2153 2154 /** 2155 * This method provides the default implementation of 2156 * {@link #access(Path, FsAction)}. 2157 * 2158 * @param stat FileStatus to check 2159 * @param mode type of access to check 2160 * @throws IOException for any error 2161 */ 2162 @InterfaceAudience.Private 2163 static void checkAccessPermissions(FileStatus stat, FsAction mode) 2164 throws IOException { 2165 FsPermission perm = stat.getPermission(); 2166 UserGroupInformation ugi = UserGroupInformation.getCurrentUser(); 2167 String user = ugi.getShortUserName(); 2168 List<String> groups = Arrays.asList(ugi.getGroupNames()); 2169 if (user.equals(stat.getOwner())) { 2170 if (perm.getUserAction().implies(mode)) { 2171 return; 2172 } 2173 } else if (groups.contains(stat.getGroup())) { 2174 if (perm.getGroupAction().implies(mode)) { 2175 return; 2176 } 2177 } else { 2178 if (perm.getOtherAction().implies(mode)) { 2179 return; 2180 } 2181 } 2182 throw new AccessControlException(String.format( 2183 "Permission denied: user=%s, path=\"%s\":%s:%s:%s%s", user, stat.getPath(), 2184 stat.getOwner(), stat.getGroup(), stat.isDirectory() ? "d" : "-", perm)); 2185 } 2186 2187 /** 2188 * See {@link FileContext#fixRelativePart} 2189 */ 2190 protected Path fixRelativePart(Path p) { 2191 if (p.isUriPathAbsolute()) { 2192 return p; 2193 } else { 2194 return new Path(getWorkingDirectory(), p); 2195 } 2196 } 2197 2198 /** 2199 * See {@link FileContext#createSymlink(Path, Path, boolean)} 2200 */ 2201 public void createSymlink(final Path target, final Path link, 2202 final boolean createParent) throws AccessControlException, 2203 FileAlreadyExistsException, FileNotFoundException, 2204 ParentNotDirectoryException, UnsupportedFileSystemException, 2205 IOException { 2206 // Supporting filesystems should override this method 2207 throw new UnsupportedOperationException( 2208 "Filesystem does not support symlinks!"); 2209 } 2210 2211 /** 2212 * See {@link FileContext#getFileLinkStatus(Path)} 2213 */ 2214 public FileStatus getFileLinkStatus(final Path f) 2215 throws AccessControlException, FileNotFoundException, 2216 UnsupportedFileSystemException, IOException { 2217 // Supporting filesystems should override this method 2218 return getFileStatus(f); 2219 } 2220 2221 /** 2222 * See {@link AbstractFileSystem#supportsSymlinks()} 2223 */ 2224 public boolean supportsSymlinks() { 2225 return false; 2226 } 2227 2228 /** 2229 * See {@link FileContext#getLinkTarget(Path)} 2230 */ 2231 public Path getLinkTarget(Path f) throws IOException { 2232 // Supporting filesystems should override this method 2233 throw new UnsupportedOperationException( 2234 "Filesystem does not support symlinks!"); 2235 } 2236 2237 /** 2238 * See {@link AbstractFileSystem#getLinkTarget(Path)} 2239 */ 2240 protected Path resolveLink(Path f) throws IOException { 2241 // Supporting filesystems should override this method 2242 throw new UnsupportedOperationException( 2243 "Filesystem does not support symlinks!"); 2244 } 2245 2246 /** 2247 * Get the checksum of a file. 2248 * 2249 * @param f The file path 2250 * @return The file checksum. The default return value is null, 2251 * which indicates that no checksum algorithm is implemented 2252 * in the corresponding FileSystem. 2253 */ 2254 public FileChecksum getFileChecksum(Path f) throws IOException { 2255 return getFileChecksum(f, Long.MAX_VALUE); 2256 } 2257 2258 /** 2259 * Get the checksum of a file, from the beginning of the file till the 2260 * specific length. 2261 * @param f The file path 2262 * @param length The length of the file range for checksum calculation 2263 * @return The file checksum. 2264 */ 2265 public FileChecksum getFileChecksum(Path f, final long length) 2266 throws IOException { 2267 return null; 2268 } 2269 2270 /** 2271 * Set the verify checksum flag. This is only applicable if the 2272 * corresponding FileSystem supports checksum. By default doesn't do anything. 2273 * @param verifyChecksum 2274 */ 2275 public void setVerifyChecksum(boolean verifyChecksum) { 2276 //doesn't do anything 2277 } 2278 2279 /** 2280 * Set the write checksum flag. This is only applicable if the 2281 * corresponding FileSystem supports checksum. By default doesn't do anything. 2282 * @param writeChecksum 2283 */ 2284 public void setWriteChecksum(boolean writeChecksum) { 2285 //doesn't do anything 2286 } 2287 2288 /** 2289 * Returns a status object describing the use and capacity of the 2290 * file system. If the file system has multiple partitions, the 2291 * use and capacity of the root partition is reflected. 2292 * 2293 * @return a FsStatus object 2294 * @throws IOException 2295 * see specific implementation 2296 */ 2297 public FsStatus getStatus() throws IOException { 2298 return getStatus(null); 2299 } 2300 2301 /** 2302 * Returns a status object describing the use and capacity of the 2303 * file system. If the file system has multiple partitions, the 2304 * use and capacity of the partition pointed to by the specified 2305 * path is reflected. 2306 * @param p Path for which status should be obtained. null means 2307 * the default partition. 2308 * @return a FsStatus object 2309 * @throws IOException 2310 * see specific implementation 2311 */ 2312 public FsStatus getStatus(Path p) throws IOException { 2313 return new FsStatus(Long.MAX_VALUE, 0, Long.MAX_VALUE); 2314 } 2315 2316 /** 2317 * Set permission of a path. 2318 * @param p 2319 * @param permission 2320 */ 2321 public void setPermission(Path p, FsPermission permission 2322 ) throws IOException { 2323 } 2324 2325 /** 2326 * Set owner of a path (i.e. a file or a directory). 2327 * The parameters username and groupname cannot both be null. 2328 * @param p The path 2329 * @param username If it is null, the original username remains unchanged. 2330 * @param groupname If it is null, the original groupname remains unchanged. 2331 */ 2332 public void setOwner(Path p, String username, String groupname 2333 ) throws IOException { 2334 } 2335 2336 /** 2337 * Set access time of a file 2338 * @param p The path 2339 * @param mtime Set the modification time of this file. 2340 * The number of milliseconds since Jan 1, 1970. 2341 * A value of -1 means that this call should not set modification time. 2342 * @param atime Set the access time of this file. 2343 * The number of milliseconds since Jan 1, 1970. 2344 * A value of -1 means that this call should not set access time. 2345 */ 2346 public void setTimes(Path p, long mtime, long atime 2347 ) throws IOException { 2348 } 2349 2350 /** 2351 * Create a snapshot with a default name. 2352 * @param path The directory where snapshots will be taken. 2353 * @return the snapshot path. 2354 */ 2355 public final Path createSnapshot(Path path) throws IOException { 2356 return createSnapshot(path, null); 2357 } 2358 2359 /** 2360 * Create a snapshot 2361 * @param path The directory where snapshots will be taken. 2362 * @param snapshotName The name of the snapshot 2363 * @return the snapshot path. 2364 */ 2365 public Path createSnapshot(Path path, String snapshotName) 2366 throws IOException { 2367 throw new UnsupportedOperationException(getClass().getSimpleName() 2368 + " doesn't support createSnapshot"); 2369 } 2370 2371 /** 2372 * Rename a snapshot 2373 * @param path The directory path where the snapshot was taken 2374 * @param snapshotOldName Old name of the snapshot 2375 * @param snapshotNewName New name of the snapshot 2376 * @throws IOException 2377 */ 2378 public void renameSnapshot(Path path, String snapshotOldName, 2379 String snapshotNewName) throws IOException { 2380 throw new UnsupportedOperationException(getClass().getSimpleName() 2381 + " doesn't support renameSnapshot"); 2382 } 2383 2384 /** 2385 * Delete a snapshot of a directory 2386 * @param path The directory that the to-be-deleted snapshot belongs to 2387 * @param snapshotName The name of the snapshot 2388 */ 2389 public void deleteSnapshot(Path path, String snapshotName) 2390 throws IOException { 2391 throw new UnsupportedOperationException(getClass().getSimpleName() 2392 + " doesn't support deleteSnapshot"); 2393 } 2394 2395 /** 2396 * Modifies ACL entries of files and directories. This method can add new ACL 2397 * entries or modify the permissions on existing ACL entries. All existing 2398 * ACL entries that are not specified in this call are retained without 2399 * changes. (Modifications are merged into the current ACL.) 2400 * 2401 * @param path Path to modify 2402 * @param aclSpec List<AclEntry> describing modifications 2403 * @throws IOException if an ACL could not be modified 2404 */ 2405 public void modifyAclEntries(Path path, List<AclEntry> aclSpec) 2406 throws IOException { 2407 throw new UnsupportedOperationException(getClass().getSimpleName() 2408 + " doesn't support modifyAclEntries"); 2409 } 2410 2411 /** 2412 * Removes ACL entries from files and directories. Other ACL entries are 2413 * retained. 2414 * 2415 * @param path Path to modify 2416 * @param aclSpec List<AclEntry> describing entries to remove 2417 * @throws IOException if an ACL could not be modified 2418 */ 2419 public void removeAclEntries(Path path, List<AclEntry> aclSpec) 2420 throws IOException { 2421 throw new UnsupportedOperationException(getClass().getSimpleName() 2422 + " doesn't support removeAclEntries"); 2423 } 2424 2425 /** 2426 * Removes all default ACL entries from files and directories. 2427 * 2428 * @param path Path to modify 2429 * @throws IOException if an ACL could not be modified 2430 */ 2431 public void removeDefaultAcl(Path path) 2432 throws IOException { 2433 throw new UnsupportedOperationException(getClass().getSimpleName() 2434 + " doesn't support removeDefaultAcl"); 2435 } 2436 2437 /** 2438 * Removes all but the base ACL entries of files and directories. The entries 2439 * for user, group, and others are retained for compatibility with permission 2440 * bits. 2441 * 2442 * @param path Path to modify 2443 * @throws IOException if an ACL could not be removed 2444 */ 2445 public void removeAcl(Path path) 2446 throws IOException { 2447 throw new UnsupportedOperationException(getClass().getSimpleName() 2448 + " doesn't support removeAcl"); 2449 } 2450 2451 /** 2452 * Fully replaces ACL of files and directories, discarding all existing 2453 * entries. 2454 * 2455 * @param path Path to modify 2456 * @param aclSpec List<AclEntry> describing modifications, must include entries 2457 * for user, group, and others for compatibility with permission bits. 2458 * @throws IOException if an ACL could not be modified 2459 */ 2460 public void setAcl(Path path, List<AclEntry> aclSpec) throws IOException { 2461 throw new UnsupportedOperationException(getClass().getSimpleName() 2462 + " doesn't support setAcl"); 2463 } 2464 2465 /** 2466 * Gets the ACL of a file or directory. 2467 * 2468 * @param path Path to get 2469 * @return AclStatus describing the ACL of the file or directory 2470 * @throws IOException if an ACL could not be read 2471 */ 2472 public AclStatus getAclStatus(Path path) throws IOException { 2473 throw new UnsupportedOperationException(getClass().getSimpleName() 2474 + " doesn't support getAclStatus"); 2475 } 2476 2477 /** 2478 * Set an xattr of a file or directory. 2479 * The name must be prefixed with the namespace followed by ".". For example, 2480 * "user.attr". 2481 * <p/> 2482 * Refer to the HDFS extended attributes user documentation for details. 2483 * 2484 * @param path Path to modify 2485 * @param name xattr name. 2486 * @param value xattr value. 2487 * @throws IOException 2488 */ 2489 public void setXAttr(Path path, String name, byte[] value) 2490 throws IOException { 2491 setXAttr(path, name, value, EnumSet.of(XAttrSetFlag.CREATE, 2492 XAttrSetFlag.REPLACE)); 2493 } 2494 2495 /** 2496 * Set an xattr of a file or directory. 2497 * The name must be prefixed with the namespace followed by ".". For example, 2498 * "user.attr". 2499 * <p/> 2500 * Refer to the HDFS extended attributes user documentation for details. 2501 * 2502 * @param path Path to modify 2503 * @param name xattr name. 2504 * @param value xattr value. 2505 * @param flag xattr set flag 2506 * @throws IOException 2507 */ 2508 public void setXAttr(Path path, String name, byte[] value, 2509 EnumSet<XAttrSetFlag> flag) throws IOException { 2510 throw new UnsupportedOperationException(getClass().getSimpleName() 2511 + " doesn't support setXAttr"); 2512 } 2513 2514 /** 2515 * Get an xattr name and value for a file or directory. 2516 * The name must be prefixed with the namespace followed by ".". For example, 2517 * "user.attr". 2518 * <p/> 2519 * Refer to the HDFS extended attributes user documentation for details. 2520 * 2521 * @param path Path to get extended attribute 2522 * @param name xattr name. 2523 * @return byte[] xattr value. 2524 * @throws IOException 2525 */ 2526 public byte[] getXAttr(Path path, String name) throws IOException { 2527 throw new UnsupportedOperationException(getClass().getSimpleName() 2528 + " doesn't support getXAttr"); 2529 } 2530 2531 /** 2532 * Get all of the xattr name/value pairs for a file or directory. 2533 * Only those xattrs which the logged-in user has permissions to view 2534 * are returned. 2535 * <p/> 2536 * Refer to the HDFS extended attributes user documentation for details. 2537 * 2538 * @param path Path to get extended attributes 2539 * @return Map<String, byte[]> describing the XAttrs of the file or directory 2540 * @throws IOException 2541 */ 2542 public Map<String, byte[]> getXAttrs(Path path) throws IOException { 2543 throw new UnsupportedOperationException(getClass().getSimpleName() 2544 + " doesn't support getXAttrs"); 2545 } 2546 2547 /** 2548 * Get all of the xattrs name/value pairs for a file or directory. 2549 * Only those xattrs which the logged-in user has permissions to view 2550 * are returned. 2551 * <p/> 2552 * Refer to the HDFS extended attributes user documentation for details. 2553 * 2554 * @param path Path to get extended attributes 2555 * @param names XAttr names. 2556 * @return Map<String, byte[]> describing the XAttrs of the file or directory 2557 * @throws IOException 2558 */ 2559 public Map<String, byte[]> getXAttrs(Path path, List<String> names) 2560 throws IOException { 2561 throw new UnsupportedOperationException(getClass().getSimpleName() 2562 + " doesn't support getXAttrs"); 2563 } 2564 2565 /** 2566 * Get all of the xattr names for a file or directory. 2567 * Only those xattr names which the logged-in user has permissions to view 2568 * are returned. 2569 * <p/> 2570 * Refer to the HDFS extended attributes user documentation for details. 2571 * 2572 * @param path Path to get extended attributes 2573 * @return List<String> of the XAttr names of the file or directory 2574 * @throws IOException 2575 */ 2576 public List<String> listXAttrs(Path path) throws IOException { 2577 throw new UnsupportedOperationException(getClass().getSimpleName() 2578 + " doesn't support listXAttrs"); 2579 } 2580 2581 /** 2582 * Remove an xattr of a file or directory. 2583 * The name must be prefixed with the namespace followed by ".". For example, 2584 * "user.attr". 2585 * <p/> 2586 * Refer to the HDFS extended attributes user documentation for details. 2587 * 2588 * @param path Path to remove extended attribute 2589 * @param name xattr name 2590 * @throws IOException 2591 */ 2592 public void removeXAttr(Path path, String name) throws IOException { 2593 throw new UnsupportedOperationException(getClass().getSimpleName() 2594 + " doesn't support removeXAttr"); 2595 } 2596 2597 /** 2598 * Get the root directory of Trash for current user when the path specified 2599 * is deleted. 2600 * 2601 * @param path the trash root of the path to be determined. 2602 * @return the default implementation returns "/user/$USER/.Trash". 2603 */ 2604 public Path getTrashRoot(Path path) { 2605 return this.makeQualified(new Path(getHomeDirectory().toUri().getPath(), 2606 TRASH_PREFIX)); 2607 } 2608 2609 /** 2610 * Get all the trash roots for current user or all users. 2611 * 2612 * @param allUsers return trash roots for all users if true. 2613 * @return all the trash root directories. 2614 * Default FileSystem returns .Trash under users' home directories if 2615 * /user/$USER/.Trash exists. 2616 */ 2617 public Collection<FileStatus> getTrashRoots(boolean allUsers) { 2618 Path userHome = new Path(getHomeDirectory().toUri().getPath()); 2619 List<FileStatus> ret = new ArrayList<>(); 2620 try { 2621 if (!allUsers) { 2622 Path userTrash = new Path(userHome, TRASH_PREFIX); 2623 if (exists(userTrash)) { 2624 ret.add(getFileStatus(userTrash)); 2625 } 2626 } else { 2627 Path homeParent = userHome.getParent(); 2628 if (exists(homeParent)) { 2629 FileStatus[] candidates = listStatus(homeParent); 2630 for (FileStatus candidate : candidates) { 2631 Path userTrash = new Path(candidate.getPath(), TRASH_PREFIX); 2632 if (exists(userTrash)) { 2633 candidate.setPath(userTrash); 2634 ret.add(candidate); 2635 } 2636 } 2637 } 2638 } 2639 } catch (IOException e) { 2640 LOG.warn("Cannot get all trash roots", e); 2641 } 2642 return ret; 2643 } 2644 2645 // making it volatile to be able to do a double checked locking 2646 private volatile static boolean FILE_SYSTEMS_LOADED = false; 2647 2648 private static final Map<String, Class<? extends FileSystem>> 2649 SERVICE_FILE_SYSTEMS = new HashMap<String, Class<? extends FileSystem>>(); 2650 2651 private static void loadFileSystems() { 2652 synchronized (FileSystem.class) { 2653 if (!FILE_SYSTEMS_LOADED) { 2654 ServiceLoader<FileSystem> serviceLoader = ServiceLoader.load(FileSystem.class); 2655 for (FileSystem fs : serviceLoader) { 2656 SERVICE_FILE_SYSTEMS.put(fs.getScheme(), fs.getClass()); 2657 } 2658 FILE_SYSTEMS_LOADED = true; 2659 } 2660 } 2661 } 2662 2663 public static Class<? extends FileSystem> getFileSystemClass(String scheme, 2664 Configuration conf) throws IOException { 2665 if (!FILE_SYSTEMS_LOADED) { 2666 loadFileSystems(); 2667 } 2668 Class<? extends FileSystem> clazz = null; 2669 if (conf != null) { 2670 clazz = (Class<? extends FileSystem>) conf.getClass("fs." + scheme + ".impl", null); 2671 } 2672 if (clazz == null) { 2673 clazz = SERVICE_FILE_SYSTEMS.get(scheme); 2674 } 2675 if (clazz == null) { 2676 throw new IOException("No FileSystem for scheme: " + scheme); 2677 } 2678 return clazz; 2679 } 2680 2681 private static FileSystem createFileSystem(URI uri, Configuration conf 2682 ) throws IOException { 2683 Tracer tracer = FsTracer.get(conf); 2684 TraceScope scope = null; 2685 if (tracer != null) { 2686 scope = tracer.newScope("FileSystem#createFileSystem"); 2687 scope.addKVAnnotation("scheme", uri.getScheme()); 2688 } 2689 try { 2690 Class<?> clazz = getFileSystemClass(uri.getScheme(), conf); 2691 if (clazz == null) { 2692 throw new IOException("No FileSystem for scheme: " + uri.getScheme()); 2693 } 2694 FileSystem fs = (FileSystem)ReflectionUtils.newInstance(clazz, conf); 2695 fs.tracer = tracer; 2696 fs.initialize(uri, conf); 2697 return fs; 2698 } finally { 2699 if (scope != null) scope.close(); 2700 } 2701 } 2702 2703 /** Caching FileSystem objects */ 2704 static class Cache { 2705 private final ClientFinalizer clientFinalizer = new ClientFinalizer(); 2706 2707 private final Map<Key, FileSystem> map = new HashMap<Key, FileSystem>(); 2708 private final Set<Key> toAutoClose = new HashSet<Key>(); 2709 2710 /** A variable that makes all objects in the cache unique */ 2711 private static AtomicLong unique = new AtomicLong(1); 2712 2713 FileSystem get(URI uri, Configuration conf) throws IOException{ 2714 Key key = new Key(uri, conf); 2715 return getInternal(uri, conf, key); 2716 } 2717 2718 /** The objects inserted into the cache using this method are all unique */ 2719 FileSystem getUnique(URI uri, Configuration conf) throws IOException{ 2720 Key key = new Key(uri, conf, unique.getAndIncrement()); 2721 return getInternal(uri, conf, key); 2722 } 2723 2724 private FileSystem getInternal(URI uri, Configuration conf, Key key) throws IOException{ 2725 FileSystem fs; 2726 synchronized (this) { 2727 fs = map.get(key); 2728 } 2729 if (fs != null) { 2730 return fs; 2731 } 2732 2733 fs = createFileSystem(uri, conf); 2734 synchronized (this) { // refetch the lock again 2735 FileSystem oldfs = map.get(key); 2736 if (oldfs != null) { // a file system is created while lock is releasing 2737 fs.close(); // close the new file system 2738 return oldfs; // return the old file system 2739 } 2740 2741 // now insert the new file system into the map 2742 if (map.isEmpty() 2743 && !ShutdownHookManager.get().isShutdownInProgress()) { 2744 ShutdownHookManager.get().addShutdownHook(clientFinalizer, SHUTDOWN_HOOK_PRIORITY); 2745 } 2746 fs.key = key; 2747 map.put(key, fs); 2748 if (conf.getBoolean("fs.automatic.close", true)) { 2749 toAutoClose.add(key); 2750 } 2751 return fs; 2752 } 2753 } 2754 2755 synchronized void remove(Key key, FileSystem fs) { 2756 FileSystem cachedFs = map.remove(key); 2757 if (fs == cachedFs) { 2758 toAutoClose.remove(key); 2759 } else if (cachedFs != null) { 2760 map.put(key, cachedFs); 2761 } 2762 } 2763 2764 synchronized void closeAll() throws IOException { 2765 closeAll(false); 2766 } 2767 2768 /** 2769 * Close all FileSystem instances in the Cache. 2770 * @param onlyAutomatic only close those that are marked for automatic closing 2771 */ 2772 synchronized void closeAll(boolean onlyAutomatic) throws IOException { 2773 List<IOException> exceptions = new ArrayList<IOException>(); 2774 2775 // Make a copy of the keys in the map since we'll be modifying 2776 // the map while iterating over it, which isn't safe. 2777 List<Key> keys = new ArrayList<Key>(); 2778 keys.addAll(map.keySet()); 2779 2780 for (Key key : keys) { 2781 final FileSystem fs = map.get(key); 2782 2783 if (onlyAutomatic && !toAutoClose.contains(key)) { 2784 continue; 2785 } 2786 2787 //remove from cache 2788 map.remove(key); 2789 toAutoClose.remove(key); 2790 2791 if (fs != null) { 2792 try { 2793 fs.close(); 2794 } 2795 catch(IOException ioe) { 2796 exceptions.add(ioe); 2797 } 2798 } 2799 } 2800 2801 if (!exceptions.isEmpty()) { 2802 throw MultipleIOException.createIOException(exceptions); 2803 } 2804 } 2805 2806 private class ClientFinalizer implements Runnable { 2807 @Override 2808 public synchronized void run() { 2809 try { 2810 closeAll(true); 2811 } catch (IOException e) { 2812 LOG.info("FileSystem.Cache.closeAll() threw an exception:\n" + e); 2813 } 2814 } 2815 } 2816 2817 synchronized void closeAll(UserGroupInformation ugi) throws IOException { 2818 List<FileSystem> targetFSList = new ArrayList<FileSystem>(); 2819 //Make a pass over the list and collect the filesystems to close 2820 //we cannot close inline since close() removes the entry from the Map 2821 for (Map.Entry<Key, FileSystem> entry : map.entrySet()) { 2822 final Key key = entry.getKey(); 2823 final FileSystem fs = entry.getValue(); 2824 if (ugi.equals(key.ugi) && fs != null) { 2825 targetFSList.add(fs); 2826 } 2827 } 2828 List<IOException> exceptions = new ArrayList<IOException>(); 2829 //now make a pass over the target list and close each 2830 for (FileSystem fs : targetFSList) { 2831 try { 2832 fs.close(); 2833 } 2834 catch(IOException ioe) { 2835 exceptions.add(ioe); 2836 } 2837 } 2838 if (!exceptions.isEmpty()) { 2839 throw MultipleIOException.createIOException(exceptions); 2840 } 2841 } 2842 2843 /** FileSystem.Cache.Key */ 2844 static class Key { 2845 final String scheme; 2846 final String authority; 2847 final UserGroupInformation ugi; 2848 final long unique; // an artificial way to make a key unique 2849 2850 Key(URI uri, Configuration conf) throws IOException { 2851 this(uri, conf, 0); 2852 } 2853 2854 Key(URI uri, Configuration conf, long unique) throws IOException { 2855 scheme = uri.getScheme()==null?"":uri.getScheme().toLowerCase(); 2856 authority = uri.getAuthority()==null?"":uri.getAuthority().toLowerCase(); 2857 this.unique = unique; 2858 2859 this.ugi = UserGroupInformation.getCurrentUser(); 2860 } 2861 2862 @Override 2863 public int hashCode() { 2864 return (scheme + authority).hashCode() + ugi.hashCode() + (int)unique; 2865 } 2866 2867 static boolean isEqual(Object a, Object b) { 2868 return a == b || (a != null && a.equals(b)); 2869 } 2870 2871 @Override 2872 public boolean equals(Object obj) { 2873 if (obj == this) { 2874 return true; 2875 } 2876 if (obj != null && obj instanceof Key) { 2877 Key that = (Key)obj; 2878 return isEqual(this.scheme, that.scheme) 2879 && isEqual(this.authority, that.authority) 2880 && isEqual(this.ugi, that.ugi) 2881 && (this.unique == that.unique); 2882 } 2883 return false; 2884 } 2885 2886 @Override 2887 public String toString() { 2888 return "("+ugi.toString() + ")@" + scheme + "://" + authority; 2889 } 2890 } 2891 } 2892 2893 /** 2894 * Tracks statistics about how many reads, writes, and so forth have been 2895 * done in a FileSystem. 2896 * 2897 * Since there is only one of these objects per FileSystem, there will 2898 * typically be many threads writing to this object. Almost every operation 2899 * on an open file will involve a write to this object. In contrast, reading 2900 * statistics is done infrequently by most programs, and not at all by others. 2901 * Hence, this is optimized for writes. 2902 * 2903 * Each thread writes to its own thread-local area of memory. This removes 2904 * contention and allows us to scale up to many, many threads. To read 2905 * statistics, the reader thread totals up the contents of all of the 2906 * thread-local data areas. 2907 */ 2908 public static final class Statistics { 2909 /** 2910 * Statistics data. 2911 * 2912 * There is only a single writer to thread-local StatisticsData objects. 2913 * Hence, volatile is adequate here-- we do not need AtomicLong or similar 2914 * to prevent lost updates. 2915 * The Java specification guarantees that updates to volatile longs will 2916 * be perceived as atomic with respect to other threads, which is all we 2917 * need. 2918 */ 2919 public static class StatisticsData { 2920 volatile long bytesRead; 2921 volatile long bytesWritten; 2922 volatile int readOps; 2923 volatile int largeReadOps; 2924 volatile int writeOps; 2925 2926 /** 2927 * Add another StatisticsData object to this one. 2928 */ 2929 void add(StatisticsData other) { 2930 this.bytesRead += other.bytesRead; 2931 this.bytesWritten += other.bytesWritten; 2932 this.readOps += other.readOps; 2933 this.largeReadOps += other.largeReadOps; 2934 this.writeOps += other.writeOps; 2935 } 2936 2937 /** 2938 * Negate the values of all statistics. 2939 */ 2940 void negate() { 2941 this.bytesRead = -this.bytesRead; 2942 this.bytesWritten = -this.bytesWritten; 2943 this.readOps = -this.readOps; 2944 this.largeReadOps = -this.largeReadOps; 2945 this.writeOps = -this.writeOps; 2946 } 2947 2948 @Override 2949 public String toString() { 2950 return bytesRead + " bytes read, " + bytesWritten + " bytes written, " 2951 + readOps + " read ops, " + largeReadOps + " large read ops, " 2952 + writeOps + " write ops"; 2953 } 2954 2955 public long getBytesRead() { 2956 return bytesRead; 2957 } 2958 2959 public long getBytesWritten() { 2960 return bytesWritten; 2961 } 2962 2963 public int getReadOps() { 2964 return readOps; 2965 } 2966 2967 public int getLargeReadOps() { 2968 return largeReadOps; 2969 } 2970 2971 public int getWriteOps() { 2972 return writeOps; 2973 } 2974 } 2975 2976 private interface StatisticsAggregator<T> { 2977 void accept(StatisticsData data); 2978 T aggregate(); 2979 } 2980 2981 private final String scheme; 2982 2983 /** 2984 * rootData is data that doesn't belong to any thread, but will be added 2985 * to the totals. This is useful for making copies of Statistics objects, 2986 * and for storing data that pertains to threads that have been garbage 2987 * collected. Protected by the Statistics lock. 2988 */ 2989 private final StatisticsData rootData; 2990 2991 /** 2992 * Thread-local data. 2993 */ 2994 private final ThreadLocal<StatisticsData> threadData; 2995 2996 /** 2997 * Set of all thread-local data areas. Protected by the Statistics lock. 2998 * The references to the statistics data are kept using phantom references 2999 * to the associated threads. Proper clean-up is performed by the cleaner 3000 * thread when the threads are garbage collected. 3001 */ 3002 private final Set<StatisticsDataReference> allData; 3003 3004 /** 3005 * Global reference queue and a cleaner thread that manage statistics data 3006 * references from all filesystem instances. 3007 */ 3008 private static final ReferenceQueue<Thread> STATS_DATA_REF_QUEUE; 3009 private static final Thread STATS_DATA_CLEANER; 3010 3011 static { 3012 STATS_DATA_REF_QUEUE = new ReferenceQueue<Thread>(); 3013 // start a single daemon cleaner thread 3014 STATS_DATA_CLEANER = new Thread(new StatisticsDataReferenceCleaner()); 3015 STATS_DATA_CLEANER. 3016 setName(StatisticsDataReferenceCleaner.class.getName()); 3017 STATS_DATA_CLEANER.setDaemon(true); 3018 STATS_DATA_CLEANER.start(); 3019 } 3020 3021 public Statistics(String scheme) { 3022 this.scheme = scheme; 3023 this.rootData = new StatisticsData(); 3024 this.threadData = new ThreadLocal<StatisticsData>(); 3025 this.allData = new HashSet<StatisticsDataReference>(); 3026 } 3027 3028 /** 3029 * Copy constructor. 3030 * 3031 * @param other The input Statistics object which is cloned. 3032 */ 3033 public Statistics(Statistics other) { 3034 this.scheme = other.scheme; 3035 this.rootData = new StatisticsData(); 3036 other.visitAll(new StatisticsAggregator<Void>() { 3037 @Override 3038 public void accept(StatisticsData data) { 3039 rootData.add(data); 3040 } 3041 3042 public Void aggregate() { 3043 return null; 3044 } 3045 }); 3046 this.threadData = new ThreadLocal<StatisticsData>(); 3047 this.allData = new HashSet<StatisticsDataReference>(); 3048 } 3049 3050 /** 3051 * A phantom reference to a thread that also includes the data associated 3052 * with that thread. On the thread being garbage collected, it is enqueued 3053 * to the reference queue for clean-up. 3054 */ 3055 private class StatisticsDataReference extends PhantomReference<Thread> { 3056 private final StatisticsData data; 3057 3058 public StatisticsDataReference(StatisticsData data, Thread thread) { 3059 super(thread, STATS_DATA_REF_QUEUE); 3060 this.data = data; 3061 } 3062 3063 public StatisticsData getData() { 3064 return data; 3065 } 3066 3067 /** 3068 * Performs clean-up action when the associated thread is garbage 3069 * collected. 3070 */ 3071 public void cleanUp() { 3072 // use the statistics lock for safety 3073 synchronized (Statistics.this) { 3074 /* 3075 * If the thread that created this thread-local data no longer exists, 3076 * remove the StatisticsData from our list and fold the values into 3077 * rootData. 3078 */ 3079 rootData.add(data); 3080 allData.remove(this); 3081 } 3082 } 3083 } 3084 3085 /** 3086 * Background action to act on references being removed. 3087 */ 3088 private static class StatisticsDataReferenceCleaner implements Runnable { 3089 @Override 3090 public void run() { 3091 while (!Thread.interrupted()) { 3092 try { 3093 StatisticsDataReference ref = 3094 (StatisticsDataReference)STATS_DATA_REF_QUEUE.remove(); 3095 ref.cleanUp(); 3096 } catch (InterruptedException ie) { 3097 LOG.warn("Cleaner thread interrupted, will stop", ie); 3098 Thread.currentThread().interrupt(); 3099 } catch (Throwable th) { 3100 LOG.warn("Exception in the cleaner thread but it will continue to " 3101 + "run", th); 3102 } 3103 } 3104 } 3105 } 3106 3107 /** 3108 * Get or create the thread-local data associated with the current thread. 3109 */ 3110 public StatisticsData getThreadStatistics() { 3111 StatisticsData data = threadData.get(); 3112 if (data == null) { 3113 data = new StatisticsData(); 3114 threadData.set(data); 3115 StatisticsDataReference ref = 3116 new StatisticsDataReference(data, Thread.currentThread()); 3117 synchronized(this) { 3118 allData.add(ref); 3119 } 3120 } 3121 return data; 3122 } 3123 3124 /** 3125 * Increment the bytes read in the statistics 3126 * @param newBytes the additional bytes read 3127 */ 3128 public void incrementBytesRead(long newBytes) { 3129 getThreadStatistics().bytesRead += newBytes; 3130 } 3131 3132 /** 3133 * Increment the bytes written in the statistics 3134 * @param newBytes the additional bytes written 3135 */ 3136 public void incrementBytesWritten(long newBytes) { 3137 getThreadStatistics().bytesWritten += newBytes; 3138 } 3139 3140 /** 3141 * Increment the number of read operations 3142 * @param count number of read operations 3143 */ 3144 public void incrementReadOps(int count) { 3145 getThreadStatistics().readOps += count; 3146 } 3147 3148 /** 3149 * Increment the number of large read operations 3150 * @param count number of large read operations 3151 */ 3152 public void incrementLargeReadOps(int count) { 3153 getThreadStatistics().largeReadOps += count; 3154 } 3155 3156 /** 3157 * Increment the number of write operations 3158 * @param count number of write operations 3159 */ 3160 public void incrementWriteOps(int count) { 3161 getThreadStatistics().writeOps += count; 3162 } 3163 3164 /** 3165 * Apply the given aggregator to all StatisticsData objects associated with 3166 * this Statistics object. 3167 * 3168 * For each StatisticsData object, we will call accept on the visitor. 3169 * Finally, at the end, we will call aggregate to get the final total. 3170 * 3171 * @param visitor to use. 3172 * @return The total. 3173 */ 3174 private synchronized <T> T visitAll(StatisticsAggregator<T> visitor) { 3175 visitor.accept(rootData); 3176 for (StatisticsDataReference ref: allData) { 3177 StatisticsData data = ref.getData(); 3178 visitor.accept(data); 3179 } 3180 return visitor.aggregate(); 3181 } 3182 3183 /** 3184 * Get the total number of bytes read 3185 * @return the number of bytes 3186 */ 3187 public long getBytesRead() { 3188 return visitAll(new StatisticsAggregator<Long>() { 3189 private long bytesRead = 0; 3190 3191 @Override 3192 public void accept(StatisticsData data) { 3193 bytesRead += data.bytesRead; 3194 } 3195 3196 public Long aggregate() { 3197 return bytesRead; 3198 } 3199 }); 3200 } 3201 3202 /** 3203 * Get the total number of bytes written 3204 * @return the number of bytes 3205 */ 3206 public long getBytesWritten() { 3207 return visitAll(new StatisticsAggregator<Long>() { 3208 private long bytesWritten = 0; 3209 3210 @Override 3211 public void accept(StatisticsData data) { 3212 bytesWritten += data.bytesWritten; 3213 } 3214 3215 public Long aggregate() { 3216 return bytesWritten; 3217 } 3218 }); 3219 } 3220 3221 /** 3222 * Get the number of file system read operations such as list files 3223 * @return number of read operations 3224 */ 3225 public int getReadOps() { 3226 return visitAll(new StatisticsAggregator<Integer>() { 3227 private int readOps = 0; 3228 3229 @Override 3230 public void accept(StatisticsData data) { 3231 readOps += data.readOps; 3232 readOps += data.largeReadOps; 3233 } 3234 3235 public Integer aggregate() { 3236 return readOps; 3237 } 3238 }); 3239 } 3240 3241 /** 3242 * Get the number of large file system read operations such as list files 3243 * under a large directory 3244 * @return number of large read operations 3245 */ 3246 public int getLargeReadOps() { 3247 return visitAll(new StatisticsAggregator<Integer>() { 3248 private int largeReadOps = 0; 3249 3250 @Override 3251 public void accept(StatisticsData data) { 3252 largeReadOps += data.largeReadOps; 3253 } 3254 3255 public Integer aggregate() { 3256 return largeReadOps; 3257 } 3258 }); 3259 } 3260 3261 /** 3262 * Get the number of file system write operations such as create, append 3263 * rename etc. 3264 * @return number of write operations 3265 */ 3266 public int getWriteOps() { 3267 return visitAll(new StatisticsAggregator<Integer>() { 3268 private int writeOps = 0; 3269 3270 @Override 3271 public void accept(StatisticsData data) { 3272 writeOps += data.writeOps; 3273 } 3274 3275 public Integer aggregate() { 3276 return writeOps; 3277 } 3278 }); 3279 } 3280 3281 3282 @Override 3283 public String toString() { 3284 return visitAll(new StatisticsAggregator<String>() { 3285 private StatisticsData total = new StatisticsData(); 3286 3287 @Override 3288 public void accept(StatisticsData data) { 3289 total.add(data); 3290 } 3291 3292 public String aggregate() { 3293 return total.toString(); 3294 } 3295 }); 3296 } 3297 3298 /** 3299 * Resets all statistics to 0. 3300 * 3301 * In order to reset, we add up all the thread-local statistics data, and 3302 * set rootData to the negative of that. 3303 * 3304 * This may seem like a counterintuitive way to reset the statsitics. Why 3305 * can't we just zero out all the thread-local data? Well, thread-local 3306 * data can only be modified by the thread that owns it. If we tried to 3307 * modify the thread-local data from this thread, our modification might get 3308 * interleaved with a read-modify-write operation done by the thread that 3309 * owns the data. That would result in our update getting lost. 3310 * 3311 * The approach used here avoids this problem because it only ever reads 3312 * (not writes) the thread-local data. Both reads and writes to rootData 3313 * are done under the lock, so we're free to modify rootData from any thread 3314 * that holds the lock. 3315 */ 3316 public void reset() { 3317 visitAll(new StatisticsAggregator<Void>() { 3318 private StatisticsData total = new StatisticsData(); 3319 3320 @Override 3321 public void accept(StatisticsData data) { 3322 total.add(data); 3323 } 3324 3325 public Void aggregate() { 3326 total.negate(); 3327 rootData.add(total); 3328 return null; 3329 } 3330 }); 3331 } 3332 3333 /** 3334 * Get the uri scheme associated with this statistics object. 3335 * @return the schema associated with this set of statistics 3336 */ 3337 public String getScheme() { 3338 return scheme; 3339 } 3340 3341 @VisibleForTesting 3342 synchronized int getAllThreadLocalDataSize() { 3343 return allData.size(); 3344 } 3345 } 3346 3347 /** 3348 * Get the Map of Statistics object indexed by URI Scheme. 3349 * @return a Map having a key as URI scheme and value as Statistics object 3350 * @deprecated use {@link #getAllStatistics} instead 3351 */ 3352 @Deprecated 3353 public static synchronized Map<String, Statistics> getStatistics() { 3354 Map<String, Statistics> result = new HashMap<String, Statistics>(); 3355 for(Statistics stat: statisticsTable.values()) { 3356 result.put(stat.getScheme(), stat); 3357 } 3358 return result; 3359 } 3360 3361 /** 3362 * Return the FileSystem classes that have Statistics 3363 */ 3364 public static synchronized List<Statistics> getAllStatistics() { 3365 return new ArrayList<Statistics>(statisticsTable.values()); 3366 } 3367 3368 /** 3369 * Get the statistics for a particular file system 3370 * @param cls the class to lookup 3371 * @return a statistics object 3372 */ 3373 public static synchronized 3374 Statistics getStatistics(String scheme, Class<? extends FileSystem> cls) { 3375 Statistics result = statisticsTable.get(cls); 3376 if (result == null) { 3377 result = new Statistics(scheme); 3378 statisticsTable.put(cls, result); 3379 } 3380 return result; 3381 } 3382 3383 /** 3384 * Reset all statistics for all file systems 3385 */ 3386 public static synchronized void clearStatistics() { 3387 for(Statistics stat: statisticsTable.values()) { 3388 stat.reset(); 3389 } 3390 } 3391 3392 /** 3393 * Print all statistics for all file systems 3394 */ 3395 public static synchronized 3396 void printStatistics() throws IOException { 3397 for (Map.Entry<Class<? extends FileSystem>, Statistics> pair: 3398 statisticsTable.entrySet()) { 3399 System.out.println(" FileSystem " + pair.getKey().getName() + 3400 ": " + pair.getValue()); 3401 } 3402 } 3403 3404 // Symlinks are temporarily disabled - see HADOOP-10020 and HADOOP-10052 3405 private static boolean symlinksEnabled = false; 3406 3407 private static Configuration conf = null; 3408 3409 @VisibleForTesting 3410 public static boolean areSymlinksEnabled() { 3411 return symlinksEnabled; 3412 } 3413 3414 @VisibleForTesting 3415 public static void enableSymlinks() { 3416 symlinksEnabled = true; 3417 } 3418}