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