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