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.FileNotFoundException;
021    import java.io.IOException;
022    import java.lang.reflect.Constructor;
023    import java.net.URI;
024    import java.net.URISyntaxException;
025    import java.util.ArrayList;
026    import java.util.EnumSet;
027    import java.util.HashMap;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.NoSuchElementException;
031    import java.util.StringTokenizer;
032    import java.util.concurrent.ConcurrentHashMap;
033    
034    import org.apache.commons.logging.Log;
035    import org.apache.commons.logging.LogFactory;
036    import org.apache.hadoop.HadoopIllegalArgumentException;
037    import org.apache.hadoop.classification.InterfaceAudience;
038    import org.apache.hadoop.classification.InterfaceStability;
039    import org.apache.hadoop.conf.Configuration;
040    import org.apache.hadoop.fs.FileSystem.Statistics;
041    import org.apache.hadoop.fs.Options.ChecksumOpt;
042    import org.apache.hadoop.fs.Options.CreateOpts;
043    import org.apache.hadoop.fs.Options.Rename;
044    import org.apache.hadoop.fs.permission.AclEntry;
045    import org.apache.hadoop.fs.permission.AclStatus;
046    import org.apache.hadoop.fs.permission.FsPermission;
047    import org.apache.hadoop.fs.InvalidPathException;
048    import org.apache.hadoop.security.AccessControlException;
049    import org.apache.hadoop.security.SecurityUtil;
050    import org.apache.hadoop.security.token.Token;
051    import org.apache.hadoop.util.Progressable;
052    
053    /**
054     * This class provides an interface for implementors of a Hadoop file system
055     * (analogous to the VFS of Unix). Applications do not access this class;
056     * instead they access files across all file systems using {@link FileContext}.
057     * 
058     * Pathnames passed to AbstractFileSystem can be fully qualified URI that
059     * matches the "this" file system (ie same scheme and authority) 
060     * or a Slash-relative name that is assumed to be relative
061     * to the root of the "this" file system .
062     */
063    @InterfaceAudience.Public
064    @InterfaceStability.Evolving /*Evolving for a release,to be changed to Stable */
065    public abstract class AbstractFileSystem {
066      static final Log LOG = LogFactory.getLog(AbstractFileSystem.class);
067    
068      /** Recording statistics per a file system class. */
069      private static final Map<URI, Statistics> 
070          STATISTICS_TABLE = new HashMap<URI, Statistics>();
071      
072      /** Cache of constructors for each file system class. */
073      private static final Map<Class<?>, Constructor<?>> CONSTRUCTOR_CACHE = 
074        new ConcurrentHashMap<Class<?>, Constructor<?>>();
075      
076      private static final Class<?>[] URI_CONFIG_ARGS = 
077        new Class[]{URI.class, Configuration.class};
078      
079      /** The statistics for this file system. */
080      protected Statistics statistics;
081      
082      private final URI myUri;
083      
084      public Statistics getStatistics() {
085        return statistics;
086      }
087      
088      /**
089       * Returns true if the specified string is considered valid in the path part
090       * of a URI by this file system.  The default implementation enforces the rules
091       * of HDFS, but subclasses may override this method to implement specific
092       * validation rules for specific file systems.
093       * 
094       * @param src String source filename to check, path part of the URI
095       * @return boolean true if the specified string is considered valid
096       */
097      public boolean isValidName(String src) {
098        // Prohibit ".." "." and anything containing ":"
099        StringTokenizer tokens = new StringTokenizer(src, Path.SEPARATOR);
100        while(tokens.hasMoreTokens()) {
101          String element = tokens.nextToken();
102          if (element.equals("..") ||
103              element.equals(".")  ||
104              (element.indexOf(":") >= 0)) {
105            return false;
106          }
107        }
108        return true;
109      }
110      
111      /** 
112       * Create an object for the given class and initialize it from conf.
113       * @param theClass class of which an object is created
114       * @param conf Configuration
115       * @return a new object
116       */
117      @SuppressWarnings("unchecked")
118      static <T> T newInstance(Class<T> theClass,
119        URI uri, Configuration conf) {
120        T result;
121        try {
122          Constructor<T> meth = (Constructor<T>) CONSTRUCTOR_CACHE.get(theClass);
123          if (meth == null) {
124            meth = theClass.getDeclaredConstructor(URI_CONFIG_ARGS);
125            meth.setAccessible(true);
126            CONSTRUCTOR_CACHE.put(theClass, meth);
127          }
128          result = meth.newInstance(uri, conf);
129        } catch (Exception e) {
130          throw new RuntimeException(e);
131        }
132        return result;
133      }
134      
135      /**
136       * Create a file system instance for the specified uri using the conf. The
137       * conf is used to find the class name that implements the file system. The
138       * conf is also passed to the file system for its configuration.
139       *
140       * @param uri URI of the file system
141       * @param conf Configuration for the file system
142       * 
143       * @return Returns the file system for the given URI
144       *
145       * @throws UnsupportedFileSystemException file system for <code>uri</code> is
146       *           not found
147       */
148      public static AbstractFileSystem createFileSystem(URI uri, Configuration conf)
149          throws UnsupportedFileSystemException {
150        Class<?> clazz = conf.getClass("fs.AbstractFileSystem." + 
151                                    uri.getScheme() + ".impl", null);
152        if (clazz == null) {
153          throw new UnsupportedFileSystemException(
154              "No AbstractFileSystem for scheme: " + uri.getScheme());
155        }
156        return (AbstractFileSystem) newInstance(clazz, uri, conf);
157      }
158    
159      /**
160       * Get the statistics for a particular file system.
161       * 
162       * @param uri
163       *          used as key to lookup STATISTICS_TABLE. Only scheme and authority
164       *          part of the uri are used.
165       * @return a statistics object
166       */
167      protected static synchronized Statistics getStatistics(URI uri) {
168        String scheme = uri.getScheme();
169        if (scheme == null) {
170          throw new IllegalArgumentException("Scheme not defined in the uri: "
171              + uri);
172        }
173        URI baseUri = getBaseUri(uri);
174        Statistics result = STATISTICS_TABLE.get(baseUri);
175        if (result == null) {
176          result = new Statistics(scheme);
177          STATISTICS_TABLE.put(baseUri, result);
178        }
179        return result;
180      }
181      
182      private static URI getBaseUri(URI uri) {
183        String scheme = uri.getScheme();
184        String authority = uri.getAuthority();
185        String baseUriString = scheme + "://";
186        if (authority != null) {
187          baseUriString = baseUriString + authority;
188        } else {
189          baseUriString = baseUriString + "/";
190        }
191        return URI.create(baseUriString);
192      }
193      
194      public static synchronized void clearStatistics() {
195        for(Statistics stat: STATISTICS_TABLE.values()) {
196          stat.reset();
197        }
198      }
199    
200      /**
201       * Prints statistics for all file systems.
202       */
203      public static synchronized void printStatistics() {
204        for (Map.Entry<URI, Statistics> pair : STATISTICS_TABLE.entrySet()) {
205          System.out.println("  FileSystem " + pair.getKey().getScheme() + "://"
206              + pair.getKey().getAuthority() + ": " + pair.getValue());
207        }
208      }
209      
210      protected static synchronized Map<URI, Statistics> getAllStatistics() {
211        Map<URI, Statistics> statsMap = new HashMap<URI, Statistics>(
212            STATISTICS_TABLE.size());
213        for (Map.Entry<URI, Statistics> pair : STATISTICS_TABLE.entrySet()) {
214          URI key = pair.getKey();
215          Statistics value = pair.getValue();
216          Statistics newStatsObj = new Statistics(value);
217          statsMap.put(URI.create(key.toString()), newStatsObj);
218        }
219        return statsMap;
220      }
221    
222      /**
223       * The main factory method for creating a file system. Get a file system for
224       * the URI's scheme and authority. The scheme of the <code>uri</code>
225       * determines a configuration property name,
226       * <tt>fs.AbstractFileSystem.<i>scheme</i>.impl</tt> whose value names the
227       * AbstractFileSystem class.
228       * 
229       * The entire URI and conf is passed to the AbstractFileSystem factory method.
230       * 
231       * @param uri for the file system to be created.
232       * @param conf which is passed to the file system impl.
233       * 
234       * @return file system for the given URI.
235       * 
236       * @throws UnsupportedFileSystemException if the file system for
237       *           <code>uri</code> is not supported.
238       */
239      public static AbstractFileSystem get(final URI uri, final Configuration conf)
240          throws UnsupportedFileSystemException {
241        return createFileSystem(uri, conf);
242      }
243    
244      /**
245       * Constructor to be called by subclasses.
246       * 
247       * @param uri for this file system.
248       * @param supportedScheme the scheme supported by the implementor
249       * @param authorityNeeded if true then theURI must have authority, if false
250       *          then the URI must have null authority.
251       *
252       * @throws URISyntaxException <code>uri</code> has syntax error
253       */
254      public AbstractFileSystem(final URI uri, final String supportedScheme,
255          final boolean authorityNeeded, final int defaultPort)
256          throws URISyntaxException {
257        myUri = getUri(uri, supportedScheme, authorityNeeded, defaultPort);
258        statistics = getStatistics(uri); 
259      }
260      
261      /**
262       * Check that the Uri's scheme matches
263       * @param uri
264       * @param supportedScheme
265       */
266      public void checkScheme(URI uri, String supportedScheme) {
267        String scheme = uri.getScheme();
268        if (scheme == null) {
269          throw new HadoopIllegalArgumentException("Uri without scheme: " + uri);
270        }
271        if (!scheme.equals(supportedScheme)) {
272          throw new HadoopIllegalArgumentException("Uri scheme " + uri
273              + " does not match the scheme " + supportedScheme);
274        }
275      }
276    
277      /**
278       * Get the URI for the file system based on the given URI. The path, query
279       * part of the given URI is stripped out and default file system port is used
280       * to form the URI.
281       * 
282       * @param uri FileSystem URI.
283       * @param authorityNeeded if true authority cannot be null in the URI. If
284       *          false authority must be null.
285       * @param defaultPort default port to use if port is not specified in the URI.
286       * 
287       * @return URI of the file system
288       * 
289       * @throws URISyntaxException <code>uri</code> has syntax error
290       */
291      private URI getUri(URI uri, String supportedScheme,
292          boolean authorityNeeded, int defaultPort) throws URISyntaxException {
293        checkScheme(uri, supportedScheme);
294        // A file system implementation that requires authority must always
295        // specify default port
296        if (defaultPort < 0 && authorityNeeded) {
297          throw new HadoopIllegalArgumentException(
298              "FileSystem implementation error -  default port " + defaultPort
299                  + " is not valid");
300        }
301        String authority = uri.getAuthority();
302        if (authority == null) {
303           if (authorityNeeded) {
304             throw new HadoopIllegalArgumentException("Uri without authority: " + uri);
305           } else {
306             return new URI(supportedScheme + ":///");
307           }   
308        }
309        // authority is non null  - AuthorityNeeded may be true or false.
310        int port = uri.getPort();
311        port = (port == -1 ? defaultPort : port);
312        if (port == -1) { // no port supplied and default port is not specified
313          return new URI(supportedScheme, authority, "/", null);
314        }
315        return new URI(supportedScheme + "://" + uri.getHost() + ":" + port);
316      }
317      
318      /**
319       * The default port of this file system.
320       * 
321       * @return default port of this file system's Uri scheme
322       *         A uri with a port of -1 => default port;
323       */
324      public abstract int getUriDefaultPort();
325    
326      /**
327       * Returns a URI whose scheme and authority identify this FileSystem.
328       * 
329       * @return the uri of this file system.
330       */
331      public URI getUri() {
332        return myUri;
333      }
334      
335      /**
336       * Check that a Path belongs to this FileSystem.
337       * 
338       * If the path is fully qualified URI, then its scheme and authority
339       * matches that of this file system. Otherwise the path must be 
340       * slash-relative name.
341       * 
342       * @throws InvalidPathException if the path is invalid
343       */
344      public void checkPath(Path path) {
345        URI uri = path.toUri();
346        String thatScheme = uri.getScheme();
347        String thatAuthority = uri.getAuthority();
348        if (thatScheme == null) {
349          if (thatAuthority == null) {
350            if (path.isUriPathAbsolute()) {
351              return;
352            }
353            throw new InvalidPathException("relative paths not allowed:" + 
354                path);
355          } else {
356            throw new InvalidPathException(
357                "Path without scheme with non-null authority:" + path);
358          }
359        }
360        String thisScheme = this.getUri().getScheme();
361        String thisHost = this.getUri().getHost();
362        String thatHost = uri.getHost();
363        
364        // Schemes and hosts must match.
365        // Allow for null Authority for file:///
366        if (!thisScheme.equalsIgnoreCase(thatScheme) ||
367           (thisHost != null && 
368                !thisHost.equalsIgnoreCase(thatHost)) ||
369           (thisHost == null && thatHost != null)) {
370          throw new InvalidPathException("Wrong FS: " + path + ", expected: "
371              + this.getUri());
372        }
373        
374        // Ports must match, unless this FS instance is using the default port, in
375        // which case the port may be omitted from the given URI
376        int thisPort = this.getUri().getPort();
377        int thatPort = uri.getPort();
378        if (thatPort == -1) { // -1 => defaultPort of Uri scheme
379          thatPort = this.getUriDefaultPort();
380        }
381        if (thisPort != thatPort) {
382          throw new InvalidPathException("Wrong FS: " + path + ", expected: "
383              + this.getUri());
384        }
385      }
386      
387      /**
388       * Get the path-part of a pathname. Checks that URI matches this file system
389       * and that the path-part is a valid name.
390       * 
391       * @param p path
392       * 
393       * @return path-part of the Path p
394       */
395      public String getUriPath(final Path p) {
396        checkPath(p);
397        String s = p.toUri().getPath();
398        if (!isValidName(s)) {
399          throw new InvalidPathException("Path part " + s + " from URI " + p
400              + " is not a valid filename.");
401        }
402        return s;
403      }
404      
405      /**
406       * Make the path fully qualified to this file system
407       * @param path
408       * @return the qualified path
409       */
410      public Path makeQualified(Path path) {
411        checkPath(path);
412        return path.makeQualified(this.getUri(), null);
413      }
414      
415      /**
416       * Some file systems like LocalFileSystem have an initial workingDir
417       * that is used as the starting workingDir. For other file systems
418       * like HDFS there is no built in notion of an initial workingDir.
419       * 
420       * @return the initial workingDir if the file system has such a notion
421       *         otherwise return a null.
422       */
423      public Path getInitialWorkingDirectory() {
424        return null;
425      }
426      
427      /** 
428       * Return the current user's home directory in this file system.
429       * The default implementation returns "/user/$USER/".
430       * 
431       * @return current user's home directory.
432       */
433      public Path getHomeDirectory() {
434        return new Path("/user/"+System.getProperty("user.name")).makeQualified(
435                                                                    getUri(), null);
436      }
437      
438      /**
439       * Return a set of server default configuration values.
440       * 
441       * @return server default configuration values
442       * 
443       * @throws IOException an I/O error occurred
444       */
445      public abstract FsServerDefaults getServerDefaults() throws IOException; 
446    
447      /**
448       * Return the fully-qualified path of path f resolving the path
449       * through any internal symlinks or mount point
450       * @param p path to be resolved
451       * @return fully qualified path 
452       * @throws FileNotFoundException, AccessControlException, IOException
453       *         UnresolvedLinkException if symbolic link on path cannot be resolved
454       *          internally
455       */
456       public Path resolvePath(final Path p) throws FileNotFoundException,
457               UnresolvedLinkException, AccessControlException, IOException {
458         checkPath(p);
459         return getFileStatus(p).getPath(); // default impl is to return the path
460       }
461      
462      /**
463       * The specification of this method matches that of
464       * {@link FileContext#create(Path, EnumSet, Options.CreateOpts...)} except
465       * that the Path f must be fully qualified and the permission is absolute
466       * (i.e. umask has been applied).
467       */
468      public final FSDataOutputStream create(final Path f,
469          final EnumSet<CreateFlag> createFlag, Options.CreateOpts... opts)
470          throws AccessControlException, FileAlreadyExistsException,
471          FileNotFoundException, ParentNotDirectoryException,
472          UnsupportedFileSystemException, UnresolvedLinkException, IOException {
473        checkPath(f);
474        int bufferSize = -1;
475        short replication = -1;
476        long blockSize = -1;
477        int bytesPerChecksum = -1;
478        ChecksumOpt checksumOpt = null;
479        FsPermission permission = null;
480        Progressable progress = null;
481        Boolean createParent = null;
482     
483        for (CreateOpts iOpt : opts) {
484          if (CreateOpts.BlockSize.class.isInstance(iOpt)) {
485            if (blockSize != -1) {
486              throw new HadoopIllegalArgumentException(
487                  "BlockSize option is set multiple times");
488            }
489            blockSize = ((CreateOpts.BlockSize) iOpt).getValue();
490          } else if (CreateOpts.BufferSize.class.isInstance(iOpt)) {
491            if (bufferSize != -1) {
492              throw new HadoopIllegalArgumentException(
493                  "BufferSize option is set multiple times");
494            }
495            bufferSize = ((CreateOpts.BufferSize) iOpt).getValue();
496          } else if (CreateOpts.ReplicationFactor.class.isInstance(iOpt)) {
497            if (replication != -1) {
498              throw new HadoopIllegalArgumentException(
499                  "ReplicationFactor option is set multiple times");
500            }
501            replication = ((CreateOpts.ReplicationFactor) iOpt).getValue();
502          } else if (CreateOpts.BytesPerChecksum.class.isInstance(iOpt)) {
503            if (bytesPerChecksum != -1) {
504              throw new HadoopIllegalArgumentException(
505                  "BytesPerChecksum option is set multiple times");
506            }
507            bytesPerChecksum = ((CreateOpts.BytesPerChecksum) iOpt).getValue();
508          } else if (CreateOpts.ChecksumParam.class.isInstance(iOpt)) {
509            if (checksumOpt != null) {
510              throw new  HadoopIllegalArgumentException(
511                  "CreateChecksumType option is set multiple times");
512            }
513            checksumOpt = ((CreateOpts.ChecksumParam) iOpt).getValue();
514          } else if (CreateOpts.Perms.class.isInstance(iOpt)) {
515            if (permission != null) {
516              throw new HadoopIllegalArgumentException(
517                  "Perms option is set multiple times");
518            }
519            permission = ((CreateOpts.Perms) iOpt).getValue();
520          } else if (CreateOpts.Progress.class.isInstance(iOpt)) {
521            if (progress != null) {
522              throw new HadoopIllegalArgumentException(
523                  "Progress option is set multiple times");
524            }
525            progress = ((CreateOpts.Progress) iOpt).getValue();
526          } else if (CreateOpts.CreateParent.class.isInstance(iOpt)) {
527            if (createParent != null) {
528              throw new HadoopIllegalArgumentException(
529                  "CreateParent option is set multiple times");
530            }
531            createParent = ((CreateOpts.CreateParent) iOpt).getValue();
532          } else {
533            throw new HadoopIllegalArgumentException("Unkown CreateOpts of type " +
534                iOpt.getClass().getName());
535          }
536        }
537        if (permission == null) {
538          throw new HadoopIllegalArgumentException("no permission supplied");
539        }
540    
541    
542        FsServerDefaults ssDef = getServerDefaults();
543        if (ssDef.getBlockSize() % ssDef.getBytesPerChecksum() != 0) {
544          throw new IOException("Internal error: default blockSize is" + 
545              " not a multiple of default bytesPerChecksum ");
546        }
547        
548        if (blockSize == -1) {
549          blockSize = ssDef.getBlockSize();
550        }
551    
552        // Create a checksum option honoring user input as much as possible.
553        // If bytesPerChecksum is specified, it will override the one set in
554        // checksumOpt. Any missing value will be filled in using the default.
555        ChecksumOpt defaultOpt = new ChecksumOpt(
556            ssDef.getChecksumType(),
557            ssDef.getBytesPerChecksum());
558        checksumOpt = ChecksumOpt.processChecksumOpt(defaultOpt,
559            checksumOpt, bytesPerChecksum);
560    
561        if (bufferSize == -1) {
562          bufferSize = ssDef.getFileBufferSize();
563        }
564        if (replication == -1) {
565          replication = ssDef.getReplication();
566        }
567        if (createParent == null) {
568          createParent = false;
569        }
570    
571        if (blockSize % bytesPerChecksum != 0) {
572          throw new HadoopIllegalArgumentException(
573                 "blockSize should be a multiple of checksumsize");
574        }
575    
576        return this.createInternal(f, createFlag, permission, bufferSize,
577          replication, blockSize, progress, checksumOpt, createParent);
578      }
579    
580      /**
581       * The specification of this method matches that of
582       * {@link #create(Path, EnumSet, Options.CreateOpts...)} except that the opts
583       * have been declared explicitly.
584       */
585      public abstract FSDataOutputStream createInternal(Path f,
586          EnumSet<CreateFlag> flag, FsPermission absolutePermission,
587          int bufferSize, short replication, long blockSize, Progressable progress,
588          ChecksumOpt checksumOpt, boolean createParent)
589          throws AccessControlException, FileAlreadyExistsException,
590          FileNotFoundException, ParentNotDirectoryException,
591          UnsupportedFileSystemException, UnresolvedLinkException, IOException;
592    
593      /**
594       * The specification of this method matches that of
595       * {@link FileContext#mkdir(Path, FsPermission, boolean)} except that the Path
596       * f must be fully qualified and the permission is absolute (i.e. 
597       * umask has been applied).
598       */
599      public abstract void mkdir(final Path dir, final FsPermission permission,
600          final boolean createParent) throws AccessControlException,
601          FileAlreadyExistsException, FileNotFoundException,
602          UnresolvedLinkException, IOException;
603    
604      /**
605       * The specification of this method matches that of
606       * {@link FileContext#delete(Path, boolean)} except that Path f must be for
607       * this file system.
608       */
609      public abstract boolean delete(final Path f, final boolean recursive)
610          throws AccessControlException, FileNotFoundException,
611          UnresolvedLinkException, IOException;
612    
613      /**
614       * The specification of this method matches that of
615       * {@link FileContext#open(Path)} except that Path f must be for this
616       * file system.
617       */
618      public FSDataInputStream open(final Path f) throws AccessControlException,
619          FileNotFoundException, UnresolvedLinkException, IOException {
620        return open(f, getServerDefaults().getFileBufferSize());
621      }
622    
623      /**
624       * The specification of this method matches that of
625       * {@link FileContext#open(Path, int)} except that Path f must be for this
626       * file system.
627       */
628      public abstract FSDataInputStream open(final Path f, int bufferSize)
629          throws AccessControlException, FileNotFoundException,
630          UnresolvedLinkException, IOException;
631    
632      /**
633       * The specification of this method matches that of
634       * {@link FileContext#setReplication(Path, short)} except that Path f must be
635       * for this file system.
636       */
637      public abstract boolean setReplication(final Path f,
638          final short replication) throws AccessControlException,
639          FileNotFoundException, UnresolvedLinkException, IOException;
640    
641      /**
642       * The specification of this method matches that of
643       * {@link FileContext#rename(Path, Path, Options.Rename...)} except that Path
644       * f must be for this file system.
645       */
646      public final void rename(final Path src, final Path dst,
647          final Options.Rename... options) throws AccessControlException,
648          FileAlreadyExistsException, FileNotFoundException,
649          ParentNotDirectoryException, UnresolvedLinkException, IOException {
650        boolean overwrite = false;
651        if (null != options) {
652          for (Rename option : options) {
653            if (option == Rename.OVERWRITE) {
654              overwrite = true;
655            }
656          }
657        }
658        renameInternal(src, dst, overwrite);
659      }
660      
661      /**
662       * The specification of this method matches that of
663       * {@link FileContext#rename(Path, Path, Options.Rename...)} except that Path
664       * f must be for this file system and NO OVERWRITE is performed.
665       * 
666       * File systems that do not have a built in overwrite need implement only this
667       * method and can take advantage of the default impl of the other
668       * {@link #renameInternal(Path, Path, boolean)}
669       */
670      public abstract void renameInternal(final Path src, final Path dst)
671          throws AccessControlException, FileAlreadyExistsException,
672          FileNotFoundException, ParentNotDirectoryException,
673          UnresolvedLinkException, IOException;
674      
675      /**
676       * The specification of this method matches that of
677       * {@link FileContext#rename(Path, Path, Options.Rename...)} except that Path
678       * f must be for this file system.
679       */
680      public void renameInternal(final Path src, final Path dst,
681          boolean overwrite) throws AccessControlException,
682          FileAlreadyExistsException, FileNotFoundException,
683          ParentNotDirectoryException, UnresolvedLinkException, IOException {
684        // Default implementation deals with overwrite in a non-atomic way
685        final FileStatus srcStatus = getFileLinkStatus(src);
686    
687        FileStatus dstStatus;
688        try {
689          dstStatus = getFileLinkStatus(dst);
690        } catch (IOException e) {
691          dstStatus = null;
692        }
693        if (dstStatus != null) {
694          if (dst.equals(src)) {
695            throw new FileAlreadyExistsException(
696                "The source "+src+" and destination "+dst+" are the same");
697          }
698          if (srcStatus.isSymlink() && dst.equals(srcStatus.getSymlink())) {
699            throw new FileAlreadyExistsException(
700                "Cannot rename symlink "+src+" to its target "+dst);
701          }
702          // It's OK to rename a file to a symlink and vice versa
703          if (srcStatus.isDirectory() != dstStatus.isDirectory()) {
704            throw new IOException("Source " + src + " and destination " + dst
705                + " must both be directories");
706          }
707          if (!overwrite) {
708            throw new FileAlreadyExistsException("Rename destination " + dst
709                + " already exists.");
710          }
711          // Delete the destination that is a file or an empty directory
712          if (dstStatus.isDirectory()) {
713            RemoteIterator<FileStatus> list = listStatusIterator(dst);
714            if (list != null && list.hasNext()) {
715              throw new IOException(
716                  "Rename cannot overwrite non empty destination directory " + dst);
717            }
718          }
719          delete(dst, false);
720        } else {
721          final Path parent = dst.getParent();
722          final FileStatus parentStatus = getFileStatus(parent);
723          if (parentStatus.isFile()) {
724            throw new ParentNotDirectoryException("Rename destination parent "
725                + parent + " is a file.");
726          }
727        }
728        renameInternal(src, dst);
729      }
730      
731      /**
732       * Returns true if the file system supports symlinks, false otherwise.
733       * @return true if filesystem supports symlinks
734       */
735      public boolean supportsSymlinks() {
736        return false;
737      }
738      
739      /**
740       * The specification of this method matches that of  
741       * {@link FileContext#createSymlink(Path, Path, boolean)};
742       */
743      public void createSymlink(final Path target, final Path link,
744          final boolean createParent) throws IOException, UnresolvedLinkException {
745        throw new IOException("File system does not support symlinks");    
746      }
747    
748      /**
749       * Partially resolves the path. This is used during symlink resolution in
750       * {@link FSLinkResolver}, and differs from the similarly named method
751       * {@link FileContext#getLinkTarget(Path)}.
752       * @throws IOException subclass implementations may throw IOException 
753       */
754      public Path getLinkTarget(final Path f) throws IOException {
755        throw new AssertionError("Implementation Error: " + getClass()
756            + " that threw an UnresolvedLinkException, causing this method to be"
757            + " called, needs to override this method.");
758      }
759        
760      /**
761       * The specification of this method matches that of
762       * {@link FileContext#setPermission(Path, FsPermission)} except that Path f
763       * must be for this file system.
764       */
765      public abstract void setPermission(final Path f,
766          final FsPermission permission) throws AccessControlException,
767          FileNotFoundException, UnresolvedLinkException, IOException;
768    
769      /**
770       * The specification of this method matches that of
771       * {@link FileContext#setOwner(Path, String, String)} except that Path f must
772       * be for this file system.
773       */
774      public abstract void setOwner(final Path f, final String username,
775          final String groupname) throws AccessControlException,
776          FileNotFoundException, UnresolvedLinkException, IOException;
777    
778      /**
779       * The specification of this method matches that of
780       * {@link FileContext#setTimes(Path, long, long)} except that Path f must be
781       * for this file system.
782       */
783      public abstract void setTimes(final Path f, final long mtime,
784        final long atime) throws AccessControlException, FileNotFoundException,
785          UnresolvedLinkException, IOException;
786    
787      /**
788       * The specification of this method matches that of
789       * {@link FileContext#getFileChecksum(Path)} except that Path f must be for
790       * this file system.
791       */
792      public abstract FileChecksum getFileChecksum(final Path f)
793          throws AccessControlException, FileNotFoundException,
794          UnresolvedLinkException, IOException;
795      
796      /**
797       * The specification of this method matches that of
798       * {@link FileContext#getFileStatus(Path)} 
799       * except that an UnresolvedLinkException may be thrown if a symlink is 
800       * encountered in the path.
801       */
802      public abstract FileStatus getFileStatus(final Path f)
803          throws AccessControlException, FileNotFoundException,
804          UnresolvedLinkException, IOException;
805    
806      /**
807       * The specification of this method matches that of
808       * {@link FileContext#getFileLinkStatus(Path)}
809       * except that an UnresolvedLinkException may be thrown if a symlink is  
810       * encountered in the path leading up to the final path component.
811       * If the file system does not support symlinks then the behavior is
812       * equivalent to {@link AbstractFileSystem#getFileStatus(Path)}.
813       */
814      public FileStatus getFileLinkStatus(final Path f)
815          throws AccessControlException, FileNotFoundException,
816          UnsupportedFileSystemException, IOException {
817        return getFileStatus(f);
818      }
819    
820      /**
821       * The specification of this method matches that of
822       * {@link FileContext#getFileBlockLocations(Path, long, long)} except that
823       * Path f must be for this file system.
824       */
825      public abstract BlockLocation[] getFileBlockLocations(final Path f,
826          final long start, final long len) throws AccessControlException,
827          FileNotFoundException, UnresolvedLinkException, IOException;
828    
829      /**
830       * The specification of this method matches that of
831       * {@link FileContext#getFsStatus(Path)} except that Path f must be for this
832       * file system.
833       */
834      public FsStatus getFsStatus(final Path f) throws AccessControlException,
835          FileNotFoundException, UnresolvedLinkException, IOException {
836        // default impl gets FsStatus of root
837        return getFsStatus();
838      }
839      
840      /**
841       * The specification of this method matches that of
842       * {@link FileContext#getFsStatus(Path)}.
843       */
844      public abstract FsStatus getFsStatus() throws AccessControlException,
845          FileNotFoundException, IOException;
846    
847      /**
848       * The specification of this method matches that of
849       * {@link FileContext#listStatus(Path)} except that Path f must be for this
850       * file system.
851       */
852      public RemoteIterator<FileStatus> listStatusIterator(final Path f)
853          throws AccessControlException, FileNotFoundException,
854          UnresolvedLinkException, IOException {
855        return new RemoteIterator<FileStatus>() {
856          private int i = 0;
857          private FileStatus[] statusList = listStatus(f);
858          
859          @Override
860          public boolean hasNext() {
861            return i < statusList.length;
862          }
863          
864          @Override
865          public FileStatus next() {
866            if (!hasNext()) {
867              throw new NoSuchElementException();
868            }
869            return statusList[i++];
870          }
871        };
872      }
873    
874      /**
875       * The specification of this method matches that of
876       * {@link FileContext#listLocatedStatus(Path)} except that Path f 
877       * must be for this file system.
878       */
879      public RemoteIterator<LocatedFileStatus> listLocatedStatus(final Path f)
880          throws AccessControlException, FileNotFoundException,
881          UnresolvedLinkException, IOException {
882        return new RemoteIterator<LocatedFileStatus>() {
883          private RemoteIterator<FileStatus> itor = listStatusIterator(f);
884          
885          @Override
886          public boolean hasNext() throws IOException {
887            return itor.hasNext();
888          }
889          
890          @Override
891          public LocatedFileStatus next() throws IOException {
892            if (!hasNext()) {
893              throw new NoSuchElementException("No more entry in " + f);
894            }
895            FileStatus result = itor.next();
896            BlockLocation[] locs = null;
897            if (result.isFile()) {
898              locs = getFileBlockLocations(
899                  result.getPath(), 0, result.getLen());
900            }
901            return new LocatedFileStatus(result, locs);
902          }
903        };
904      }
905    
906      /**
907       * The specification of this method matches that of
908       * {@link FileContext.Util#listStatus(Path)} except that Path f must be 
909       * for this file system.
910       */
911      public abstract FileStatus[] listStatus(final Path f)
912          throws AccessControlException, FileNotFoundException,
913          UnresolvedLinkException, IOException;
914    
915      /**
916       * @return an iterator over the corrupt files under the given path
917       * (may contain duplicates if a file has more than one corrupt block)
918       * @throws IOException
919       */
920      public RemoteIterator<Path> listCorruptFileBlocks(Path path)
921        throws IOException {
922        throw new UnsupportedOperationException(getClass().getCanonicalName() +
923                                                " does not support" +
924                                                " listCorruptFileBlocks");
925      }
926    
927      /**
928       * The specification of this method matches that of
929       * {@link FileContext#setVerifyChecksum(boolean, Path)} except that Path f
930       * must be for this file system.
931       */
932      public abstract void setVerifyChecksum(final boolean verifyChecksum)
933          throws AccessControlException, IOException;
934      
935      /**
936       * Get a canonical name for this file system.
937       * @return a URI string that uniquely identifies this file system
938       */
939      public String getCanonicalServiceName() {
940        return SecurityUtil.buildDTServiceName(getUri(), getUriDefaultPort());
941      }
942      
943      /**
944       * Get one or more delegation tokens associated with the filesystem. Normally
945       * a file system returns a single delegation token. A file system that manages
946       * multiple file systems underneath, could return set of delegation tokens for
947       * all the file systems it manages
948       * 
949       * @param renewer the account name that is allowed to renew the token.
950       * @return List of delegation tokens.
951       *   If delegation tokens not supported then return a list of size zero.
952       * @throws IOException
953       */
954      @InterfaceAudience.LimitedPrivate( { "HDFS", "MapReduce" })
955      public List<Token<?>> getDelegationTokens(String renewer) throws IOException {
956        return new ArrayList<Token<?>>(0);
957      }
958    
959      /**
960       * Modifies ACL entries of files and directories.  This method can add new ACL
961       * entries or modify the permissions on existing ACL entries.  All existing
962       * ACL entries that are not specified in this call are retained without
963       * changes.  (Modifications are merged into the current ACL.)
964       *
965       * @param path Path to modify
966       * @param aclSpec List<AclEntry> describing modifications
967       * @throws IOException if an ACL could not be modified
968       */
969      public void modifyAclEntries(Path path, List<AclEntry> aclSpec)
970          throws IOException {
971        throw new UnsupportedOperationException(getClass().getSimpleName()
972            + " doesn't support modifyAclEntries");
973      }
974    
975      /**
976       * Removes ACL entries from files and directories.  Other ACL entries are
977       * retained.
978       *
979       * @param path Path to modify
980       * @param aclSpec List<AclEntry> describing entries to remove
981       * @throws IOException if an ACL could not be modified
982       */
983      public void removeAclEntries(Path path, List<AclEntry> aclSpec)
984          throws IOException {
985        throw new UnsupportedOperationException(getClass().getSimpleName()
986            + " doesn't support removeAclEntries");
987      }
988    
989      /**
990       * Removes all default ACL entries from files and directories.
991       *
992       * @param path Path to modify
993       * @throws IOException if an ACL could not be modified
994       */
995      public void removeDefaultAcl(Path path)
996          throws IOException {
997        throw new UnsupportedOperationException(getClass().getSimpleName()
998            + " doesn't support removeDefaultAcl");
999      }
1000    
1001      /**
1002       * Removes all but the base ACL entries of files and directories.  The entries
1003       * for user, group, and others are retained for compatibility with permission
1004       * bits.
1005       *
1006       * @param path Path to modify
1007       * @throws IOException if an ACL could not be removed
1008       */
1009      public void removeAcl(Path path)
1010          throws IOException {
1011        throw new UnsupportedOperationException(getClass().getSimpleName()
1012            + " doesn't support removeAcl");
1013      }
1014    
1015      /**
1016       * Fully replaces ACL of files and directories, discarding all existing
1017       * entries.
1018       *
1019       * @param path Path to modify
1020       * @param aclSpec List<AclEntry> describing modifications, must include entries
1021       *   for user, group, and others for compatibility with permission bits.
1022       * @throws IOException if an ACL could not be modified
1023       */
1024      public void setAcl(Path path, List<AclEntry> aclSpec) throws IOException {
1025        throw new UnsupportedOperationException(getClass().getSimpleName()
1026            + " doesn't support setAcl");
1027      }
1028    
1029      /**
1030       * Gets the ACLs of files and directories.
1031       *
1032       * @param path Path to get
1033       * @return RemoteIterator<AclStatus> which returns each AclStatus
1034       * @throws IOException if an ACL could not be read
1035       */
1036      public AclStatus getAclStatus(Path path) throws IOException {
1037        throw new UnsupportedOperationException(getClass().getSimpleName()
1038            + " doesn't support getAclStatus");
1039      }
1040    
1041      /**
1042       * Set an xattr of a file or directory.
1043       * The name must be prefixed with user/trusted/security/system and
1044       * followed by ".". For example, "user.attr".
1045       * <p/>
1046       * A regular user can only set an xattr for the "user" namespace.
1047       * The super user can set an xattr of either the "user" or "trusted" namespaces.
1048       * The xattrs of the "security" and "system" namespaces are only used/exposed 
1049       * internally by/to the FS impl.
1050       * <p/>
1051       * The access permissions of an xattr in the "user" namespace are
1052       * defined by the file and directory permission bits.
1053       * An xattr can only be set when the logged-in user has the correct permissions.
1054       * If the xattr exists, it will be replaced.
1055       * <p/>
1056       * @see <a href="http://en.wikipedia.org/wiki/Extended_file_attributes">
1057       * http://en.wikipedia.org/wiki/Extended_file_attributes</a>
1058       *
1059       * @param path Path to modify
1060       * @param name xattr name.
1061       * @param value xattr value.
1062       * @throws IOException
1063       */
1064      public void setXAttr(Path path, String name, byte[] value)
1065          throws IOException {
1066        setXAttr(path, name, value, EnumSet.of(XAttrSetFlag.CREATE,
1067            XAttrSetFlag.REPLACE));
1068      }
1069    
1070      /**
1071       * Set an xattr of a file or directory.
1072       * The name must be prefixed with user/trusted/security/system and
1073       * followed by ".". For example, "user.attr".
1074       * <p/>
1075       * A regular user can only set an xattr for the "user" namespace.
1076       * The super user can set an xattr of either the "user" or "trusted" namespaces.
1077       * The xattrs of the "security" and "system" namespaces are only used/exposed 
1078       * internally by/to the FS impl.
1079       * <p/>
1080       * The access permissions of an xattr in the "user" namespace are
1081       * defined by the file and directory permission bits.
1082       * An xattr can only be set when the logged-in user has the correct permissions.
1083       * If the xattr exists, it will be replaced.
1084       * <p/>
1085       * @see <a href="http://en.wikipedia.org/wiki/Extended_file_attributes">
1086       * http://en.wikipedia.org/wiki/Extended_file_attributes</a>
1087       *
1088       * @param path Path to modify
1089       * @param name xattr name.
1090       * @param value xattr value.
1091       * @param flag xattr set flag
1092       * @throws IOException
1093       */
1094      public void setXAttr(Path path, String name, byte[] value,
1095          EnumSet<XAttrSetFlag> flag) throws IOException {
1096        throw new UnsupportedOperationException(getClass().getSimpleName()
1097            + " doesn't support setXAttr");
1098      }
1099    
1100      /**
1101       * Get an xattr for a file or directory.
1102       * The name must be prefixed with user/trusted/security/system and
1103       * followed by ".". For example, "user.attr".
1104       * <p/>
1105       * A regular user can only get an xattr for the "user" namespace.
1106       * The super user can get an xattr of either the "user" or "trusted" namespaces.
1107       * The xattrs of the "security" and "system" namespaces are only used/exposed 
1108       * internally by/to the FS impl.
1109       * <p/>
1110       * An xattr will only be returned when the logged-in user has the correct permissions.
1111       * <p/>
1112       * @see <a href="http://en.wikipedia.org/wiki/Extended_file_attributes">
1113       * http://en.wikipedia.org/wiki/Extended_file_attributes</a>
1114       *
1115       * @param path Path to get extended attribute
1116       * @param name xattr name.
1117       * @return byte[] xattr value.
1118       * @throws IOException
1119       */
1120      public byte[] getXAttr(Path path, String name) throws IOException {
1121        throw new UnsupportedOperationException(getClass().getSimpleName()
1122            + " doesn't support getXAttr");
1123      }
1124    
1125      /**
1126       * Get all of the xattrs for a file or directory.
1127       * Only those xattrs for which the logged-in user has permissions to view
1128       * are returned.
1129       * <p/>
1130       * A regular user can only get xattrs for the "user" namespace.
1131       * The super user can only get xattrs for "user" and "trusted" namespaces.
1132       * The xattr of "security" and "system" namespaces are only used/exposed 
1133       * internally by/to the FS impl.
1134       * <p/>
1135       * @see <a href="http://en.wikipedia.org/wiki/Extended_file_attributes">
1136       * http://en.wikipedia.org/wiki/Extended_file_attributes</a>
1137       *
1138       * @param path Path to get extended attributes
1139       * @return Map<String, byte[]> describing the XAttrs of the file or directory
1140       * @throws IOException
1141       */
1142      public Map<String, byte[]> getXAttrs(Path path) throws IOException {
1143        throw new UnsupportedOperationException(getClass().getSimpleName()
1144            + " doesn't support getXAttrs");
1145      }
1146    
1147      /**
1148       * Get all of the xattrs for a file or directory.
1149       * Only those xattrs for which the logged-in user has permissions to view
1150       * are returned.
1151       * <p/>
1152       * A regular user can only get xattrs for the "user" namespace.
1153       * The super user can only get xattrs for "user" and "trusted" namespaces.
1154       * The xattr of "security" and "system" namespaces are only used/exposed 
1155       * internally by/to the FS impl.
1156       * <p/>
1157       * @see <a href="http://en.wikipedia.org/wiki/Extended_file_attributes">
1158       * http://en.wikipedia.org/wiki/Extended_file_attributes</a>
1159       *
1160       * @param path Path to get extended attributes
1161       * @param names XAttr names.
1162       * @return Map<String, byte[]> describing the XAttrs of the file or directory
1163       * @throws IOException
1164       */
1165      public Map<String, byte[]> getXAttrs(Path path, List<String> names)
1166          throws IOException {
1167        throw new UnsupportedOperationException(getClass().getSimpleName()
1168            + " doesn't support getXAttrs");
1169      }
1170    
1171      /**
1172       * Get all of the xattr names for a file or directory.
1173       * Only the xattr names for which the logged-in user has permissions to view
1174       * are returned.
1175       * <p/>
1176       * A regular user can only get xattr names for the "user" namespace.
1177       * The super user can only get xattr names for the "user" and "trusted"
1178       * namespaces.
1179       * The xattr names in the "security" and "system" namespaces are only
1180       * used/exposed internally by/to the FS impl.
1181       * <p/>
1182       * @see <a href="http://en.wikipedia.org/wiki/Extended_file_attributes">
1183       * http://en.wikipedia.org/wiki/Extended_file_attributes</a>
1184       *
1185       * @param path Path to get extended attributes
1186       * @return Map<String, byte[]> describing the XAttrs of the file or directory
1187       * @throws IOException
1188       */
1189      public List<String> listXAttrs(Path path)
1190              throws IOException {
1191        throw new UnsupportedOperationException(getClass().getSimpleName()
1192                + " doesn't support listXAttrs");
1193      }
1194    
1195      /**
1196       * Remove an xattr of a file or directory.
1197       * The name must be prefixed with user/trusted/security/system and
1198       * followed by ".". For example, "user.attr".
1199       * <p/>
1200       * A regular user can only remove an xattr for the "user" namespace.
1201       * The super user can remove an xattr of either the "user" or "trusted" namespaces.
1202       * The xattrs of the "security" and "system" namespaces are only used/exposed 
1203       * internally by/to the FS impl.
1204       * <p/>
1205       * The access permissions of an xattr in the "user" namespace are
1206       * defined by the file and directory permission bits.
1207       * An xattr can only be set when the logged-in user has the correct permissions.
1208       * If the xattr exists, it will be replaced.
1209       * <p/>
1210       * @see <a href="http://en.wikipedia.org/wiki/Extended_file_attributes">
1211       * http://en.wikipedia.org/wiki/Extended_file_attributes</a>
1212       *
1213       * @param path Path to remove extended attribute
1214       * @param name xattr name
1215       * @throws IOException
1216       */
1217      public void removeXAttr(Path path, String name) throws IOException {
1218        throw new UnsupportedOperationException(getClass().getSimpleName()
1219            + " doesn't support removeXAttr");
1220      }
1221    
1222      @Override //Object
1223      public int hashCode() {
1224        return myUri.hashCode();
1225      }
1226      
1227      @Override //Object
1228      public boolean equals(Object other) {
1229        if (other == null || !(other instanceof AbstractFileSystem)) {
1230          return false;
1231        }
1232        return myUri.equals(((AbstractFileSystem) other).myUri);
1233      }
1234    }