001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.fs.viewfs;
019
020import static org.apache.hadoop.fs.viewfs.Constants.PERMISSION_RRR;
021
022import java.io.FileNotFoundException;
023import java.io.IOException;
024import java.net.URI;
025import java.net.URISyntaxException;
026import java.util.Arrays;
027import java.util.EnumSet;
028import java.util.HashSet;
029import java.util.List;
030import java.util.Set;
031import java.util.StringTokenizer;
032import java.util.Map.Entry;
033
034import org.apache.hadoop.classification.InterfaceAudience;
035import org.apache.hadoop.classification.InterfaceStability;
036import org.apache.hadoop.conf.Configuration;
037import org.apache.hadoop.fs.BlockLocation;
038import org.apache.hadoop.fs.ContentSummary;
039import org.apache.hadoop.fs.CreateFlag;
040import org.apache.hadoop.fs.FSDataInputStream;
041import org.apache.hadoop.fs.FSDataOutputStream;
042import org.apache.hadoop.fs.FileAlreadyExistsException;
043import org.apache.hadoop.fs.FileChecksum;
044import org.apache.hadoop.fs.FileStatus;
045import org.apache.hadoop.fs.FileSystem;
046import org.apache.hadoop.fs.FsConstants;
047import org.apache.hadoop.fs.FsServerDefaults;
048import org.apache.hadoop.fs.InvalidPathException;
049import org.apache.hadoop.fs.Path;
050import org.apache.hadoop.fs.UnsupportedFileSystemException;
051import org.apache.hadoop.fs.permission.FsPermission;
052import org.apache.hadoop.fs.viewfs.InodeTree.INode;
053import org.apache.hadoop.fs.viewfs.InodeTree.INodeLink;
054import org.apache.hadoop.security.AccessControlException;
055import org.apache.hadoop.security.UserGroupInformation;
056import org.apache.hadoop.util.Progressable;
057import 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 */
066public 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  @Override
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 URI getUri() {
240    return myUri;
241  }
242  
243  @Override
244  public Path resolvePath(final Path f)
245      throws IOException {
246    final InodeTree.ResolveResult<FileSystem> res;
247      res = fsState.resolve(getUriPath(f), true);
248    if (res.isInternalDir()) {
249      return f;
250    }
251    return res.targetFileSystem.resolvePath(res.remainingPath);
252  }
253  
254  @Override
255  public Path getHomeDirectory() {
256    if (homeDir == null) {
257      String base = fsState.getHomeDirPrefixValue();
258      if (base == null) {
259        base = "/user";
260      }
261      homeDir = (base.equals("/") ? 
262          this.makeQualified(new Path(base + ugi.getShortUserName())):
263          this.makeQualified(new Path(base + "/" + ugi.getShortUserName())));
264    }
265    return homeDir;
266  }
267  
268  @Override
269  public Path getWorkingDirectory() {
270    return workingDir;
271  }
272
273  @Override
274  public void setWorkingDirectory(final Path new_dir) {
275    getUriPath(new_dir); // this validates the path
276    workingDir = makeAbsolute(new_dir);
277  }
278  
279  @Override
280  public FSDataOutputStream append(final Path f, final int bufferSize,
281      final Progressable progress) throws IOException {
282    InodeTree.ResolveResult<FileSystem> res = 
283      fsState.resolve(getUriPath(f), true);
284    return res.targetFileSystem.append(res.remainingPath, bufferSize, progress);
285  }
286  
287  @Override
288  public FSDataOutputStream createNonRecursive(Path f, FsPermission permission,
289      EnumSet<CreateFlag> flags, int bufferSize, short replication, long blockSize,
290      Progressable progress) throws IOException {
291    InodeTree.ResolveResult<FileSystem> res;
292    try {
293      res = fsState.resolve(getUriPath(f), false);
294    } catch (FileNotFoundException e) {
295        throw readOnlyMountTable("create", f);
296    }
297    assert(res.remainingPath != null);
298    return res.targetFileSystem.createNonRecursive(res.remainingPath, permission,
299         flags, bufferSize, replication, blockSize, progress);
300  }
301  
302  @Override
303  public FSDataOutputStream create(final Path f, final FsPermission permission,
304      final boolean overwrite, final int bufferSize, final short replication,
305      final long blockSize, final Progressable progress) throws IOException {
306    InodeTree.ResolveResult<FileSystem> res;
307    try {
308      res = fsState.resolve(getUriPath(f), false);
309    } catch (FileNotFoundException e) {
310        throw readOnlyMountTable("create", f);
311    }
312    assert(res.remainingPath != null);
313    return res.targetFileSystem.create(res.remainingPath, permission,
314         overwrite, bufferSize, replication, blockSize, progress);
315  }
316
317  
318  @Override
319  public boolean delete(final Path f, final boolean recursive)
320      throws AccessControlException, FileNotFoundException,
321      IOException {
322    InodeTree.ResolveResult<FileSystem> res = 
323      fsState.resolve(getUriPath(f), true);
324    // If internal dir or target is a mount link (ie remainingPath is Slash)
325    if (res.isInternalDir() || res.remainingPath == InodeTree.SlashPath) {
326      throw readOnlyMountTable("delete", f);
327    }
328    return res.targetFileSystem.delete(res.remainingPath, recursive);
329  }
330  
331  @Override
332  @SuppressWarnings("deprecation")
333  public boolean delete(final Path f)
334      throws AccessControlException, FileNotFoundException,
335      IOException {
336      return delete(f, true);
337  }
338  
339  @Override
340  public BlockLocation[] getFileBlockLocations(FileStatus fs, 
341      long start, long len) throws IOException {
342    final InodeTree.ResolveResult<FileSystem> res = 
343      fsState.resolve(getUriPath(fs.getPath()), true);
344    return res.targetFileSystem.getFileBlockLocations(
345          new ViewFsFileStatus(fs, res.remainingPath), start, len);
346  }
347
348  @Override
349  public FileChecksum getFileChecksum(final Path f)
350      throws AccessControlException, FileNotFoundException,
351      IOException {
352    InodeTree.ResolveResult<FileSystem> res = 
353      fsState.resolve(getUriPath(f), true);
354    return res.targetFileSystem.getFileChecksum(res.remainingPath);
355  }
356
357  @Override
358  public FileStatus getFileStatus(final Path f) throws AccessControlException,
359      FileNotFoundException, IOException {
360    InodeTree.ResolveResult<FileSystem> res = 
361      fsState.resolve(getUriPath(f), true);
362    
363    // FileStatus#getPath is a fully qualified path relative to the root of 
364    // target file system.
365    // We need to change it to viewfs URI - relative to root of mount table.
366    
367    // The implementors of RawLocalFileSystem were trying to be very smart.
368    // They implement FileStatus#getOwener lazily -- the object
369    // returned is really a RawLocalFileSystem that expect the
370    // FileStatus#getPath to be unchanged so that it can get owner when needed.
371    // Hence we need to interpose a new ViewFileSystemFileStatus that 
372    // works around.
373    FileStatus status =  res.targetFileSystem.getFileStatus(res.remainingPath);
374    return new ViewFsFileStatus(status, this.makeQualified(f));
375  }
376  
377  
378  @Override
379  public FileStatus[] listStatus(final Path f) throws AccessControlException,
380      FileNotFoundException, IOException {
381    InodeTree.ResolveResult<FileSystem> res =
382      fsState.resolve(getUriPath(f), true);
383    
384    FileStatus[] statusLst = res.targetFileSystem.listStatus(res.remainingPath);
385    if (!res.isInternalDir()) {
386      // We need to change the name in the FileStatus as described in
387      // {@link #getFileStatus }
388      ChRootedFileSystem targetFs;
389      targetFs = (ChRootedFileSystem) res.targetFileSystem;
390      int i = 0;
391      for (FileStatus status : statusLst) {
392          String suffix = targetFs.stripOutRoot(status.getPath());
393          statusLst[i++] = new ViewFsFileStatus(status, this.makeQualified(
394              suffix.length() == 0 ? f : new Path(res.resolvedPath, suffix)));
395      }
396    }
397    return statusLst;
398  }
399
400  @Override
401  public boolean mkdirs(final Path dir, final FsPermission permission)
402      throws IOException {
403    InodeTree.ResolveResult<FileSystem> res = 
404      fsState.resolve(getUriPath(dir), false);
405   return  res.targetFileSystem.mkdirs(res.remainingPath, permission);
406  }
407
408  @Override
409  public FSDataInputStream open(final Path f, final int bufferSize)
410      throws AccessControlException, FileNotFoundException,
411      IOException {
412    InodeTree.ResolveResult<FileSystem> res = 
413        fsState.resolve(getUriPath(f), true);
414    return res.targetFileSystem.open(res.remainingPath, bufferSize);
415  }
416
417  
418  @Override
419  public boolean rename(final Path src, final Path dst) throws IOException {
420    // passing resolveLastComponet as false to catch renaming a mount point to 
421    // itself. We need to catch this as an internal operation and fail.
422    InodeTree.ResolveResult<FileSystem> resSrc = 
423      fsState.resolve(getUriPath(src), false); 
424  
425    if (resSrc.isInternalDir()) {
426      throw readOnlyMountTable("rename", src);
427    }
428      
429    InodeTree.ResolveResult<FileSystem> resDst = 
430      fsState.resolve(getUriPath(dst), false);
431    if (resDst.isInternalDir()) {
432          throw readOnlyMountTable("rename", dst);
433    }
434    /**
435    // Alternate 1: renames within same file system - valid but we disallow
436    // Alternate 2: (as described in next para - valid but we have disallowed it
437    //
438    // Note we compare the URIs. the URIs include the link targets. 
439    // hence we allow renames across mount links as long as the mount links
440    // point to the same target.
441    if (!resSrc.targetFileSystem.getUri().equals(
442              resDst.targetFileSystem.getUri())) {
443      throw new IOException("Renames across Mount points not supported");
444    }
445    */
446    
447    //
448    // Alternate 3 : renames ONLY within the the same mount links.
449    //
450    if (resSrc.targetFileSystem !=resDst.targetFileSystem) {
451      throw new IOException("Renames across Mount points not supported");
452    }
453    return resSrc.targetFileSystem.rename(resSrc.remainingPath,
454        resDst.remainingPath);
455  }
456  
457  @Override
458  public void setOwner(final Path f, final String username,
459      final String groupname) throws AccessControlException,
460      FileNotFoundException,
461      IOException {
462    InodeTree.ResolveResult<FileSystem> res = 
463      fsState.resolve(getUriPath(f), true);
464    res.targetFileSystem.setOwner(res.remainingPath, username, groupname); 
465  }
466
467  @Override
468  public void setPermission(final Path f, final FsPermission permission)
469      throws AccessControlException, FileNotFoundException,
470      IOException {
471    InodeTree.ResolveResult<FileSystem> res = 
472      fsState.resolve(getUriPath(f), true);
473    res.targetFileSystem.setPermission(res.remainingPath, permission); 
474  }
475
476  @Override
477  public boolean setReplication(final Path f, final short replication)
478      throws AccessControlException, FileNotFoundException,
479      IOException {
480    InodeTree.ResolveResult<FileSystem> res = 
481      fsState.resolve(getUriPath(f), true);
482    return res.targetFileSystem.setReplication(res.remainingPath, replication);
483  }
484
485  @Override
486  public void setTimes(final Path f, final long mtime, final long atime)
487      throws AccessControlException, FileNotFoundException,
488      IOException {
489    InodeTree.ResolveResult<FileSystem> res = 
490      fsState.resolve(getUriPath(f), true);
491    res.targetFileSystem.setTimes(res.remainingPath, mtime, atime); 
492  }
493
494  @Override
495  public void setVerifyChecksum(final boolean verifyChecksum) { 
496    List<InodeTree.MountPoint<FileSystem>> mountPoints = 
497        fsState.getMountPoints();
498    for (InodeTree.MountPoint<FileSystem> mount : mountPoints) {
499      mount.target.targetFileSystem.setVerifyChecksum(verifyChecksum);
500    }
501  }
502  
503  @Override
504  public long getDefaultBlockSize() {
505    throw new NotInMountpointException("getDefaultBlockSize");
506  }
507
508  @Override
509  public short getDefaultReplication() {
510    throw new NotInMountpointException("getDefaultReplication");
511  }
512
513  @Override
514  public FsServerDefaults getServerDefaults() throws IOException {
515    throw new NotInMountpointException("getServerDefaults");
516  }
517
518  @Override
519  public long getDefaultBlockSize(Path f) {
520    try {
521      InodeTree.ResolveResult<FileSystem> res =
522        fsState.resolve(getUriPath(f), true);
523      return res.targetFileSystem.getDefaultBlockSize(res.remainingPath);
524    } catch (FileNotFoundException e) {
525      throw new NotInMountpointException(f, "getDefaultBlockSize"); 
526    }
527  }
528
529  @Override
530  public short getDefaultReplication(Path f) {
531    try {
532      InodeTree.ResolveResult<FileSystem> res =
533        fsState.resolve(getUriPath(f), true);
534      return res.targetFileSystem.getDefaultReplication(res.remainingPath);
535    } catch (FileNotFoundException e) {
536      throw new NotInMountpointException(f, "getDefaultReplication"); 
537    }
538  }
539
540  @Override
541  public FsServerDefaults getServerDefaults(Path f) throws IOException {
542    InodeTree.ResolveResult<FileSystem> res =
543      fsState.resolve(getUriPath(f), true);
544    return res.targetFileSystem.getServerDefaults(res.remainingPath);    
545  }
546
547  @Override
548  public ContentSummary getContentSummary(Path f) throws IOException {
549    InodeTree.ResolveResult<FileSystem> res = 
550      fsState.resolve(getUriPath(f), true);
551    return res.targetFileSystem.getContentSummary(res.remainingPath);
552  }
553
554  @Override
555  public void setWriteChecksum(final boolean writeChecksum) { 
556    List<InodeTree.MountPoint<FileSystem>> mountPoints = 
557        fsState.getMountPoints();
558    for (InodeTree.MountPoint<FileSystem> mount : mountPoints) {
559      mount.target.targetFileSystem.setWriteChecksum(writeChecksum);
560    }
561  }
562
563  @Override
564  public FileSystem[] getChildFileSystems() {
565    List<InodeTree.MountPoint<FileSystem>> mountPoints =
566        fsState.getMountPoints();
567    Set<FileSystem> children = new HashSet<FileSystem>();
568    for (InodeTree.MountPoint<FileSystem> mountPoint : mountPoints) {
569      FileSystem targetFs = mountPoint.target.targetFileSystem;
570      children.addAll(Arrays.asList(targetFs.getChildFileSystems()));
571    }
572    return children.toArray(new FileSystem[]{});
573  }
574  
575  public MountPoint[] getMountPoints() {
576    List<InodeTree.MountPoint<FileSystem>> mountPoints = 
577                  fsState.getMountPoints();
578    
579    MountPoint[] result = new MountPoint[mountPoints.size()];
580    for ( int i = 0; i < mountPoints.size(); ++i ) {
581      result[i] = new MountPoint(new Path(mountPoints.get(i).src), 
582                              mountPoints.get(i).target.targetDirLinkList);
583    }
584    return result;
585  }
586  
587  /*
588   * An instance of this class represents an internal dir of the viewFs 
589   * that is internal dir of the mount table.
590   * It is a read only mount tables and create, mkdir or delete operations
591   * are not allowed.
592   * If called on create or mkdir then this target is the parent of the
593   * directory in which one is trying to create or mkdir; hence
594   * in this case the path name passed in is the last component. 
595   * Otherwise this target is the end point of the path and hence
596   * the path name passed in is null. 
597   */
598  static class InternalDirOfViewFs extends FileSystem {
599    final InodeTree.INodeDir<FileSystem>  theInternalDir;
600    final long creationTime; // of the the mount table
601    final UserGroupInformation ugi; // the user/group of user who created mtable
602    final URI myUri;
603    
604    public InternalDirOfViewFs(final InodeTree.INodeDir<FileSystem> dir,
605        final long cTime, final UserGroupInformation ugi, URI uri)
606      throws URISyntaxException {
607      myUri = uri;
608      try {
609        initialize(myUri, new Configuration());
610      } catch (IOException e) {
611        throw new RuntimeException("Cannot occur");
612      }
613      theInternalDir = dir;
614      creationTime = cTime;
615      this.ugi = ugi;
616    }
617
618    static private void checkPathIsSlash(final Path f) throws IOException {
619      if (f != InodeTree.SlashPath) {
620        throw new IOException (
621        "Internal implementation error: expected file name to be /" );
622      }
623    }
624    
625    @Override
626    public URI getUri() {
627      return myUri;
628    }
629
630    @Override
631    public Path getWorkingDirectory() {
632      throw new RuntimeException (
633      "Internal impl error: getWorkingDir should not have been called" );
634    }
635
636    @Override
637    public void setWorkingDirectory(final Path new_dir) {
638      throw new RuntimeException (
639      "Internal impl error: getWorkingDir should not have been called" ); 
640    }
641
642    @Override
643    public FSDataOutputStream append(final Path f, final int bufferSize,
644        final Progressable progress) throws IOException {
645      throw readOnlyMountTable("append", f);
646    }
647
648    @Override
649    public FSDataOutputStream create(final Path f,
650        final FsPermission permission, final boolean overwrite,
651        final int bufferSize, final short replication, final long blockSize,
652        final Progressable progress) throws AccessControlException {
653      throw readOnlyMountTable("create", f);
654    }
655
656    @Override
657    public boolean delete(final Path f, final boolean recursive)
658        throws AccessControlException, IOException {
659      checkPathIsSlash(f);
660      throw readOnlyMountTable("delete", f);
661    }
662    
663    @Override
664    @SuppressWarnings("deprecation")
665    public boolean delete(final Path f)
666        throws AccessControlException, IOException {
667      return delete(f, true);
668    }
669
670    @Override
671    public BlockLocation[] getFileBlockLocations(final FileStatus fs,
672        final long start, final long len) throws 
673        FileNotFoundException, IOException {
674      checkPathIsSlash(fs.getPath());
675      throw new FileNotFoundException("Path points to dir not a file");
676    }
677
678    @Override
679    public FileChecksum getFileChecksum(final Path f)
680        throws FileNotFoundException, IOException {
681      checkPathIsSlash(f);
682      throw new FileNotFoundException("Path points to dir not a file");
683    }
684
685    @Override
686    public FileStatus getFileStatus(Path f) throws IOException {
687      checkPathIsSlash(f);
688      return new FileStatus(0, true, 0, 0, creationTime, creationTime,
689          PERMISSION_RRR, ugi.getUserName(), ugi.getGroupNames()[0],
690
691          new Path(theInternalDir.fullPath).makeQualified(
692              myUri, null));
693    }
694    
695
696    @Override
697    public FileStatus[] listStatus(Path f) throws AccessControlException,
698        FileNotFoundException, IOException {
699      checkPathIsSlash(f);
700      FileStatus[] result = new FileStatus[theInternalDir.children.size()];
701      int i = 0;
702      for (Entry<String, INode<FileSystem>> iEntry : 
703                                          theInternalDir.children.entrySet()) {
704        INode<FileSystem> inode = iEntry.getValue();
705        if (inode instanceof INodeLink ) {
706          INodeLink<FileSystem> link = (INodeLink<FileSystem>) inode;
707
708          result[i++] = new FileStatus(0, false, 0, 0,
709            creationTime, creationTime, PERMISSION_RRR,
710            ugi.getUserName(), ugi.getGroupNames()[0],
711            link.getTargetLink(),
712            new Path(inode.fullPath).makeQualified(
713                myUri, null));
714        } else {
715          result[i++] = new FileStatus(0, true, 0, 0,
716            creationTime, creationTime, PERMISSION_RRR,
717            ugi.getUserName(), ugi.getGroupNames()[0],
718            new Path(inode.fullPath).makeQualified(
719                myUri, null));
720        }
721      }
722      return result;
723    }
724
725    @Override
726    public boolean mkdirs(Path dir, FsPermission permission)
727        throws AccessControlException, FileAlreadyExistsException {
728      if (theInternalDir.isRoot && dir == null) {
729        throw new FileAlreadyExistsException("/ already exits");
730      }
731      // Note dir starts with /
732      if (theInternalDir.children.containsKey(dir.toString().substring(1))) {
733        return true; // this is the stupid semantics of FileSystem
734      }
735      throw readOnlyMountTable("mkdirs",  dir);
736    }
737
738    @Override
739    public FSDataInputStream open(Path f, int bufferSize)
740        throws AccessControlException, FileNotFoundException, IOException {
741      checkPathIsSlash(f);
742      throw new FileNotFoundException("Path points to dir not a file");
743    }
744
745    @Override
746    public boolean rename(Path src, Path dst) throws AccessControlException,
747        IOException {
748      checkPathIsSlash(src);
749      checkPathIsSlash(dst);
750      throw readOnlyMountTable("rename", src);     
751    }
752
753    @Override
754    public void setOwner(Path f, String username, String groupname)
755        throws AccessControlException, IOException {
756      checkPathIsSlash(f);
757      throw readOnlyMountTable("setOwner", f);
758    }
759
760    @Override
761    public void setPermission(Path f, FsPermission permission)
762        throws AccessControlException, IOException {
763      checkPathIsSlash(f);
764      throw readOnlyMountTable("setPermission", f);    
765    }
766
767    @Override
768    public boolean setReplication(Path f, short replication)
769        throws AccessControlException, IOException {
770      checkPathIsSlash(f);
771      throw readOnlyMountTable("setReplication", f);
772    }
773
774    @Override
775    public void setTimes(Path f, long mtime, long atime)
776        throws AccessControlException, IOException {
777      checkPathIsSlash(f);
778      throw readOnlyMountTable("setTimes", f);    
779    }
780
781    @Override
782    public void setVerifyChecksum(boolean verifyChecksum) {
783      // Noop for viewfs
784    }
785
786    @Override
787    public FsServerDefaults getServerDefaults(Path f) throws IOException {
788      throw new NotInMountpointException(f, "getServerDefaults");
789    }
790    
791    @Override
792    public long getDefaultBlockSize(Path f) {
793      throw new NotInMountpointException(f, "getDefaultBlockSize");
794    }
795
796    @Override
797    public short getDefaultReplication(Path f) {
798      throw new NotInMountpointException(f, "getDefaultReplication");
799    }
800  }
801}