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.viewfs;
019    
020    import static org.apache.hadoop.fs.viewfs.Constants.PERMISSION_RRR;
021    
022    import java.io.FileNotFoundException;
023    import java.io.IOException;
024    import java.net.URI;
025    import java.net.URISyntaxException;
026    import java.util.ArrayList;
027    import java.util.HashSet;
028    import java.util.List;
029    import java.util.Set;
030    import java.util.StringTokenizer;
031    import java.util.Map.Entry;
032    
033    import org.apache.hadoop.classification.InterfaceAudience;
034    import org.apache.hadoop.classification.InterfaceStability;
035    import org.apache.hadoop.conf.Configuration;
036    import org.apache.hadoop.fs.BlockLocation;
037    import org.apache.hadoop.fs.ContentSummary;
038    import org.apache.hadoop.fs.FSDataInputStream;
039    import org.apache.hadoop.fs.FSDataOutputStream;
040    import org.apache.hadoop.fs.FileAlreadyExistsException;
041    import org.apache.hadoop.fs.FileChecksum;
042    import org.apache.hadoop.fs.FileStatus;
043    import org.apache.hadoop.fs.FileSystem;
044    import org.apache.hadoop.fs.FsConstants;
045    import org.apache.hadoop.fs.FsServerDefaults;
046    import org.apache.hadoop.fs.InvalidPathException;
047    import org.apache.hadoop.fs.Path;
048    import org.apache.hadoop.fs.UnsupportedFileSystemException;
049    import org.apache.hadoop.fs.permission.FsPermission;
050    import org.apache.hadoop.fs.viewfs.InodeTree.INode;
051    import org.apache.hadoop.fs.viewfs.InodeTree.INodeLink;
052    import org.apache.hadoop.io.Text;
053    import org.apache.hadoop.security.AccessControlException;
054    import org.apache.hadoop.security.Credentials;
055    import org.apache.hadoop.security.UserGroupInformation;
056    import org.apache.hadoop.security.token.Token;
057    import org.apache.hadoop.util.Progressable;
058    import org.apache.hadoop.util.Time;
059    
060    /**
061     * ViewFileSystem (extends the FileSystem interface) implements a client-side
062     * mount table. Its spec and implementation is identical to {@link ViewFs}.
063     */
064    
065    @InterfaceAudience.Public
066    @InterfaceStability.Evolving /*Evolving for a release,to be changed to Stable */
067    public class ViewFileSystem extends FileSystem {
068      static AccessControlException readOnlyMountTable(final String operation,
069          final String p) {
070        return new AccessControlException( 
071            "InternalDir of ViewFileSystem is readonly; operation=" + operation + 
072            "Path=" + p);
073      }
074      static AccessControlException readOnlyMountTable(final String operation,
075          final Path p) {
076        return readOnlyMountTable(operation, p.toString());
077      }
078      
079      static public class MountPoint {
080        private Path src;       // the src of the mount
081        private URI[] targets; //  target of the mount; Multiple targets imply mergeMount
082        MountPoint(Path srcPath, URI[] targetURIs) {
083          src = srcPath;
084          targets = targetURIs;
085        }
086        Path getSrc() {
087          return src;
088        }
089        URI[] getTargets() {
090          return targets;
091        }
092      }
093      
094      final long creationTime; // of the the mount table
095      final UserGroupInformation ugi; // the user/group of user who created mtable
096      URI myUri;
097      private Path workingDir;
098      Configuration config;
099      InodeTree<FileSystem> fsState;  // the fs state; ie the mount table
100      Path homeDir = null;
101      
102      /**
103       * Prohibits names which contain a ".", "..", ":" or "/" 
104       */
105      private static boolean isValidName(final String src) {
106        // Check for ".." "." ":" "/"
107        final StringTokenizer tokens = new StringTokenizer(src, Path.SEPARATOR);
108        while(tokens.hasMoreTokens()) {
109          String element = tokens.nextToken();
110          if (element.equals("..") ||
111              element.equals(".")  ||
112              (element.indexOf(":") >= 0)) {
113            return false;
114          }
115        }
116        return true;
117      }
118      
119      /**
120       * Make the path Absolute and get the path-part of a pathname.
121       * Checks that URI matches this file system 
122       * and that the path-part is a valid name.
123       * 
124       * @param p path
125       * @return path-part of the Path p
126       */
127      private String getUriPath(final Path p) {
128        checkPath(p);
129        String s = makeAbsolute(p).toUri().getPath();
130        if (!isValidName(s)) {
131          throw new InvalidPathException("Path part " + s + " from URI" + p
132              + " is not a valid filename.");
133        }
134        return s;
135      }
136      
137      private Path makeAbsolute(final Path f) {
138        return f.isAbsolute() ? f : new Path(workingDir, f);
139      }
140      
141      /**
142       * This is the  constructor with the signature needed by
143       * {@link FileSystem#createFileSystem(URI, Configuration)}
144       * 
145       * After this constructor is called initialize() is called.
146       * @throws IOException 
147       */
148      public ViewFileSystem() throws IOException {
149        ugi = UserGroupInformation.getCurrentUser();
150        creationTime = Time.now();
151      }
152    
153      /**
154       * Return the protocol scheme for the FileSystem.
155       * <p/>
156       *
157       * @return <code>viewfs</code>
158       */
159      @Override
160      public String getScheme() {
161        return "viewfs";
162      }
163    
164      /**
165       * Called after a new FileSystem instance is constructed.
166       * @param theUri a uri whose authority section names the host, port, etc. for
167       *          this FileSystem
168       * @param conf the configuration
169       */
170      public void initialize(final URI theUri, final Configuration conf)
171          throws IOException {
172        super.initialize(theUri, conf);
173        setConf(conf);
174        config = conf;
175        // Now build  client side view (i.e. client side mount table) from config.
176        final String authority = theUri.getAuthority();
177        try {
178          myUri = new URI(FsConstants.VIEWFS_SCHEME, authority, "/", null, null);
179          fsState = new InodeTree<FileSystem>(conf, authority) {
180    
181            @Override
182            protected
183            FileSystem getTargetFileSystem(final URI uri)
184              throws URISyntaxException, IOException {
185                return new ChRootedFileSystem(uri, config);
186            }
187    
188            @Override
189            protected
190            FileSystem getTargetFileSystem(final INodeDir<FileSystem> dir)
191              throws URISyntaxException {
192              return new InternalDirOfViewFs(dir, creationTime, ugi, myUri);
193            }
194    
195            @Override
196            protected
197            FileSystem getTargetFileSystem(URI[] mergeFsURIList)
198                throws URISyntaxException, UnsupportedFileSystemException {
199              throw new UnsupportedFileSystemException("mergefs not implemented");
200              // return MergeFs.createMergeFs(mergeFsURIList, config);
201            }
202          };
203          workingDir = this.getHomeDirectory();
204        } catch (URISyntaxException e) {
205          throw new IOException("URISyntax exception: " + theUri);
206        }
207    
208      }
209      
210      
211      /**
212       * Convenience Constructor for apps to call directly
213       * @param theUri which must be that of ViewFileSystem
214       * @param conf
215       * @throws IOException
216       */
217      ViewFileSystem(final URI theUri, final Configuration conf)
218        throws IOException {
219        this();
220        initialize(theUri, conf);
221      }
222      
223      /**
224       * Convenience Constructor for apps to call directly
225       * @param conf
226       * @throws IOException
227       */
228      public ViewFileSystem(final Configuration conf) throws IOException {
229        this(FsConstants.VIEWFS_URI, conf);
230      }
231      
232      public Path getTrashCanLocation(final Path f) throws FileNotFoundException {
233        final InodeTree.ResolveResult<FileSystem> res = 
234          fsState.resolve(getUriPath(f), true);
235        return res.isInternalDir() ? null : res.targetFileSystem.getHomeDirectory();
236      }
237      
238      @Override
239      public String getCanonicalServiceName() {
240        return null;
241      }
242    
243      @Override
244      public URI getUri() {
245        return myUri;
246      }
247      
248      @Override
249      public Path resolvePath(final Path f)
250          throws IOException {
251        final InodeTree.ResolveResult<FileSystem> res;
252          res = fsState.resolve(getUriPath(f), true);
253        if (res.isInternalDir()) {
254          return f;
255        }
256        return res.targetFileSystem.resolvePath(res.remainingPath);
257      }
258      
259      @Override
260      public Path getHomeDirectory() {
261        if (homeDir == null) {
262          String base = fsState.getHomeDirPrefixValue();
263          if (base == null) {
264            base = "/user";
265          }
266          homeDir = 
267            this.makeQualified(new Path(base + "/" + ugi.getShortUserName()));
268        }
269        return homeDir;
270      }
271      
272      @Override
273      public Path getWorkingDirectory() {
274        return workingDir;
275      }
276    
277      @Override
278      public void setWorkingDirectory(final Path new_dir) {
279        getUriPath(new_dir); // this validates the path
280        workingDir = makeAbsolute(new_dir);
281      }
282      
283      @Override
284      public FSDataOutputStream append(final Path f, final int bufferSize,
285          final Progressable progress) throws IOException {
286        InodeTree.ResolveResult<FileSystem> res = 
287          fsState.resolve(getUriPath(f), true);
288        return res.targetFileSystem.append(res.remainingPath, bufferSize, progress);
289      }
290      
291      @Override
292      public FSDataOutputStream create(final Path f, final FsPermission permission,
293          final boolean overwrite, final int bufferSize, final short replication,
294          final long blockSize, final Progressable progress) throws IOException {
295        InodeTree.ResolveResult<FileSystem> res;
296        try {
297          res = fsState.resolve(getUriPath(f), false);
298        } catch (FileNotFoundException e) {
299            throw readOnlyMountTable("create", f);
300        }
301        assert(res.remainingPath != null);
302        return res.targetFileSystem.create(res.remainingPath, permission,
303             overwrite, bufferSize, replication, blockSize, progress);
304      }
305    
306      
307      @Override
308      public boolean delete(final Path f, final boolean recursive)
309          throws AccessControlException, FileNotFoundException,
310          IOException {
311        InodeTree.ResolveResult<FileSystem> res = 
312          fsState.resolve(getUriPath(f), true);
313        // If internal dir or target is a mount link (ie remainingPath is Slash)
314        if (res.isInternalDir() || res.remainingPath == InodeTree.SlashPath) {
315          throw readOnlyMountTable("delete", f);
316        }
317        return res.targetFileSystem.delete(res.remainingPath, recursive);
318      }
319      
320      @Override
321      @SuppressWarnings("deprecation")
322      public boolean delete(final Path f)
323          throws AccessControlException, FileNotFoundException,
324          IOException {
325          return delete(f, true);
326      }
327      
328      @Override
329      public BlockLocation[] getFileBlockLocations(FileStatus fs, 
330          long start, long len) throws IOException {
331        final InodeTree.ResolveResult<FileSystem> res = 
332          fsState.resolve(getUriPath(fs.getPath()), true);
333        return res.targetFileSystem.getFileBlockLocations(
334              new ViewFsFileStatus(fs, res.remainingPath), start, len);
335      }
336    
337      @Override
338      public FileChecksum getFileChecksum(final Path f)
339          throws AccessControlException, FileNotFoundException,
340          IOException {
341        InodeTree.ResolveResult<FileSystem> res = 
342          fsState.resolve(getUriPath(f), true);
343        return res.targetFileSystem.getFileChecksum(res.remainingPath);
344      }
345    
346      @Override
347      public FileStatus getFileStatus(final Path f) throws AccessControlException,
348          FileNotFoundException, IOException {
349        InodeTree.ResolveResult<FileSystem> res = 
350          fsState.resolve(getUriPath(f), true);
351        
352        // FileStatus#getPath is a fully qualified path relative to the root of 
353        // target file system.
354        // We need to change it to viewfs URI - relative to root of mount table.
355        
356        // The implementors of RawLocalFileSystem were trying to be very smart.
357        // They implement FileStatus#getOwener lazily -- the object
358        // returned is really a RawLocalFileSystem that expect the
359        // FileStatus#getPath to be unchanged so that it can get owner when needed.
360        // Hence we need to interpose a new ViewFileSystemFileStatus that 
361        // works around.
362        FileStatus status =  res.targetFileSystem.getFileStatus(res.remainingPath);
363        return new ViewFsFileStatus(status, this.makeQualified(f));
364      }
365      
366      
367      @Override
368      public FileStatus[] listStatus(final Path f) throws AccessControlException,
369          FileNotFoundException, IOException {
370        InodeTree.ResolveResult<FileSystem> res =
371          fsState.resolve(getUriPath(f), true);
372        
373        FileStatus[] statusLst = res.targetFileSystem.listStatus(res.remainingPath);
374        if (!res.isInternalDir()) {
375          // We need to change the name in the FileStatus as described in
376          // {@link #getFileStatus }
377          ChRootedFileSystem targetFs;
378          targetFs = (ChRootedFileSystem) res.targetFileSystem;
379          int i = 0;
380          for (FileStatus status : statusLst) {
381              String suffix = targetFs.stripOutRoot(status.getPath());
382              statusLst[i++] = new ViewFsFileStatus(status, this.makeQualified(
383                  suffix.length() == 0 ? f : new Path(res.resolvedPath, suffix)));
384          }
385        }
386        return statusLst;
387      }
388    
389      @Override
390      public boolean mkdirs(final Path dir, final FsPermission permission)
391          throws IOException {
392        InodeTree.ResolveResult<FileSystem> res = 
393          fsState.resolve(getUriPath(dir), false);
394       return  res.targetFileSystem.mkdirs(res.remainingPath, permission);
395      }
396    
397      @Override
398      public FSDataInputStream open(final Path f, final int bufferSize)
399          throws AccessControlException, FileNotFoundException,
400          IOException {
401        InodeTree.ResolveResult<FileSystem> res = 
402            fsState.resolve(getUriPath(f), true);
403        return res.targetFileSystem.open(res.remainingPath, bufferSize);
404      }
405    
406      
407      @Override
408      public boolean rename(final Path src, final Path dst) throws IOException {
409        // passing resolveLastComponet as false to catch renaming a mount point to 
410        // itself. We need to catch this as an internal operation and fail.
411        InodeTree.ResolveResult<FileSystem> resSrc = 
412          fsState.resolve(getUriPath(src), false); 
413      
414        if (resSrc.isInternalDir()) {
415          throw readOnlyMountTable("rename", src);
416        }
417          
418        InodeTree.ResolveResult<FileSystem> resDst = 
419          fsState.resolve(getUriPath(dst), false);
420        if (resDst.isInternalDir()) {
421              throw readOnlyMountTable("rename", dst);
422        }
423        /**
424        // Alternate 1: renames within same file system - valid but we disallow
425        // Alternate 2: (as described in next para - valid but we have disallowed it
426        //
427        // Note we compare the URIs. the URIs include the link targets. 
428        // hence we allow renames across mount links as long as the mount links
429        // point to the same target.
430        if (!resSrc.targetFileSystem.getUri().equals(
431                  resDst.targetFileSystem.getUri())) {
432          throw new IOException("Renames across Mount points not supported");
433        }
434        */
435        
436        //
437        // Alternate 3 : renames ONLY within the the same mount links.
438        //
439        if (resSrc.targetFileSystem !=resDst.targetFileSystem) {
440          throw new IOException("Renames across Mount points not supported");
441        }
442        return resSrc.targetFileSystem.rename(resSrc.remainingPath,
443            resDst.remainingPath);
444      }
445      
446      @Override
447      public void setOwner(final Path f, final String username,
448          final String groupname) throws AccessControlException,
449          FileNotFoundException,
450          IOException {
451        InodeTree.ResolveResult<FileSystem> res = 
452          fsState.resolve(getUriPath(f), true);
453        res.targetFileSystem.setOwner(res.remainingPath, username, groupname); 
454      }
455    
456      @Override
457      public void setPermission(final Path f, final FsPermission permission)
458          throws AccessControlException, FileNotFoundException,
459          IOException {
460        InodeTree.ResolveResult<FileSystem> res = 
461          fsState.resolve(getUriPath(f), true);
462        res.targetFileSystem.setPermission(res.remainingPath, permission); 
463      }
464    
465      @Override
466      public boolean setReplication(final Path f, final short replication)
467          throws AccessControlException, FileNotFoundException,
468          IOException {
469        InodeTree.ResolveResult<FileSystem> res = 
470          fsState.resolve(getUriPath(f), true);
471        return res.targetFileSystem.setReplication(res.remainingPath, replication);
472      }
473    
474      @Override
475      public void setTimes(final Path f, final long mtime, final long atime)
476          throws AccessControlException, FileNotFoundException,
477          IOException {
478        InodeTree.ResolveResult<FileSystem> res = 
479          fsState.resolve(getUriPath(f), true);
480        res.targetFileSystem.setTimes(res.remainingPath, mtime, atime); 
481      }
482    
483      @Override
484      public void setVerifyChecksum(final boolean verifyChecksum) { 
485        List<InodeTree.MountPoint<FileSystem>> mountPoints = 
486            fsState.getMountPoints();
487        for (InodeTree.MountPoint<FileSystem> mount : mountPoints) {
488          mount.target.targetFileSystem.setVerifyChecksum(verifyChecksum);
489        }
490      }
491      
492      @Override
493      public long getDefaultBlockSize() {
494        throw new NotInMountpointException("getDefaultBlockSize");
495      }
496    
497      @Override
498      public short getDefaultReplication() {
499        throw new NotInMountpointException("getDefaultReplication");
500      }
501    
502      @Override
503      public FsServerDefaults getServerDefaults() throws IOException {
504        throw new NotInMountpointException("getServerDefaults");
505      }
506    
507      @Override
508      public long getDefaultBlockSize(Path f) {
509        try {
510          InodeTree.ResolveResult<FileSystem> res =
511            fsState.resolve(getUriPath(f), true);
512          return res.targetFileSystem.getDefaultBlockSize(res.remainingPath);
513        } catch (FileNotFoundException e) {
514          throw new NotInMountpointException(f, "getDefaultBlockSize"); 
515        }
516      }
517    
518      @Override
519      public short getDefaultReplication(Path f) {
520        try {
521          InodeTree.ResolveResult<FileSystem> res =
522            fsState.resolve(getUriPath(f), true);
523          return res.targetFileSystem.getDefaultReplication(res.remainingPath);
524        } catch (FileNotFoundException e) {
525          throw new NotInMountpointException(f, "getDefaultReplication"); 
526        }
527      }
528    
529      @Override
530      public FsServerDefaults getServerDefaults(Path f) throws IOException {
531        InodeTree.ResolveResult<FileSystem> res =
532          fsState.resolve(getUriPath(f), true);
533        return res.targetFileSystem.getServerDefaults(res.remainingPath);    
534      }
535    
536      @Override
537      public ContentSummary getContentSummary(Path f) throws IOException {
538        InodeTree.ResolveResult<FileSystem> res = 
539          fsState.resolve(getUriPath(f), true);
540        return res.targetFileSystem.getContentSummary(res.remainingPath);
541      }
542    
543      @Override
544      public void setWriteChecksum(final boolean writeChecksum) { 
545        List<InodeTree.MountPoint<FileSystem>> mountPoints = 
546            fsState.getMountPoints();
547        for (InodeTree.MountPoint<FileSystem> mount : mountPoints) {
548          mount.target.targetFileSystem.setWriteChecksum(writeChecksum);
549        }
550      }
551    
552      public MountPoint[] getMountPoints() {
553        List<InodeTree.MountPoint<FileSystem>> mountPoints = 
554                      fsState.getMountPoints();
555        
556        MountPoint[] result = new MountPoint[mountPoints.size()];
557        for ( int i = 0; i < mountPoints.size(); ++i ) {
558          result[i] = new MountPoint(new Path(mountPoints.get(i).src), 
559                                  mountPoints.get(i).target.targetDirLinkList);
560        }
561        return result;
562      }
563      
564     
565      @Override
566      public List<Token<?>> getDelegationTokens(String renewer) throws IOException {
567        List<InodeTree.MountPoint<FileSystem>> mountPoints = 
568                    fsState.getMountPoints();
569        int initialListSize  = 0;
570        for (InodeTree.MountPoint<FileSystem> im : mountPoints) {
571          initialListSize += im.target.targetDirLinkList.length; 
572        }
573        List<Token<?>> result = new ArrayList<Token<?>>(initialListSize);
574        for ( int i = 0; i < mountPoints.size(); ++i ) {
575          List<Token<?>> tokens = 
576            mountPoints.get(i).target.targetFileSystem.getDelegationTokens(renewer);
577          if (tokens != null) {
578            result.addAll(tokens);
579          }
580        }
581        return result;
582      }
583    
584      @Override
585      public List<Token<?>> getDelegationTokens(String renewer,
586          Credentials credentials) throws IOException {
587        List<InodeTree.MountPoint<FileSystem>> mountPoints =
588            fsState.getMountPoints();
589        int initialListSize = 0;
590        for (InodeTree.MountPoint<FileSystem> im : mountPoints) {
591          initialListSize += im.target.targetDirLinkList.length;
592        }
593        Set<String> seenServiceNames = new HashSet<String>();
594        List<Token<?>> result = new ArrayList<Token<?>>(initialListSize);
595        for (int i = 0; i < mountPoints.size(); ++i) {
596          String serviceName =
597              mountPoints.get(i).target.targetFileSystem.getCanonicalServiceName();
598          if (serviceName == null || seenServiceNames.contains(serviceName)) {
599            continue;
600          }
601          seenServiceNames.add(serviceName);
602          Token<?> knownToken = credentials.getToken(new Text(serviceName));
603          if (knownToken != null) {
604            result.add(knownToken);
605          } else {
606            List<Token<?>> tokens =
607                mountPoints.get(i).target.targetFileSystem
608                    .getDelegationTokens(renewer);
609            if (tokens != null) {
610              result.addAll(tokens);
611            }
612          }
613        }
614        return result;
615      }
616    
617      /*
618       * An instance of this class represents an internal dir of the viewFs 
619       * that is internal dir of the mount table.
620       * It is a read only mount tables and create, mkdir or delete operations
621       * are not allowed.
622       * If called on create or mkdir then this target is the parent of the
623       * directory in which one is trying to create or mkdir; hence
624       * in this case the path name passed in is the last component. 
625       * Otherwise this target is the end point of the path and hence
626       * the path name passed in is null. 
627       */
628      static class InternalDirOfViewFs extends FileSystem {
629        final InodeTree.INodeDir<FileSystem>  theInternalDir;
630        final long creationTime; // of the the mount table
631        final UserGroupInformation ugi; // the user/group of user who created mtable
632        final URI myUri;
633        
634        public InternalDirOfViewFs(final InodeTree.INodeDir<FileSystem> dir,
635            final long cTime, final UserGroupInformation ugi, URI uri)
636          throws URISyntaxException {
637          myUri = uri;
638          try {
639            initialize(myUri, new Configuration());
640          } catch (IOException e) {
641            throw new RuntimeException("Cannot occur");
642          }
643          theInternalDir = dir;
644          creationTime = cTime;
645          this.ugi = ugi;
646        }
647    
648        static private void checkPathIsSlash(final Path f) throws IOException {
649          if (f != InodeTree.SlashPath) {
650            throw new IOException (
651            "Internal implementation error: expected file name to be /" );
652          }
653        }
654        
655        @Override
656        public URI getUri() {
657          return myUri;
658        }
659    
660        @Override
661        public Path getWorkingDirectory() {
662          throw new RuntimeException (
663          "Internal impl error: getWorkingDir should not have been called" );
664        }
665    
666        @Override
667        public void setWorkingDirectory(final Path new_dir) {
668          throw new RuntimeException (
669          "Internal impl error: getWorkingDir should not have been called" ); 
670        }
671    
672        @Override
673        public FSDataOutputStream append(final Path f, final int bufferSize,
674            final Progressable progress) throws IOException {
675          throw readOnlyMountTable("append", f);
676        }
677    
678        @Override
679        public FSDataOutputStream create(final Path f,
680            final FsPermission permission, final boolean overwrite,
681            final int bufferSize, final short replication, final long blockSize,
682            final Progressable progress) throws AccessControlException {
683          throw readOnlyMountTable("create", f);
684        }
685    
686        @Override
687        public boolean delete(final Path f, final boolean recursive)
688            throws AccessControlException, IOException {
689          checkPathIsSlash(f);
690          throw readOnlyMountTable("delete", f);
691        }
692        
693        @Override
694        @SuppressWarnings("deprecation")
695        public boolean delete(final Path f)
696            throws AccessControlException, IOException {
697          return delete(f, true);
698        }
699    
700        @Override
701        public BlockLocation[] getFileBlockLocations(final FileStatus fs,
702            final long start, final long len) throws 
703            FileNotFoundException, IOException {
704          checkPathIsSlash(fs.getPath());
705          throw new FileNotFoundException("Path points to dir not a file");
706        }
707    
708        @Override
709        public FileChecksum getFileChecksum(final Path f)
710            throws FileNotFoundException, IOException {
711          checkPathIsSlash(f);
712          throw new FileNotFoundException("Path points to dir not a file");
713        }
714    
715        @Override
716        public FileStatus getFileStatus(Path f) throws IOException {
717          checkPathIsSlash(f);
718          return new FileStatus(0, true, 0, 0, creationTime, creationTime,
719              PERMISSION_RRR, ugi.getUserName(), ugi.getGroupNames()[0],
720    
721              new Path(theInternalDir.fullPath).makeQualified(
722                  myUri, null));
723        }
724        
725    
726        @Override
727        public FileStatus[] listStatus(Path f) throws AccessControlException,
728            FileNotFoundException, IOException {
729          checkPathIsSlash(f);
730          FileStatus[] result = new FileStatus[theInternalDir.children.size()];
731          int i = 0;
732          for (Entry<String, INode<FileSystem>> iEntry : 
733                                              theInternalDir.children.entrySet()) {
734            INode<FileSystem> inode = iEntry.getValue();
735            if (inode instanceof INodeLink ) {
736              INodeLink<FileSystem> link = (INodeLink<FileSystem>) inode;
737    
738              result[i++] = new FileStatus(0, false, 0, 0,
739                creationTime, creationTime, PERMISSION_RRR,
740                ugi.getUserName(), ugi.getGroupNames()[0],
741                link.getTargetLink(),
742                new Path(inode.fullPath).makeQualified(
743                    myUri, null));
744            } else {
745              result[i++] = new FileStatus(0, true, 0, 0,
746                creationTime, creationTime, PERMISSION_RRR,
747                ugi.getUserName(), ugi.getGroupNames()[0],
748                new Path(inode.fullPath).makeQualified(
749                    myUri, null));
750            }
751          }
752          return result;
753        }
754    
755        @Override
756        public boolean mkdirs(Path dir, FsPermission permission)
757            throws AccessControlException, FileAlreadyExistsException {
758          if (theInternalDir.isRoot & dir == null) {
759            throw new FileAlreadyExistsException("/ already exits");
760          }
761          // Note dir starts with /
762          if (theInternalDir.children.containsKey(dir.toString().substring(1))) {
763            return true; // this is the stupid semantics of FileSystem
764          }
765          throw readOnlyMountTable("mkdirs",  dir);
766        }
767    
768        @Override
769        public FSDataInputStream open(Path f, int bufferSize)
770            throws AccessControlException, FileNotFoundException, IOException {
771          checkPathIsSlash(f);
772          throw new FileNotFoundException("Path points to dir not a file");
773        }
774    
775        @Override
776        public boolean rename(Path src, Path dst) throws AccessControlException,
777            IOException {
778          checkPathIsSlash(src);
779          checkPathIsSlash(dst);
780          throw readOnlyMountTable("rename", src);     
781        }
782    
783        @Override
784        public void setOwner(Path f, String username, String groupname)
785            throws AccessControlException, IOException {
786          checkPathIsSlash(f);
787          throw readOnlyMountTable("setOwner", f);
788        }
789    
790        @Override
791        public void setPermission(Path f, FsPermission permission)
792            throws AccessControlException, IOException {
793          checkPathIsSlash(f);
794          throw readOnlyMountTable("setPermission", f);    
795        }
796    
797        @Override
798        public boolean setReplication(Path f, short replication)
799            throws AccessControlException, IOException {
800          checkPathIsSlash(f);
801          throw readOnlyMountTable("setReplication", f);
802        }
803    
804        @Override
805        public void setTimes(Path f, long mtime, long atime)
806            throws AccessControlException, IOException {
807          checkPathIsSlash(f);
808          throw readOnlyMountTable("setTimes", f);    
809        }
810    
811        @Override
812        public void setVerifyChecksum(boolean verifyChecksum) {
813          // Noop for viewfs
814        }
815    
816        @Override
817        public FsServerDefaults getServerDefaults(Path f) throws IOException {
818          throw new NotInMountpointException(f, "getServerDefaults");
819        }
820        
821        @Override
822        public long getDefaultBlockSize(Path f) {
823          throw new NotInMountpointException(f, "getDefaultBlockSize");
824        }
825    
826        @Override
827        public short getDefaultReplication(Path f) {
828          throw new NotInMountpointException(f, "getDefaultReplication");
829        }
830      }
831    }