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