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}