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