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