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