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