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.hdfs.server.namenode; 019 020import static org.apache.hadoop.hdfs.server.common.Util.now; 021 022import java.io.Closeable; 023import java.io.FileNotFoundException; 024import java.io.IOException; 025import java.util.ArrayList; 026import java.util.List; 027import java.util.concurrent.TimeUnit; 028import java.util.concurrent.locks.Condition; 029import java.util.concurrent.locks.ReentrantReadWriteLock; 030 031import org.apache.hadoop.conf.Configuration; 032import org.apache.hadoop.fs.ContentSummary; 033import org.apache.hadoop.fs.FileAlreadyExistsException; 034import org.apache.hadoop.fs.Options; 035import org.apache.hadoop.fs.Options.Rename; 036import org.apache.hadoop.fs.ParentNotDirectoryException; 037import org.apache.hadoop.fs.Path; 038import org.apache.hadoop.fs.UnresolvedLinkException; 039import org.apache.hadoop.fs.permission.FsAction; 040import org.apache.hadoop.fs.permission.FsPermission; 041import org.apache.hadoop.fs.permission.PermissionStatus; 042import org.apache.hadoop.hdfs.DFSConfigKeys; 043import org.apache.hadoop.hdfs.DistributedFileSystem; 044import org.apache.hadoop.hdfs.protocol.Block; 045import org.apache.hadoop.hdfs.protocol.ClientProtocol; 046import org.apache.hadoop.hdfs.protocol.DirectoryListing; 047import org.apache.hadoop.hdfs.protocol.HdfsConstants; 048import org.apache.hadoop.hdfs.protocol.FSLimitException; 049import org.apache.hadoop.hdfs.protocol.FSLimitException.MaxDirectoryItemsExceededException; 050import org.apache.hadoop.hdfs.protocol.FSLimitException.PathComponentTooLongException; 051import org.apache.hadoop.hdfs.protocol.HdfsFileStatus; 052import org.apache.hadoop.hdfs.protocol.HdfsLocatedFileStatus; 053import org.apache.hadoop.hdfs.protocol.LocatedBlocks; 054import org.apache.hadoop.hdfs.protocol.QuotaExceededException; 055import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfo; 056import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfoUnderConstruction; 057import org.apache.hadoop.hdfs.server.blockmanagement.BlockManager; 058import org.apache.hadoop.hdfs.server.blockmanagement.DatanodeDescriptor; 059import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.BlockUCState; 060import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.StartupOption; 061import org.apache.hadoop.hdfs.util.ByteArray; 062 063import com.google.common.annotations.VisibleForTesting; 064import com.google.common.base.Preconditions; 065 066/************************************************* 067 * FSDirectory stores the filesystem directory state. 068 * It handles writing/loading values to disk, and logging 069 * changes as we go. 070 * 071 * It keeps the filename->blockset mapping always-current 072 * and logged to disk. 073 * 074 *************************************************/ 075public class FSDirectory implements Closeable { 076 077 INodeDirectoryWithQuota rootDir; 078 FSImage fsImage; 079 private volatile boolean ready = false; 080 private static final long UNKNOWN_DISK_SPACE = -1; 081 private final int maxComponentLength; 082 private final int maxDirItems; 083 private final int lsLimit; // max list limit 084 private final int contentCountLimit; // max content summary counts per run 085 086 // lock to protect the directory and BlockMap 087 private ReentrantReadWriteLock dirLock; 088 private Condition cond; 089 090 // utility methods to acquire and release read lock and write lock 091 void readLock() { 092 this.dirLock.readLock().lock(); 093 } 094 095 void readUnlock() { 096 this.dirLock.readLock().unlock(); 097 } 098 099 void writeLock() { 100 this.dirLock.writeLock().lock(); 101 } 102 103 void writeUnlock() { 104 this.dirLock.writeLock().unlock(); 105 } 106 107 boolean hasWriteLock() { 108 return this.dirLock.isWriteLockedByCurrentThread(); 109 } 110 111 boolean hasReadLock() { 112 return this.dirLock.getReadHoldCount() > 0; 113 } 114 115 public int getReadHoldCount() { 116 return this.dirLock.getReadHoldCount(); 117 } 118 119 public int getWriteHoldCount() { 120 return this.dirLock.getWriteHoldCount(); 121 } 122 123 /** 124 * Caches frequently used file names used in {@link INode} to reuse 125 * byte[] objects and reduce heap usage. 126 */ 127 private final NameCache<ByteArray> nameCache; 128 129 /** Access an existing dfs name directory. */ 130 FSDirectory(FSNamesystem ns, Configuration conf) throws IOException { 131 this(new FSImage(conf), ns, conf); 132 } 133 134 FSDirectory(FSImage fsImage, FSNamesystem ns, Configuration conf) { 135 this.dirLock = new ReentrantReadWriteLock(true); // fair 136 this.cond = dirLock.writeLock().newCondition(); 137 fsImage.setFSNamesystem(ns); 138 rootDir = new INodeDirectoryWithQuota(INodeDirectory.ROOT_NAME, 139 ns.createFsOwnerPermissions(new FsPermission((short)0755)), 140 Integer.MAX_VALUE, UNKNOWN_DISK_SPACE); 141 this.fsImage = fsImage; 142 int configuredLimit = conf.getInt( 143 DFSConfigKeys.DFS_LIST_LIMIT, DFSConfigKeys.DFS_LIST_LIMIT_DEFAULT); 144 this.lsLimit = configuredLimit>0 ? 145 configuredLimit : DFSConfigKeys.DFS_LIST_LIMIT_DEFAULT; 146 147 this.contentCountLimit = conf.getInt( 148 DFSConfigKeys.DFS_CONTENT_SUMMARY_LIMIT_KEY, 149 DFSConfigKeys.DFS_CONTENT_SUMMARY_LIMIT_DEFAULT); 150 151 // filesystem limits 152 this.maxComponentLength = conf.getInt( 153 DFSConfigKeys.DFS_NAMENODE_MAX_COMPONENT_LENGTH_KEY, 154 DFSConfigKeys.DFS_NAMENODE_MAX_COMPONENT_LENGTH_DEFAULT); 155 this.maxDirItems = conf.getInt( 156 DFSConfigKeys.DFS_NAMENODE_MAX_DIRECTORY_ITEMS_KEY, 157 DFSConfigKeys.DFS_NAMENODE_MAX_DIRECTORY_ITEMS_DEFAULT); 158 159 int threshold = conf.getInt( 160 DFSConfigKeys.DFS_NAMENODE_NAME_CACHE_THRESHOLD_KEY, 161 DFSConfigKeys.DFS_NAMENODE_NAME_CACHE_THRESHOLD_DEFAULT); 162 NameNode.LOG.info("Caching file names occuring more than " + threshold 163 + " times "); 164 nameCache = new NameCache<ByteArray>(threshold); 165 } 166 167 private FSNamesystem getFSNamesystem() { 168 return fsImage.getFSNamesystem(); 169 } 170 171 private BlockManager getBlockManager() { 172 return getFSNamesystem().getBlockManager(); 173 } 174 175 /** 176 * Load the filesystem image into memory. 177 * 178 * @param startOpt Startup type as specified by the user. 179 * @throws IOException If image or editlog cannot be read. 180 */ 181 void loadFSImage(StartupOption startOpt) 182 throws IOException { 183 // format before starting up if requested 184 if (startOpt == StartupOption.FORMAT) { 185 fsImage.format(fsImage.getStorage().determineClusterId());// reuse current id 186 187 startOpt = StartupOption.REGULAR; 188 } 189 boolean success = false; 190 try { 191 if (fsImage.recoverTransitionRead(startOpt)) { 192 fsImage.saveNamespace(); 193 } 194 fsImage.openEditLog(); 195 196 fsImage.setCheckpointDirectories(null, null); 197 success = true; 198 } finally { 199 if (!success) { 200 fsImage.close(); 201 } 202 } 203 setReady(); 204 } 205 206 207 /** 208 * Notify that loading of this FSDirectory is complete, and 209 * it is ready for use 210 */ 211 void imageLoadComplete() { 212 Preconditions.checkState(!ready, "FSDirectory already loaded"); 213 setReady(); 214 } 215 216 void setReady() { 217 if(ready) return; 218 writeLock(); 219 try { 220 setReady(true); 221 this.nameCache.initialized(); 222 cond.signalAll(); 223 } finally { 224 writeUnlock(); 225 } 226 } 227 228 //This is for testing purposes only 229 @VisibleForTesting 230 boolean isReady() { 231 return ready; 232 } 233 234 // exposed for unit tests 235 protected void setReady(boolean flag) { 236 ready = flag; 237 } 238 239 private void incrDeletedFileCount(int count) { 240 if (getFSNamesystem() != null) 241 NameNode.getNameNodeMetrics().incrFilesDeleted(count); 242 } 243 244 /** 245 * Shutdown the filestore 246 */ 247 public void close() throws IOException { 248 fsImage.close(); 249 } 250 251 /** 252 * Block until the object is ready to be used. 253 */ 254 void waitForReady() { 255 if (!ready) { 256 writeLock(); 257 try { 258 while (!ready) { 259 try { 260 cond.await(5000, TimeUnit.MILLISECONDS); 261 } catch (InterruptedException ie) { 262 } 263 } 264 } finally { 265 writeUnlock(); 266 } 267 } 268 } 269 270 /** 271 * Add the given filename to the fs. 272 * @throws QuotaExceededException 273 * @throws FileAlreadyExistsException 274 */ 275 INodeFileUnderConstruction addFile(String path, 276 PermissionStatus permissions, 277 short replication, 278 long preferredBlockSize, 279 String clientName, 280 String clientMachine, 281 DatanodeDescriptor clientNode, 282 long generationStamp) 283 throws FileAlreadyExistsException, QuotaExceededException, 284 UnresolvedLinkException { 285 waitForReady(); 286 287 // Always do an implicit mkdirs for parent directory tree. 288 long modTime = now(); 289 290 Path parent = new Path(path).getParent(); 291 if (parent == null) { 292 // Trying to add "/" as a file - this path has no 293 // parent -- avoids an NPE below. 294 return null; 295 } 296 297 if (!mkdirs(parent.toString(), permissions, true, modTime)) { 298 return null; 299 } 300 INodeFileUnderConstruction newNode = new INodeFileUnderConstruction( 301 permissions,replication, 302 preferredBlockSize, modTime, clientName, 303 clientMachine, clientNode); 304 writeLock(); 305 try { 306 newNode = addNode(path, newNode, UNKNOWN_DISK_SPACE); 307 } finally { 308 writeUnlock(); 309 } 310 if (newNode == null) { 311 NameNode.stateChangeLog.info("DIR* FSDirectory.addFile: " 312 +"failed to add "+path 313 +" to the file system"); 314 return null; 315 } 316 317 if(NameNode.stateChangeLog.isDebugEnabled()) { 318 NameNode.stateChangeLog.debug("DIR* FSDirectory.addFile: " 319 +path+" is added to the file system"); 320 } 321 return newNode; 322 } 323 324 /** 325 */ 326 INode unprotectedAddFile( String path, 327 PermissionStatus permissions, 328 BlockInfo[] blocks, 329 short replication, 330 long modificationTime, 331 long atime, 332 long preferredBlockSize, 333 String clientName, 334 String clientMachine) 335 throws UnresolvedLinkException { 336 INode newNode; 337 assert hasWriteLock(); 338 if (blocks == null) 339 newNode = new INodeDirectory(permissions, modificationTime); 340 else if(blocks.length == 0 || blocks[blocks.length-1].getBlockUCState() 341 == BlockUCState.UNDER_CONSTRUCTION) { 342 newNode = new INodeFileUnderConstruction( 343 permissions, blocks.length, replication, 344 preferredBlockSize, modificationTime, clientName, 345 clientMachine, null); 346 } else { 347 newNode = new INodeFile(permissions, blocks.length, replication, 348 modificationTime, atime, preferredBlockSize); 349 } 350 writeLock(); 351 try { 352 try { 353 newNode = addNode(path, newNode, UNKNOWN_DISK_SPACE); 354 if(newNode != null && blocks != null) { 355 int nrBlocks = blocks.length; 356 // Add file->block mapping 357 INodeFile newF = (INodeFile)newNode; 358 for (int i = 0; i < nrBlocks; i++) { 359 newF.setBlock(i, getBlockManager().addINode(blocks[i], newF)); 360 } 361 } 362 } catch (IOException e) { 363 return null; 364 } 365 return newNode; 366 } finally { 367 writeUnlock(); 368 } 369 370 } 371 372 /** 373 * Update files in-memory data structures with new block information. 374 * @throws IOException 375 */ 376 void updateFile(INodeFile file, 377 String path, 378 BlockInfo[] blocks, 379 long mtime, 380 long atime) throws IOException { 381 382 // Update the salient file attributes. 383 file.setAccessTime(atime); 384 file.setModificationTimeForce(mtime); 385 386 // Update its block list 387 BlockInfo[] oldBlocks = file.getBlocks(); 388 389 // Are we only updating the last block's gen stamp. 390 boolean isGenStampUpdate = oldBlocks.length == blocks.length; 391 392 // First, update blocks in common 393 BlockInfo oldBlock = null; 394 for (int i = 0; i < oldBlocks.length && i < blocks.length; i++) { 395 oldBlock = oldBlocks[i]; 396 Block newBlock = blocks[i]; 397 398 boolean isLastBlock = i == oldBlocks.length - 1; 399 if (oldBlock.getBlockId() != newBlock.getBlockId() || 400 (oldBlock.getGenerationStamp() != newBlock.getGenerationStamp() && 401 !(isGenStampUpdate && isLastBlock))) { 402 throw new IOException("Mismatched block IDs or generation stamps, " + 403 "attempting to replace block " + oldBlock + " with " + newBlock + 404 " as block # " + i + "/" + blocks.length + " of " + path); 405 } 406 407 oldBlock.setNumBytes(newBlock.getNumBytes()); 408 oldBlock.setGenerationStamp(newBlock.getGenerationStamp()); 409 } 410 411 if (blocks.length < oldBlocks.length) { 412 // We're removing a block from the file, e.g. abandonBlock(...) 413 if (!file.isUnderConstruction()) { 414 throw new IOException("Trying to remove a block from file " + 415 path + " which is not under construction."); 416 } 417 if (blocks.length != oldBlocks.length - 1) { 418 throw new IOException("Trying to remove more than one block from file " 419 + path); 420 } 421 unprotectedRemoveBlock(path, 422 (INodeFileUnderConstruction)file, oldBlocks[oldBlocks.length - 1]); 423 } else if (blocks.length > oldBlocks.length) { 424 // We're adding blocks 425 // First complete last old Block 426 getBlockManager().completeBlock(file, oldBlocks.length-1, true); 427 // Add the new blocks 428 for (int i = oldBlocks.length; i < blocks.length; i++) { 429 // addBlock(); 430 BlockInfo newBI = blocks[i]; 431 getBlockManager().addINode(newBI, file); 432 file.addBlock(newBI); 433 } 434 } 435 } 436 437 INodeDirectory addToParent(byte[] src, INodeDirectory parentINode, 438 INode newNode, boolean propagateModTime) throws UnresolvedLinkException { 439 // NOTE: This does not update space counts for parents 440 INodeDirectory newParent = null; 441 writeLock(); 442 try { 443 try { 444 newParent = rootDir.addToParent(src, newNode, parentINode, 445 propagateModTime); 446 cacheName(newNode); 447 } catch (FileNotFoundException e) { 448 return null; 449 } 450 if(newParent == null) 451 return null; 452 if(!newNode.isDirectory() && !newNode.isLink()) { 453 // Add file->block mapping 454 INodeFile newF = (INodeFile)newNode; 455 BlockInfo[] blocks = newF.getBlocks(); 456 for (int i = 0; i < blocks.length; i++) { 457 newF.setBlock(i, getBlockManager().addINode(blocks[i], newF)); 458 } 459 } 460 } finally { 461 writeUnlock(); 462 } 463 return newParent; 464 } 465 466 /** 467 * Add a block to the file. Returns a reference to the added block. 468 */ 469 BlockInfo addBlock(String path, 470 INode[] inodes, 471 Block block, 472 DatanodeDescriptor targets[] 473 ) throws QuotaExceededException { 474 waitForReady(); 475 476 writeLock(); 477 try { 478 assert inodes[inodes.length-1].isUnderConstruction() : 479 "INode should correspond to a file under construction"; 480 INodeFileUnderConstruction fileINode = 481 (INodeFileUnderConstruction)inodes[inodes.length-1]; 482 483 // check quota limits and updated space consumed 484 updateCount(inodes, inodes.length-1, 0, 485 fileINode.getPreferredBlockSize()*fileINode.getReplication(), true); 486 487 // associate new last block for the file 488 BlockInfoUnderConstruction blockInfo = 489 new BlockInfoUnderConstruction( 490 block, 491 fileINode.getReplication(), 492 BlockUCState.UNDER_CONSTRUCTION, 493 targets); 494 getBlockManager().addINode(blockInfo, fileINode); 495 fileINode.addBlock(blockInfo); 496 497 if(NameNode.stateChangeLog.isDebugEnabled()) { 498 NameNode.stateChangeLog.debug("DIR* FSDirectory.addBlock: " 499 + path + " with " + block 500 + " block is added to the in-memory " 501 + "file system"); 502 } 503 return blockInfo; 504 } finally { 505 writeUnlock(); 506 } 507 } 508 509 /** 510 * Persist the block list for the inode. 511 */ 512 void persistBlocks(String path, INodeFileUnderConstruction file) { 513 waitForReady(); 514 515 writeLock(); 516 try { 517 fsImage.getEditLog().logOpenFile(path, file); 518 if(NameNode.stateChangeLog.isDebugEnabled()) { 519 NameNode.stateChangeLog.debug("DIR* FSDirectory.persistBlocks: " 520 +path+" with "+ file.getBlocks().length 521 +" blocks is persisted to the file system"); 522 } 523 } finally { 524 writeUnlock(); 525 } 526 } 527 528 /** 529 * Close file. 530 */ 531 void closeFile(String path, INodeFile file) { 532 waitForReady(); 533 long now = now(); 534 writeLock(); 535 try { 536 // file is closed 537 file.setModificationTimeForce(now); 538 fsImage.getEditLog().logCloseFile(path, file); 539 if (NameNode.stateChangeLog.isDebugEnabled()) { 540 NameNode.stateChangeLog.debug("DIR* FSDirectory.closeFile: " 541 +path+" with "+ file.getBlocks().length 542 +" blocks is persisted to the file system"); 543 } 544 } finally { 545 writeUnlock(); 546 } 547 } 548 549 /** 550 * Remove a block to the file. 551 */ 552 boolean removeBlock(String path, INodeFileUnderConstruction fileNode, 553 Block block) throws IOException { 554 waitForReady(); 555 556 writeLock(); 557 try { 558 unprotectedRemoveBlock(path, fileNode, block); 559 // write modified block locations to log 560 fsImage.getEditLog().logOpenFile(path, fileNode); 561 } finally { 562 writeUnlock(); 563 } 564 return true; 565 } 566 567 void unprotectedRemoveBlock(String path, INodeFileUnderConstruction fileNode, 568 Block block) throws IOException { 569 // modify file-> block and blocksMap 570 fileNode.removeLastBlock(block); 571 getBlockManager().removeBlockFromMap(block); 572 573 if(NameNode.stateChangeLog.isDebugEnabled()) { 574 NameNode.stateChangeLog.debug("DIR* FSDirectory.removeBlock: " 575 +path+" with "+block 576 +" block is removed from the file system"); 577 } 578 579 // update space consumed 580 INode[] pathINodes = getExistingPathINodes(path); 581 updateCount(pathINodes, pathINodes.length-1, 0, 582 -fileNode.getPreferredBlockSize()*fileNode.getReplication(), true); 583 } 584 585 /** 586 * @see #unprotectedRenameTo(String, String, long) 587 * @deprecated Use {@link #renameTo(String, String, Rename...)} instead. 588 */ 589 @Deprecated 590 boolean renameTo(String src, String dst) 591 throws QuotaExceededException, UnresolvedLinkException, 592 FileAlreadyExistsException { 593 if (NameNode.stateChangeLog.isDebugEnabled()) { 594 NameNode.stateChangeLog.debug("DIR* FSDirectory.renameTo: " 595 +src+" to "+dst); 596 } 597 waitForReady(); 598 long now = now(); 599 writeLock(); 600 try { 601 if (!unprotectedRenameTo(src, dst, now)) 602 return false; 603 } finally { 604 writeUnlock(); 605 } 606 fsImage.getEditLog().logRename(src, dst, now); 607 return true; 608 } 609 610 /** 611 * @see #unprotectedRenameTo(String, String, long, Options.Rename...) 612 */ 613 void renameTo(String src, String dst, Options.Rename... options) 614 throws FileAlreadyExistsException, FileNotFoundException, 615 ParentNotDirectoryException, QuotaExceededException, 616 UnresolvedLinkException, IOException { 617 if (NameNode.stateChangeLog.isDebugEnabled()) { 618 NameNode.stateChangeLog.debug("DIR* FSDirectory.renameTo: " + src 619 + " to " + dst); 620 } 621 waitForReady(); 622 long now = now(); 623 writeLock(); 624 try { 625 if (unprotectedRenameTo(src, dst, now, options)) { 626 incrDeletedFileCount(1); 627 } 628 } finally { 629 writeUnlock(); 630 } 631 fsImage.getEditLog().logRename(src, dst, now, options); 632 } 633 634 /** 635 * Change a path name 636 * 637 * @param src source path 638 * @param dst destination path 639 * @return true if rename succeeds; false otherwise 640 * @throws QuotaExceededException if the operation violates any quota limit 641 * @throws FileAlreadyExistsException if the src is a symlink that points to dst 642 * @deprecated See {@link #renameTo(String, String)} 643 */ 644 @Deprecated 645 boolean unprotectedRenameTo(String src, String dst, long timestamp) 646 throws QuotaExceededException, UnresolvedLinkException, 647 FileAlreadyExistsException { 648 assert hasWriteLock(); 649 INode[] srcInodes = rootDir.getExistingPathINodes(src, false); 650 INode srcInode = srcInodes[srcInodes.length-1]; 651 652 // check the validation of the source 653 if (srcInode == null) { 654 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 655 + "failed to rename " + src + " to " + dst 656 + " because source does not exist"); 657 return false; 658 } 659 if (srcInodes.length == 1) { 660 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 661 +"failed to rename "+src+" to "+dst+ " because source is the root"); 662 return false; 663 } 664 if (isDir(dst)) { 665 dst += Path.SEPARATOR + new Path(src).getName(); 666 } 667 668 // check the validity of the destination 669 if (dst.equals(src)) { 670 return true; 671 } 672 if (srcInode.isLink() && 673 dst.equals(((INodeSymlink)srcInode).getLinkValue())) { 674 throw new FileAlreadyExistsException( 675 "Cannot rename symlink "+src+" to its target "+dst); 676 } 677 678 // dst cannot be directory or a file under src 679 if (dst.startsWith(src) && 680 dst.charAt(src.length()) == Path.SEPARATOR_CHAR) { 681 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 682 + "failed to rename " + src + " to " + dst 683 + " because destination starts with src"); 684 return false; 685 } 686 687 byte[][] dstComponents = INode.getPathComponents(dst); 688 INode[] dstInodes = new INode[dstComponents.length]; 689 rootDir.getExistingPathINodes(dstComponents, dstInodes, false); 690 if (dstInodes[dstInodes.length-1] != null) { 691 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 692 +"failed to rename "+src+" to "+dst+ 693 " because destination exists"); 694 return false; 695 } 696 if (dstInodes[dstInodes.length-2] == null) { 697 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 698 +"failed to rename "+src+" to "+dst+ 699 " because destination's parent does not exist"); 700 return false; 701 } 702 703 // Ensure dst has quota to accommodate rename 704 verifyQuotaForRename(srcInodes,dstInodes); 705 706 INode dstChild = null; 707 INode srcChild = null; 708 String srcChildName = null; 709 try { 710 // remove src 711 srcChild = removeChild(srcInodes, srcInodes.length-1); 712 if (srcChild == null) { 713 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 714 + "failed to rename " + src + " to " + dst 715 + " because the source can not be removed"); 716 return false; 717 } 718 srcChildName = srcChild.getLocalName(); 719 srcChild.setLocalName(dstComponents[dstInodes.length-1]); 720 721 // add src to the destination 722 dstChild = addChildNoQuotaCheck(dstInodes, dstInodes.length - 1, 723 srcChild, UNKNOWN_DISK_SPACE); 724 if (dstChild != null) { 725 srcChild = null; 726 if (NameNode.stateChangeLog.isDebugEnabled()) { 727 NameNode.stateChangeLog.debug("DIR* FSDirectory.unprotectedRenameTo: " 728 + src + " is renamed to " + dst); 729 } 730 // update modification time of dst and the parent of src 731 srcInodes[srcInodes.length-2].setModificationTime(timestamp); 732 dstInodes[dstInodes.length-2].setModificationTime(timestamp); 733 // update moved leases with new filename 734 getFSNamesystem().unprotectedChangeLease(src, dst); 735 return true; 736 } 737 } finally { 738 if (dstChild == null && srcChild != null) { 739 // put it back 740 srcChild.setLocalName(srcChildName); 741 addChildNoQuotaCheck(srcInodes, srcInodes.length - 1, srcChild, 742 UNKNOWN_DISK_SPACE); 743 } 744 } 745 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 746 +"failed to rename "+src+" to "+dst); 747 return false; 748 } 749 750 /** 751 * Rename src to dst. 752 * See {@link DistributedFileSystem#rename(Path, Path, Options.Rename...)} 753 * for details related to rename semantics and exceptions. 754 * 755 * @param src source path 756 * @param dst destination path 757 * @param timestamp modification time 758 * @param options Rename options 759 */ 760 boolean unprotectedRenameTo(String src, String dst, long timestamp, 761 Options.Rename... options) throws FileAlreadyExistsException, 762 FileNotFoundException, ParentNotDirectoryException, 763 QuotaExceededException, UnresolvedLinkException, IOException { 764 assert hasWriteLock(); 765 boolean overwrite = false; 766 if (null != options) { 767 for (Rename option : options) { 768 if (option == Rename.OVERWRITE) { 769 overwrite = true; 770 } 771 } 772 } 773 String error = null; 774 final INode[] srcInodes = rootDir.getExistingPathINodes(src, false); 775 final INode srcInode = srcInodes[srcInodes.length - 1]; 776 // validate source 777 if (srcInode == null) { 778 error = "rename source " + src + " is not found."; 779 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 780 + error); 781 throw new FileNotFoundException(error); 782 } 783 if (srcInodes.length == 1) { 784 error = "rename source cannot be the root"; 785 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 786 + error); 787 throw new IOException(error); 788 } 789 790 // validate the destination 791 if (dst.equals(src)) { 792 throw new FileAlreadyExistsException( 793 "The source "+src+" and destination "+dst+" are the same"); 794 } 795 if (srcInode.isLink() && 796 dst.equals(((INodeSymlink)srcInode).getLinkValue())) { 797 throw new FileAlreadyExistsException( 798 "Cannot rename symlink "+src+" to its target "+dst); 799 } 800 // dst cannot be a directory or a file under src 801 if (dst.startsWith(src) && 802 dst.charAt(src.length()) == Path.SEPARATOR_CHAR) { 803 error = "Rename destination " + dst 804 + " is a directory or file under source " + src; 805 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 806 + error); 807 throw new IOException(error); 808 } 809 final byte[][] dstComponents = INode.getPathComponents(dst); 810 final INode[] dstInodes = new INode[dstComponents.length]; 811 rootDir.getExistingPathINodes(dstComponents, dstInodes, false); 812 INode dstInode = dstInodes[dstInodes.length - 1]; 813 if (dstInodes.length == 1) { 814 error = "rename destination cannot be the root"; 815 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 816 + error); 817 throw new IOException(error); 818 } 819 if (dstInode != null) { // Destination exists 820 // It's OK to rename a file to a symlink and vice versa 821 if (dstInode.isDirectory() != srcInode.isDirectory()) { 822 error = "Source " + src + " and destination " + dst 823 + " must both be directories"; 824 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 825 + error); 826 throw new IOException(error); 827 } 828 if (!overwrite) { // If destination exists, overwrite flag must be true 829 error = "rename destination " + dst + " already exists"; 830 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 831 + error); 832 throw new FileAlreadyExistsException(error); 833 } 834 List<INode> children = dstInode.isDirectory() ? 835 ((INodeDirectory) dstInode).getChildrenRaw() : null; 836 if (children != null && children.size() != 0) { 837 error = "rename cannot overwrite non empty destination directory " 838 + dst; 839 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 840 + error); 841 throw new IOException(error); 842 } 843 } 844 if (dstInodes[dstInodes.length - 2] == null) { 845 error = "rename destination parent " + dst + " not found."; 846 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 847 + error); 848 throw new FileNotFoundException(error); 849 } 850 if (!dstInodes[dstInodes.length - 2].isDirectory()) { 851 error = "rename destination parent " + dst + " is a file."; 852 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 853 + error); 854 throw new ParentNotDirectoryException(error); 855 } 856 857 // Ensure dst has quota to accommodate rename 858 verifyQuotaForRename(srcInodes, dstInodes); 859 INode removedSrc = removeChild(srcInodes, srcInodes.length - 1); 860 if (removedSrc == null) { 861 error = "Failed to rename " + src + " to " + dst 862 + " because the source can not be removed"; 863 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 864 + error); 865 throw new IOException(error); 866 } 867 final String srcChildName = removedSrc.getLocalName(); 868 String dstChildName = null; 869 INode removedDst = null; 870 try { 871 if (dstInode != null) { // dst exists remove it 872 removedDst = removeChild(dstInodes, dstInodes.length - 1); 873 dstChildName = removedDst.getLocalName(); 874 } 875 876 INode dstChild = null; 877 removedSrc.setLocalName(dstComponents[dstInodes.length - 1]); 878 // add src as dst to complete rename 879 dstChild = addChildNoQuotaCheck(dstInodes, dstInodes.length - 1, 880 removedSrc, UNKNOWN_DISK_SPACE); 881 882 int filesDeleted = 0; 883 if (dstChild != null) { 884 removedSrc = null; 885 if (NameNode.stateChangeLog.isDebugEnabled()) { 886 NameNode.stateChangeLog.debug( 887 "DIR* FSDirectory.unprotectedRenameTo: " + src 888 + " is renamed to " + dst); 889 } 890 srcInodes[srcInodes.length - 2].setModificationTime(timestamp); 891 dstInodes[dstInodes.length - 2].setModificationTime(timestamp); 892 // update moved lease with new filename 893 getFSNamesystem().unprotectedChangeLease(src, dst); 894 895 // Collect the blocks and remove the lease for previous dst 896 if (removedDst != null) { 897 INode rmdst = removedDst; 898 removedDst = null; 899 List<Block> collectedBlocks = new ArrayList<Block>(); 900 filesDeleted = rmdst.collectSubtreeBlocksAndClear(collectedBlocks); 901 getFSNamesystem().removePathAndBlocks(src, collectedBlocks); 902 } 903 return filesDeleted >0; 904 } 905 } finally { 906 if (removedSrc != null) { 907 // Rename failed - restore src 908 removedSrc.setLocalName(srcChildName); 909 addChildNoQuotaCheck(srcInodes, srcInodes.length - 1, removedSrc, 910 UNKNOWN_DISK_SPACE); 911 } 912 if (removedDst != null) { 913 // Rename failed - restore dst 914 removedDst.setLocalName(dstChildName); 915 addChildNoQuotaCheck(dstInodes, dstInodes.length - 1, removedDst, 916 UNKNOWN_DISK_SPACE); 917 } 918 } 919 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: " 920 + "failed to rename " + src + " to " + dst); 921 throw new IOException("rename from " + src + " to " + dst + " failed."); 922 } 923 924 /** 925 * Set file replication 926 * 927 * @param src file name 928 * @param replication new replication 929 * @param oldReplication old replication - output parameter 930 * @return array of file blocks 931 * @throws QuotaExceededException 932 */ 933 Block[] setReplication(String src, short replication, short[] oldReplication) 934 throws QuotaExceededException, UnresolvedLinkException { 935 waitForReady(); 936 Block[] fileBlocks = null; 937 writeLock(); 938 try { 939 fileBlocks = unprotectedSetReplication(src, replication, oldReplication); 940 if (fileBlocks != null) // log replication change 941 fsImage.getEditLog().logSetReplication(src, replication); 942 return fileBlocks; 943 } finally { 944 writeUnlock(); 945 } 946 } 947 948 Block[] unprotectedSetReplication(String src, 949 short replication, 950 short[] oldReplication 951 ) throws QuotaExceededException, 952 UnresolvedLinkException { 953 assert hasWriteLock(); 954 955 INode[] inodes = rootDir.getExistingPathINodes(src, true); 956 INode inode = inodes[inodes.length - 1]; 957 if (inode == null) { 958 return null; 959 } 960 assert !inode.isLink(); 961 if (inode.isDirectory()) { 962 return null; 963 } 964 INodeFile fileNode = (INodeFile)inode; 965 final short oldRepl = fileNode.getReplication(); 966 967 // check disk quota 968 long dsDelta = (replication - oldRepl) * (fileNode.diskspaceConsumed()/oldRepl); 969 updateCount(inodes, inodes.length-1, 0, dsDelta, true); 970 971 fileNode.setReplication(replication); 972 973 if (oldReplication != null) { 974 oldReplication[0] = oldRepl; 975 } 976 return fileNode.getBlocks(); 977 } 978 979 /** 980 * Get the blocksize of a file 981 * @param filename the filename 982 * @return the number of bytes 983 */ 984 long getPreferredBlockSize(String filename) throws UnresolvedLinkException, 985 FileNotFoundException, IOException { 986 readLock(); 987 try { 988 INode inode = rootDir.getNode(filename, false); 989 if (inode == null) { 990 throw new FileNotFoundException("File does not exist: " + filename); 991 } 992 if (inode.isDirectory() || inode.isLink()) { 993 throw new IOException("Getting block size of non-file: "+ filename); 994 } 995 return ((INodeFile)inode).getPreferredBlockSize(); 996 } finally { 997 readUnlock(); 998 } 999 } 1000 1001 boolean exists(String src) throws UnresolvedLinkException { 1002 src = normalizePath(src); 1003 readLock(); 1004 try { 1005 INode inode = rootDir.getNode(src, false); 1006 if (inode == null) { 1007 return false; 1008 } 1009 return inode.isDirectory() || inode.isLink() 1010 ? true 1011 : ((INodeFile)inode).getBlocks() != null; 1012 } finally { 1013 readUnlock(); 1014 } 1015 } 1016 1017 void setPermission(String src, FsPermission permission) 1018 throws FileNotFoundException, UnresolvedLinkException { 1019 writeLock(); 1020 try { 1021 unprotectedSetPermission(src, permission); 1022 } finally { 1023 writeUnlock(); 1024 } 1025 fsImage.getEditLog().logSetPermissions(src, permission); 1026 } 1027 1028 void unprotectedSetPermission(String src, FsPermission permissions) 1029 throws FileNotFoundException, UnresolvedLinkException { 1030 assert hasWriteLock(); 1031 INode inode = rootDir.getNode(src, true); 1032 if (inode == null) { 1033 throw new FileNotFoundException("File does not exist: " + src); 1034 } 1035 inode.setPermission(permissions); 1036 } 1037 1038 void setOwner(String src, String username, String groupname) 1039 throws FileNotFoundException, UnresolvedLinkException { 1040 writeLock(); 1041 try { 1042 unprotectedSetOwner(src, username, groupname); 1043 } finally { 1044 writeUnlock(); 1045 } 1046 fsImage.getEditLog().logSetOwner(src, username, groupname); 1047 } 1048 1049 void unprotectedSetOwner(String src, String username, String groupname) 1050 throws FileNotFoundException, UnresolvedLinkException { 1051 assert hasWriteLock(); 1052 INode inode = rootDir.getNode(src, true); 1053 if (inode == null) { 1054 throw new FileNotFoundException("File does not exist: " + src); 1055 } 1056 if (username != null) { 1057 inode.setUser(username); 1058 } 1059 if (groupname != null) { 1060 inode.setGroup(groupname); 1061 } 1062 } 1063 1064 /** 1065 * Concat all the blocks from srcs to trg and delete the srcs files 1066 */ 1067 public void concat(String target, String [] srcs) 1068 throws UnresolvedLinkException { 1069 writeLock(); 1070 try { 1071 // actual move 1072 waitForReady(); 1073 long timestamp = now(); 1074 unprotectedConcat(target, srcs, timestamp); 1075 // do the commit 1076 fsImage.getEditLog().logConcat(target, srcs, timestamp); 1077 } finally { 1078 writeUnlock(); 1079 } 1080 } 1081 1082 1083 1084 /** 1085 * Concat all the blocks from srcs to trg and delete the srcs files 1086 * @param target target file to move the blocks to 1087 * @param srcs list of file to move the blocks from 1088 * Must be public because also called from EditLogs 1089 * NOTE: - it does not update quota (not needed for concat) 1090 */ 1091 public void unprotectedConcat(String target, String [] srcs, long timestamp) 1092 throws UnresolvedLinkException { 1093 assert hasWriteLock(); 1094 if (NameNode.stateChangeLog.isDebugEnabled()) { 1095 NameNode.stateChangeLog.debug("DIR* FSNamesystem.concat to "+target); 1096 } 1097 // do the move 1098 1099 INode [] trgINodes = getExistingPathINodes(target); 1100 INodeFile trgInode = (INodeFile) trgINodes[trgINodes.length-1]; 1101 INodeDirectory trgParent = (INodeDirectory)trgINodes[trgINodes.length-2]; 1102 1103 INodeFile [] allSrcInodes = new INodeFile[srcs.length]; 1104 int i = 0; 1105 int totalBlocks = 0; 1106 for(String src : srcs) { 1107 INodeFile srcInode = getFileINode(src); 1108 allSrcInodes[i++] = srcInode; 1109 totalBlocks += srcInode.blocks.length; 1110 } 1111 trgInode.appendBlocks(allSrcInodes, totalBlocks); // copy the blocks 1112 1113 // since we are in the same dir - we can use same parent to remove files 1114 int count = 0; 1115 for(INodeFile nodeToRemove: allSrcInodes) { 1116 if(nodeToRemove == null) continue; 1117 1118 nodeToRemove.blocks = null; 1119 trgParent.removeChild(nodeToRemove); 1120 count++; 1121 } 1122 1123 trgInode.setModificationTimeForce(timestamp); 1124 trgParent.setModificationTime(timestamp); 1125 // update quota on the parent directory ('count' files removed, 0 space) 1126 unprotectedUpdateCount(trgINodes, trgINodes.length-1, - count, 0); 1127 } 1128 1129 /** 1130 * Delete the target directory and collect the blocks under it 1131 * 1132 * @param src Path of a directory to delete 1133 * @param collectedBlocks Blocks under the deleted directory 1134 * @return true on successful deletion; else false 1135 */ 1136 boolean delete(String src, List<Block>collectedBlocks) 1137 throws UnresolvedLinkException { 1138 if (NameNode.stateChangeLog.isDebugEnabled()) { 1139 NameNode.stateChangeLog.debug("DIR* FSDirectory.delete: " + src); 1140 } 1141 waitForReady(); 1142 long now = now(); 1143 int filesRemoved; 1144 writeLock(); 1145 try { 1146 filesRemoved = unprotectedDelete(src, collectedBlocks, now); 1147 } finally { 1148 writeUnlock(); 1149 } 1150 if (filesRemoved <= 0) { 1151 return false; 1152 } 1153 incrDeletedFileCount(filesRemoved); 1154 // Blocks will be deleted later by the caller of this method 1155 getFSNamesystem().removePathAndBlocks(src, null); 1156 fsImage.getEditLog().logDelete(src, now); 1157 return true; 1158 } 1159 1160 /** Return if a directory is empty or not **/ 1161 boolean isDirEmpty(String src) throws UnresolvedLinkException { 1162 boolean dirNotEmpty = true; 1163 if (!isDir(src)) { 1164 return true; 1165 } 1166 readLock(); 1167 try { 1168 INode targetNode = rootDir.getNode(src, false); 1169 assert targetNode != null : "should be taken care in isDir() above"; 1170 if (((INodeDirectory)targetNode).getChildren().size() != 0) { 1171 dirNotEmpty = false; 1172 } 1173 } finally { 1174 readUnlock(); 1175 } 1176 return dirNotEmpty; 1177 } 1178 1179 boolean isEmpty() { 1180 try { 1181 return isDirEmpty("/"); 1182 } catch (UnresolvedLinkException e) { 1183 if(NameNode.stateChangeLog.isDebugEnabled()) { 1184 NameNode.stateChangeLog.debug("/ cannot be a symlink"); 1185 } 1186 assert false : "/ cannot be a symlink"; 1187 return true; 1188 } 1189 } 1190 1191 /** 1192 * Delete a path from the name space 1193 * Update the count at each ancestor directory with quota 1194 * <br> 1195 * Note: This is to be used by {@link FSEditLog} only. 1196 * <br> 1197 * @param src a string representation of a path to an inode 1198 * @param mtime the time the inode is removed 1199 */ 1200 void unprotectedDelete(String src, long mtime) 1201 throws UnresolvedLinkException { 1202 assert hasWriteLock(); 1203 List<Block> collectedBlocks = new ArrayList<Block>(); 1204 int filesRemoved = unprotectedDelete(src, collectedBlocks, mtime); 1205 if (filesRemoved > 0) { 1206 getFSNamesystem().removePathAndBlocks(src, collectedBlocks); 1207 } 1208 } 1209 1210 /** 1211 * Delete a path from the name space 1212 * Update the count at each ancestor directory with quota 1213 * @param src a string representation of a path to an inode 1214 * @param collectedBlocks blocks collected from the deleted path 1215 * @param mtime the time the inode is removed 1216 * @return the number of inodes deleted; 0 if no inodes are deleted. 1217 */ 1218 int unprotectedDelete(String src, List<Block> collectedBlocks, 1219 long mtime) throws UnresolvedLinkException { 1220 assert hasWriteLock(); 1221 src = normalizePath(src); 1222 1223 INode[] inodes = rootDir.getExistingPathINodes(src, false); 1224 INode targetNode = inodes[inodes.length-1]; 1225 1226 if (targetNode == null) { // non-existent src 1227 if(NameNode.stateChangeLog.isDebugEnabled()) { 1228 NameNode.stateChangeLog.debug("DIR* FSDirectory.unprotectedDelete: " 1229 +"failed to remove "+src+" because it does not exist"); 1230 } 1231 return 0; 1232 } 1233 if (inodes.length == 1) { // src is the root 1234 NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedDelete: " + 1235 "failed to remove " + src + 1236 " because the root is not allowed to be deleted"); 1237 return 0; 1238 } 1239 int pos = inodes.length - 1; 1240 // Remove the node from the namespace 1241 targetNode = removeChild(inodes, pos); 1242 if (targetNode == null) { 1243 return 0; 1244 } 1245 // set the parent's modification time 1246 inodes[pos-1].setModificationTime(mtime); 1247 int filesRemoved = targetNode.collectSubtreeBlocksAndClear(collectedBlocks); 1248 if (NameNode.stateChangeLog.isDebugEnabled()) { 1249 NameNode.stateChangeLog.debug("DIR* FSDirectory.unprotectedDelete: " 1250 +src+" is removed"); 1251 } 1252 return filesRemoved; 1253 } 1254 1255 /** 1256 * Replaces the specified inode with the specified one. 1257 */ 1258 public void replaceNode(String path, INodeFile oldnode, INodeFile newnode) 1259 throws IOException, UnresolvedLinkException { 1260 writeLock(); 1261 try { 1262 // 1263 // Remove the node from the namespace 1264 // 1265 if (!oldnode.removeNode()) { 1266 NameNode.stateChangeLog.warn("DIR* FSDirectory.replaceNode: " + 1267 "failed to remove " + path); 1268 throw new IOException("FSDirectory.replaceNode: " + 1269 "failed to remove " + path); 1270 } 1271 1272 /* Currently oldnode and newnode are assumed to contain the same 1273 * blocks. Otherwise, blocks need to be removed from the blocksMap. 1274 */ 1275 rootDir.addNode(path, newnode); 1276 1277 int index = 0; 1278 for (BlockInfo b : newnode.getBlocks()) { 1279 BlockInfo info = getBlockManager().addINode(b, newnode); 1280 newnode.setBlock(index, info); // inode refers to the block in BlocksMap 1281 index++; 1282 } 1283 } finally { 1284 writeUnlock(); 1285 } 1286 } 1287 1288 /** 1289 * Get a partial listing of the indicated directory 1290 * 1291 * @param src the directory name 1292 * @param startAfter the name to start listing after 1293 * @param needLocation if block locations are returned 1294 * @return a partial listing starting after startAfter 1295 */ 1296 DirectoryListing getListing(String src, byte[] startAfter, 1297 boolean needLocation) throws UnresolvedLinkException, IOException { 1298 String srcs = normalizePath(src); 1299 1300 readLock(); 1301 try { 1302 INode targetNode = rootDir.getNode(srcs, true); 1303 if (targetNode == null) 1304 return null; 1305 1306 if (!targetNode.isDirectory()) { 1307 return new DirectoryListing( 1308 new HdfsFileStatus[]{createFileStatus(HdfsFileStatus.EMPTY_NAME, 1309 targetNode, needLocation)}, 0); 1310 } 1311 INodeDirectory dirInode = (INodeDirectory)targetNode; 1312 List<INode> contents = dirInode.getChildren(); 1313 int startChild = dirInode.nextChild(startAfter); 1314 int totalNumChildren = contents.size(); 1315 int numOfListing = Math.min(totalNumChildren-startChild, this.lsLimit); 1316 HdfsFileStatus listing[] = new HdfsFileStatus[numOfListing]; 1317 for (int i=0; i<numOfListing; i++) { 1318 INode cur = contents.get(startChild+i); 1319 listing[i] = createFileStatus(cur.name, cur, needLocation); 1320 } 1321 return new DirectoryListing( 1322 listing, totalNumChildren-startChild-numOfListing); 1323 } finally { 1324 readUnlock(); 1325 } 1326 } 1327 1328 /** Get the file info for a specific file. 1329 * @param src The string representation of the path to the file 1330 * @param resolveLink whether to throw UnresolvedLinkException 1331 * @return object containing information regarding the file 1332 * or null if file not found 1333 */ 1334 HdfsFileStatus getFileInfo(String src, boolean resolveLink) 1335 throws UnresolvedLinkException { 1336 String srcs = normalizePath(src); 1337 readLock(); 1338 try { 1339 INode targetNode = rootDir.getNode(srcs, resolveLink); 1340 if (targetNode == null) { 1341 return null; 1342 } 1343 else { 1344 return createFileStatus(HdfsFileStatus.EMPTY_NAME, targetNode); 1345 } 1346 } finally { 1347 readUnlock(); 1348 } 1349 } 1350 1351 /** 1352 * Get the blocks associated with the file. 1353 */ 1354 Block[] getFileBlocks(String src) throws UnresolvedLinkException { 1355 waitForReady(); 1356 readLock(); 1357 try { 1358 INode targetNode = rootDir.getNode(src, false); 1359 if (targetNode == null) 1360 return null; 1361 if (targetNode.isDirectory()) 1362 return null; 1363 if (targetNode.isLink()) 1364 return null; 1365 return ((INodeFile)targetNode).getBlocks(); 1366 } finally { 1367 readUnlock(); 1368 } 1369 } 1370 1371 /** 1372 * Get {@link INode} associated with the file. 1373 */ 1374 INodeFile getFileINode(String src) throws UnresolvedLinkException { 1375 INode inode = getINode(src); 1376 if (inode == null || inode.isDirectory()) 1377 return null; 1378 assert !inode.isLink(); 1379 return (INodeFile) inode; 1380 } 1381 1382 /** 1383 * Get {@link INode} associated with the file / directory. 1384 */ 1385 INode getINode(String src) throws UnresolvedLinkException { 1386 readLock(); 1387 try { 1388 INode iNode = rootDir.getNode(src, true); 1389 return iNode; 1390 } finally { 1391 readUnlock(); 1392 } 1393 } 1394 1395 /** 1396 * Retrieve the existing INodes along the given path. 1397 * 1398 * @param path the path to explore 1399 * @return INodes array containing the existing INodes in the order they 1400 * appear when following the path from the root INode to the 1401 * deepest INodes. The array size will be the number of expected 1402 * components in the path, and non existing components will be 1403 * filled with null 1404 * 1405 * @see INodeDirectory#getExistingPathINodes(byte[][], INode[]) 1406 */ 1407 INode[] getExistingPathINodes(String path) 1408 throws UnresolvedLinkException { 1409 readLock(); 1410 try { 1411 return rootDir.getExistingPathINodes(path, true); 1412 } finally { 1413 readUnlock(); 1414 } 1415 } 1416 1417 /** 1418 * Get the parent node of path. 1419 * 1420 * @param path the path to explore 1421 * @return its parent node 1422 */ 1423 INodeDirectory getParent(byte[][] path) 1424 throws FileNotFoundException, UnresolvedLinkException { 1425 readLock(); 1426 try { 1427 return rootDir.getParent(path); 1428 } finally { 1429 readUnlock(); 1430 } 1431 } 1432 1433 /** 1434 * Check whether the filepath could be created 1435 */ 1436 boolean isValidToCreate(String src) throws UnresolvedLinkException { 1437 String srcs = normalizePath(src); 1438 readLock(); 1439 try { 1440 if (srcs.startsWith("/") && 1441 !srcs.endsWith("/") && 1442 rootDir.getNode(srcs, false) == null) { 1443 return true; 1444 } else { 1445 return false; 1446 } 1447 } finally { 1448 readUnlock(); 1449 } 1450 } 1451 1452 /** 1453 * Check whether the path specifies a directory 1454 */ 1455 boolean isDir(String src) throws UnresolvedLinkException { 1456 src = normalizePath(src); 1457 readLock(); 1458 try { 1459 INode node = rootDir.getNode(src, false); 1460 return node != null && node.isDirectory(); 1461 } finally { 1462 readUnlock(); 1463 } 1464 } 1465 1466 /** Updates namespace and diskspace consumed for all 1467 * directories until the parent directory of file represented by path. 1468 * 1469 * @param path path for the file. 1470 * @param nsDelta the delta change of namespace 1471 * @param dsDelta the delta change of diskspace 1472 * @throws QuotaExceededException if the new count violates any quota limit 1473 * @throws FileNotFound if path does not exist. 1474 */ 1475 void updateSpaceConsumed(String path, long nsDelta, long dsDelta) 1476 throws QuotaExceededException, 1477 FileNotFoundException, 1478 UnresolvedLinkException { 1479 writeLock(); 1480 try { 1481 INode[] inodes = rootDir.getExistingPathINodes(path, false); 1482 int len = inodes.length; 1483 if (inodes[len - 1] == null) { 1484 throw new FileNotFoundException(path + 1485 " does not exist under rootDir."); 1486 } 1487 updateCount(inodes, len-1, nsDelta, dsDelta, true); 1488 } finally { 1489 writeUnlock(); 1490 } 1491 } 1492 1493 /** update count of each inode with quota 1494 * 1495 * @param inodes an array of inodes on a path 1496 * @param numOfINodes the number of inodes to update starting from index 0 1497 * @param nsDelta the delta change of namespace 1498 * @param dsDelta the delta change of diskspace 1499 * @param checkQuota if true then check if quota is exceeded 1500 * @throws QuotaExceededException if the new count violates any quota limit 1501 */ 1502 private void updateCount(INode[] inodes, int numOfINodes, 1503 long nsDelta, long dsDelta, boolean checkQuota) 1504 throws QuotaExceededException { 1505 assert hasWriteLock(); 1506 if (!ready) { 1507 //still initializing. do not check or update quotas. 1508 return; 1509 } 1510 if (numOfINodes>inodes.length) { 1511 numOfINodes = inodes.length; 1512 } 1513 if (checkQuota) { 1514 verifyQuota(inodes, numOfINodes, nsDelta, dsDelta, null); 1515 } 1516 for(int i = 0; i < numOfINodes; i++) { 1517 if (inodes[i].isQuotaSet()) { // a directory with quota 1518 INodeDirectoryWithQuota node =(INodeDirectoryWithQuota)inodes[i]; 1519 node.updateNumItemsInTree(nsDelta, dsDelta); 1520 } 1521 } 1522 } 1523 1524 /** 1525 * update quota of each inode and check to see if quota is exceeded. 1526 * See {@link #updateCount(INode[], int, long, long, boolean)} 1527 */ 1528 private void updateCountNoQuotaCheck(INode[] inodes, int numOfINodes, 1529 long nsDelta, long dsDelta) { 1530 assert hasWriteLock(); 1531 try { 1532 updateCount(inodes, numOfINodes, nsDelta, dsDelta, false); 1533 } catch (QuotaExceededException e) { 1534 NameNode.LOG.warn("FSDirectory.updateCountNoQuotaCheck - unexpected ", e); 1535 } 1536 } 1537 1538 /** 1539 * updates quota without verification 1540 * callers responsibility is to make sure quota is not exceeded 1541 * @param inodes 1542 * @param numOfINodes 1543 * @param nsDelta 1544 * @param dsDelta 1545 */ 1546 void unprotectedUpdateCount(INode[] inodes, int numOfINodes, 1547 long nsDelta, long dsDelta) { 1548 assert hasWriteLock(); 1549 for(int i=0; i < numOfINodes; i++) { 1550 if (inodes[i].isQuotaSet()) { // a directory with quota 1551 INodeDirectoryWithQuota node =(INodeDirectoryWithQuota)inodes[i]; 1552 node.unprotectedUpdateNumItemsInTree(nsDelta, dsDelta); 1553 } 1554 } 1555 } 1556 1557 /** Return the name of the path represented by inodes at [0, pos] */ 1558 private static String getFullPathName(INode[] inodes, int pos) { 1559 StringBuilder fullPathName = new StringBuilder(); 1560 if (inodes[0].isRoot()) { 1561 if (pos == 0) return Path.SEPARATOR; 1562 } else { 1563 fullPathName.append(inodes[0].getLocalName()); 1564 } 1565 1566 for (int i=1; i<=pos; i++) { 1567 fullPathName.append(Path.SEPARATOR_CHAR).append(inodes[i].getLocalName()); 1568 } 1569 return fullPathName.toString(); 1570 } 1571 1572 /** Return the full path name of the specified inode */ 1573 static String getFullPathName(INode inode) { 1574 // calculate the depth of this inode from root 1575 int depth = 0; 1576 for (INode i = inode; i != null; i = i.parent) { 1577 depth++; 1578 } 1579 INode[] inodes = new INode[depth]; 1580 1581 // fill up the inodes in the path from this inode to root 1582 for (int i = 0; i < depth; i++) { 1583 if (inode == null) { 1584 NameNode.stateChangeLog.warn("Could not get full path." 1585 + " Corresponding file might have deleted already."); 1586 return null; 1587 } 1588 inodes[depth-i-1] = inode; 1589 inode = inode.parent; 1590 } 1591 return getFullPathName(inodes, depth-1); 1592 } 1593 1594 /** 1595 * Create a directory 1596 * If ancestor directories do not exist, automatically create them. 1597 1598 * @param src string representation of the path to the directory 1599 * @param permissions the permission of the directory 1600 * @param isAutocreate if the permission of the directory should inherit 1601 * from its parent or not. u+wx is implicitly added to 1602 * the automatically created directories, and to the 1603 * given directory if inheritPermission is true 1604 * @param now creation time 1605 * @return true if the operation succeeds false otherwise 1606 * @throws FileNotFoundException if an ancestor or itself is a file 1607 * @throws QuotaExceededException if directory creation violates 1608 * any quota limit 1609 * @throws UnresolvedLinkException if a symlink is encountered in src. 1610 */ 1611 boolean mkdirs(String src, PermissionStatus permissions, 1612 boolean inheritPermission, long now) 1613 throws FileAlreadyExistsException, QuotaExceededException, 1614 UnresolvedLinkException { 1615 src = normalizePath(src); 1616 String[] names = INode.getPathNames(src); 1617 byte[][] components = INode.getPathComponents(names); 1618 INode[] inodes = new INode[components.length]; 1619 final int lastInodeIndex = inodes.length - 1; 1620 1621 writeLock(); 1622 try { 1623 rootDir.getExistingPathINodes(components, inodes, false); 1624 1625 // find the index of the first null in inodes[] 1626 StringBuilder pathbuilder = new StringBuilder(); 1627 int i = 1; 1628 for(; i < inodes.length && inodes[i] != null; i++) { 1629 pathbuilder.append(Path.SEPARATOR + names[i]); 1630 if (!inodes[i].isDirectory()) { 1631 throw new FileAlreadyExistsException("Parent path is not a directory: " 1632 + pathbuilder+ " "+inodes[i].getLocalName()); 1633 } 1634 } 1635 1636 // default to creating parent dirs with the given perms 1637 PermissionStatus parentPermissions = permissions; 1638 1639 // if not inheriting and it's the last inode, there's no use in 1640 // computing perms that won't be used 1641 if (inheritPermission || (i < lastInodeIndex)) { 1642 // if inheriting (ie. creating a file or symlink), use the parent dir, 1643 // else the supplied permissions 1644 // NOTE: the permissions of the auto-created directories violate posix 1645 FsPermission parentFsPerm = inheritPermission 1646 ? inodes[i-1].getFsPermission() : permissions.getPermission(); 1647 1648 // ensure that the permissions allow user write+execute 1649 if (!parentFsPerm.getUserAction().implies(FsAction.WRITE_EXECUTE)) { 1650 parentFsPerm = new FsPermission( 1651 parentFsPerm.getUserAction().or(FsAction.WRITE_EXECUTE), 1652 parentFsPerm.getGroupAction(), 1653 parentFsPerm.getOtherAction() 1654 ); 1655 } 1656 1657 if (!parentPermissions.getPermission().equals(parentFsPerm)) { 1658 parentPermissions = new PermissionStatus( 1659 parentPermissions.getUserName(), 1660 parentPermissions.getGroupName(), 1661 parentFsPerm 1662 ); 1663 // when inheriting, use same perms for entire path 1664 if (inheritPermission) permissions = parentPermissions; 1665 } 1666 } 1667 1668 // create directories beginning from the first null index 1669 for(; i < inodes.length; i++) { 1670 pathbuilder.append(Path.SEPARATOR + names[i]); 1671 String cur = pathbuilder.toString(); 1672 unprotectedMkdir(inodes, i, components[i], 1673 (i < lastInodeIndex) ? parentPermissions : permissions, now); 1674 if (inodes[i] == null) { 1675 return false; 1676 } 1677 // Directory creation also count towards FilesCreated 1678 // to match count of FilesDeleted metric. 1679 if (getFSNamesystem() != null) 1680 NameNode.getNameNodeMetrics().incrFilesCreated(); 1681 fsImage.getEditLog().logMkDir(cur, inodes[i]); 1682 if(NameNode.stateChangeLog.isDebugEnabled()) { 1683 NameNode.stateChangeLog.debug( 1684 "DIR* FSDirectory.mkdirs: created directory " + cur); 1685 } 1686 } 1687 } finally { 1688 writeUnlock(); 1689 } 1690 return true; 1691 } 1692 1693 /** 1694 */ 1695 INode unprotectedMkdir(String src, PermissionStatus permissions, 1696 long timestamp) throws QuotaExceededException, 1697 UnresolvedLinkException { 1698 assert hasWriteLock(); 1699 byte[][] components = INode.getPathComponents(src); 1700 INode[] inodes = new INode[components.length]; 1701 1702 rootDir.getExistingPathINodes(components, inodes, false); 1703 unprotectedMkdir(inodes, inodes.length-1, components[inodes.length-1], 1704 permissions, timestamp); 1705 return inodes[inodes.length-1]; 1706 } 1707 1708 /** create a directory at index pos. 1709 * The parent path to the directory is at [0, pos-1]. 1710 * All ancestors exist. Newly created one stored at index pos. 1711 */ 1712 private void unprotectedMkdir(INode[] inodes, int pos, 1713 byte[] name, PermissionStatus permission, 1714 long timestamp) throws QuotaExceededException { 1715 assert hasWriteLock(); 1716 inodes[pos] = addChild(inodes, pos, 1717 new INodeDirectory(name, permission, timestamp), 1718 -1); 1719 } 1720 1721 /** Add a node child to the namespace. The full path name of the node is src. 1722 * childDiskspace should be -1, if unknown. 1723 * QuotaExceededException is thrown if it violates quota limit */ 1724 private <T extends INode> T addNode(String src, T child, 1725 long childDiskspace) 1726 throws QuotaExceededException, UnresolvedLinkException { 1727 byte[][] components = INode.getPathComponents(src); 1728 byte[] path = components[components.length-1]; 1729 child.setLocalName(path); 1730 cacheName(child); 1731 INode[] inodes = new INode[components.length]; 1732 writeLock(); 1733 try { 1734 rootDir.getExistingPathINodes(components, inodes, false); 1735 return addChild(inodes, inodes.length-1, child, childDiskspace); 1736 } finally { 1737 writeUnlock(); 1738 } 1739 } 1740 1741 /** 1742 * Verify quota for adding or moving a new INode with required 1743 * namespace and diskspace to a given position. 1744 * 1745 * @param inodes INodes corresponding to a path 1746 * @param pos position where a new INode will be added 1747 * @param nsDelta needed namespace 1748 * @param dsDelta needed diskspace 1749 * @param commonAncestor Last node in inodes array that is a common ancestor 1750 * for a INode that is being moved from one location to the other. 1751 * Pass null if a node is not being moved. 1752 * @throws QuotaExceededException if quota limit is exceeded. 1753 */ 1754 private void verifyQuota(INode[] inodes, int pos, long nsDelta, long dsDelta, 1755 INode commonAncestor) throws QuotaExceededException { 1756 if (!ready) { 1757 // Do not check quota if edits log is still being processed 1758 return; 1759 } 1760 if (nsDelta <= 0 && dsDelta <= 0) { 1761 // if quota is being freed or not being consumed 1762 return; 1763 } 1764 if (pos>inodes.length) { 1765 pos = inodes.length; 1766 } 1767 int i = pos - 1; 1768 try { 1769 // check existing components in the path 1770 for(; i >= 0; i--) { 1771 if (commonAncestor == inodes[i]) { 1772 // Moving an existing node. Stop checking for quota when common 1773 // ancestor is reached 1774 return; 1775 } 1776 if (inodes[i].isQuotaSet()) { // a directory with quota 1777 INodeDirectoryWithQuota node =(INodeDirectoryWithQuota)inodes[i]; 1778 node.verifyQuota(nsDelta, dsDelta); 1779 } 1780 } 1781 } catch (QuotaExceededException e) { 1782 e.setPathName(getFullPathName(inodes, i)); 1783 throw e; 1784 } 1785 } 1786 1787 /** 1788 * Verify quota for rename operation where srcInodes[srcInodes.length-1] moves 1789 * dstInodes[dstInodes.length-1] 1790 * 1791 * @param srcInodes directory from where node is being moved. 1792 * @param dstInodes directory to where node is moved to. 1793 * @throws QuotaExceededException if quota limit is exceeded. 1794 */ 1795 private void verifyQuotaForRename(INode[] srcInodes, INode[]dstInodes) 1796 throws QuotaExceededException { 1797 if (!ready) { 1798 // Do not check quota if edits log is still being processed 1799 return; 1800 } 1801 INode srcInode = srcInodes[srcInodes.length - 1]; 1802 INode commonAncestor = null; 1803 for(int i =0;srcInodes[i] == dstInodes[i]; i++) { 1804 commonAncestor = srcInodes[i]; 1805 } 1806 INode.DirCounts srcCounts = new INode.DirCounts(); 1807 srcInode.spaceConsumedInTree(srcCounts); 1808 long nsDelta = srcCounts.getNsCount(); 1809 long dsDelta = srcCounts.getDsCount(); 1810 1811 // Reduce the required quota by dst that is being removed 1812 INode dstInode = dstInodes[dstInodes.length - 1]; 1813 if (dstInode != null) { 1814 INode.DirCounts dstCounts = new INode.DirCounts(); 1815 dstInode.spaceConsumedInTree(dstCounts); 1816 nsDelta -= dstCounts.getNsCount(); 1817 dsDelta -= dstCounts.getDsCount(); 1818 } 1819 verifyQuota(dstInodes, dstInodes.length - 1, nsDelta, dsDelta, 1820 commonAncestor); 1821 } 1822 1823 /** 1824 * Verify that filesystem limit constraints are not violated 1825 * @throws PathComponentTooLongException child's name is too long 1826 * @throws MaxDirectoryItemsExceededException items per directory is exceeded 1827 */ 1828 protected <T extends INode> void verifyFsLimits(INode[] pathComponents, 1829 int pos, T child) throws FSLimitException { 1830 boolean includeChildName = false; 1831 try { 1832 if (maxComponentLength != 0) { 1833 int length = child.getLocalName().length(); 1834 if (length > maxComponentLength) { 1835 includeChildName = true; 1836 throw new PathComponentTooLongException(maxComponentLength, length); 1837 } 1838 } 1839 if (maxDirItems != 0) { 1840 INodeDirectory parent = (INodeDirectory)pathComponents[pos-1]; 1841 int count = parent.getChildren().size(); 1842 if (count >= maxDirItems) { 1843 throw new MaxDirectoryItemsExceededException(maxDirItems, count); 1844 } 1845 } 1846 } catch (FSLimitException e) { 1847 String badPath = getFullPathName(pathComponents, pos-1); 1848 if (includeChildName) { 1849 badPath += Path.SEPARATOR + child.getLocalName(); 1850 } 1851 e.setPathName(badPath); 1852 // Do not throw if edits log is still being processed 1853 if (ready) throw(e); 1854 // log pre-existing paths that exceed limits 1855 NameNode.LOG.error("FSDirectory.verifyFsLimits - " + e.getLocalizedMessage()); 1856 } 1857 } 1858 1859 /** Add a node child to the inodes at index pos. 1860 * Its ancestors are stored at [0, pos-1]. 1861 * QuotaExceededException is thrown if it violates quota limit */ 1862 private <T extends INode> T addChild(INode[] pathComponents, int pos, 1863 T child, long childDiskspace, 1864 boolean checkQuota) throws QuotaExceededException { 1865 // The filesystem limits are not really quotas, so this check may appear 1866 // odd. It's because a rename operation deletes the src, tries to add 1867 // to the dest, if that fails, re-adds the src from whence it came. 1868 // The rename code disables the quota when it's restoring to the 1869 // original location becase a quota violation would cause the the item 1870 // to go "poof". The fs limits must be bypassed for the same reason. 1871 if (checkQuota) { 1872 verifyFsLimits(pathComponents, pos, child); 1873 } 1874 1875 INode.DirCounts counts = new INode.DirCounts(); 1876 child.spaceConsumedInTree(counts); 1877 if (childDiskspace < 0) { 1878 childDiskspace = counts.getDsCount(); 1879 } 1880 updateCount(pathComponents, pos, counts.getNsCount(), childDiskspace, 1881 checkQuota); 1882 if (pathComponents[pos-1] == null) { 1883 throw new NullPointerException("Panic: parent does not exist"); 1884 } 1885 T addedNode = ((INodeDirectory)pathComponents[pos-1]).addChild( 1886 child, true); 1887 if (addedNode == null) { 1888 updateCount(pathComponents, pos, -counts.getNsCount(), 1889 -childDiskspace, true); 1890 } 1891 return addedNode; 1892 } 1893 1894 private <T extends INode> T addChild(INode[] pathComponents, int pos, 1895 T child, long childDiskspace) 1896 throws QuotaExceededException { 1897 return addChild(pathComponents, pos, child, childDiskspace, true); 1898 } 1899 1900 private <T extends INode> T addChildNoQuotaCheck(INode[] pathComponents, 1901 int pos, T child, long childDiskspace) { 1902 T inode = null; 1903 try { 1904 inode = addChild(pathComponents, pos, child, childDiskspace, false); 1905 } catch (QuotaExceededException e) { 1906 NameNode.LOG.warn("FSDirectory.addChildNoQuotaCheck - unexpected", e); 1907 } 1908 return inode; 1909 } 1910 1911 /** Remove an inode at index pos from the namespace. 1912 * Its ancestors are stored at [0, pos-1]. 1913 * Count of each ancestor with quota is also updated. 1914 * Return the removed node; null if the removal fails. 1915 */ 1916 private INode removeChild(INode[] pathComponents, int pos) { 1917 INode removedNode = 1918 ((INodeDirectory)pathComponents[pos-1]).removeChild(pathComponents[pos]); 1919 if (removedNode != null) { 1920 INode.DirCounts counts = new INode.DirCounts(); 1921 removedNode.spaceConsumedInTree(counts); 1922 updateCountNoQuotaCheck(pathComponents, pos, 1923 -counts.getNsCount(), -counts.getDsCount()); 1924 } 1925 return removedNode; 1926 } 1927 1928 /** 1929 */ 1930 String normalizePath(String src) { 1931 if (src.length() > 1 && src.endsWith("/")) { 1932 src = src.substring(0, src.length() - 1); 1933 } 1934 return src; 1935 } 1936 1937 ContentSummary getContentSummary(String src) 1938 throws FileNotFoundException, UnresolvedLinkException { 1939 String srcs = normalizePath(src); 1940 readLock(); 1941 try { 1942 INode targetNode = rootDir.getNode(srcs, false); 1943 if (targetNode == null) { 1944 throw new FileNotFoundException("File does not exist: " + srcs); 1945 } 1946 else { 1947 // Make it relinquish locks everytime contentCountLimit entries are 1948 // processed. 0 means disabled. I.e. blocking for the entire duration. 1949 return targetNode.computeAndConvertContentSummary( 1950 new ContentSummaryComputationContext(this, getFSNamesystem(), 1951 contentCountLimit)); 1952 } 1953 } finally { 1954 readUnlock(); 1955 } 1956 } 1957 1958 /** Update the count of each directory with quota in the namespace 1959 * A directory's count is defined as the total number inodes in the tree 1960 * rooted at the directory. 1961 * 1962 * This is an update of existing state of the filesystem and does not 1963 * throw QuotaExceededException. 1964 */ 1965 void updateCountForINodeWithQuota() { 1966 updateCountForINodeWithQuota(rootDir, new INode.DirCounts(), 1967 new ArrayList<INode>(50)); 1968 } 1969 1970 /** 1971 * Update the count of the directory if it has a quota and return the count 1972 * 1973 * This does not throw a QuotaExceededException. This is just an update 1974 * of of existing state and throwing QuotaExceededException does not help 1975 * with fixing the state, if there is a problem. 1976 * 1977 * @param dir the root of the tree that represents the directory 1978 * @param counters counters for name space and disk space 1979 * @param nodesInPath INodes for the each of components in the path. 1980 */ 1981 private static void updateCountForINodeWithQuota(INodeDirectory dir, 1982 INode.DirCounts counts, 1983 ArrayList<INode> nodesInPath) { 1984 long parentNamespace = counts.nsCount; 1985 long parentDiskspace = counts.dsCount; 1986 1987 counts.nsCount = 1L;//for self. should not call node.spaceConsumedInTree() 1988 counts.dsCount = 0L; 1989 1990 /* We don't need nodesInPath if we could use 'parent' field in 1991 * INode. using 'parent' is not currently recommended. */ 1992 nodesInPath.add(dir); 1993 1994 for (INode child : dir.getChildren()) { 1995 if (child.isDirectory()) { 1996 updateCountForINodeWithQuota((INodeDirectory)child, 1997 counts, nodesInPath); 1998 } else if (child.isLink()) { 1999 counts.nsCount += 1; 2000 } else { // reduce recursive calls 2001 counts.nsCount += 1; 2002 counts.dsCount += ((INodeFile)child).diskspaceConsumed(); 2003 } 2004 } 2005 2006 if (dir.isQuotaSet()) { 2007 ((INodeDirectoryWithQuota)dir).setSpaceConsumed(counts.nsCount, 2008 counts.dsCount); 2009 2010 // check if quota is violated for some reason. 2011 if ((dir.getNsQuota() >= 0 && counts.nsCount > dir.getNsQuota()) || 2012 (dir.getDsQuota() >= 0 && counts.dsCount > dir.getDsQuota())) { 2013 2014 // can only happen because of a software bug. the bug should be fixed. 2015 StringBuilder path = new StringBuilder(512); 2016 for (INode n : nodesInPath) { 2017 path.append('/'); 2018 path.append(n.getLocalName()); 2019 } 2020 2021 NameNode.LOG.warn("Quota violation in image for " + path + 2022 " (Namespace quota : " + dir.getNsQuota() + 2023 " consumed : " + counts.nsCount + ")" + 2024 " (Diskspace quota : " + dir.getDsQuota() + 2025 " consumed : " + counts.dsCount + ")."); 2026 } 2027 } 2028 2029 // pop 2030 nodesInPath.remove(nodesInPath.size()-1); 2031 2032 counts.nsCount += parentNamespace; 2033 counts.dsCount += parentDiskspace; 2034 } 2035 2036 /** 2037 * See {@link ClientProtocol#setQuota(String, long, long)} for the contract. 2038 * Sets quota for for a directory. 2039 * @returns INodeDirectory if any of the quotas have changed. null other wise. 2040 * @throws FileNotFoundException if the path does not exist or is a file 2041 * @throws QuotaExceededException if the directory tree size is 2042 * greater than the given quota 2043 * @throws UnresolvedLinkException if a symlink is encountered in src. 2044 */ 2045 INodeDirectory unprotectedSetQuota(String src, long nsQuota, long dsQuota) 2046 throws FileNotFoundException, QuotaExceededException, 2047 UnresolvedLinkException { 2048 assert hasWriteLock(); 2049 // sanity check 2050 if ((nsQuota < 0 && nsQuota != HdfsConstants.QUOTA_DONT_SET && 2051 nsQuota < HdfsConstants.QUOTA_RESET) || 2052 (dsQuota < 0 && dsQuota != HdfsConstants.QUOTA_DONT_SET && 2053 dsQuota < HdfsConstants.QUOTA_RESET)) { 2054 throw new IllegalArgumentException("Illegal value for nsQuota or " + 2055 "dsQuota : " + nsQuota + " and " + 2056 dsQuota); 2057 } 2058 2059 String srcs = normalizePath(src); 2060 2061 INode[] inodes = rootDir.getExistingPathINodes(src, true); 2062 INode targetNode = inodes[inodes.length-1]; 2063 if (targetNode == null) { 2064 throw new FileNotFoundException("Directory does not exist: " + srcs); 2065 } else if (!targetNode.isDirectory()) { 2066 throw new FileNotFoundException("Cannot set quota on a file: " + srcs); 2067 } else if (targetNode.isRoot() && nsQuota == HdfsConstants.QUOTA_RESET) { 2068 throw new IllegalArgumentException("Cannot clear namespace quota on root."); 2069 } else { // a directory inode 2070 INodeDirectory dirNode = (INodeDirectory)targetNode; 2071 long oldNsQuota = dirNode.getNsQuota(); 2072 long oldDsQuota = dirNode.getDsQuota(); 2073 if (nsQuota == HdfsConstants.QUOTA_DONT_SET) { 2074 nsQuota = oldNsQuota; 2075 } 2076 if (dsQuota == HdfsConstants.QUOTA_DONT_SET) { 2077 dsQuota = oldDsQuota; 2078 } 2079 2080 if (dirNode instanceof INodeDirectoryWithQuota) { 2081 // a directory with quota; so set the quota to the new value 2082 ((INodeDirectoryWithQuota)dirNode).setQuota(nsQuota, dsQuota); 2083 if (!dirNode.isQuotaSet()) { 2084 // will not come here for root because root's nsQuota is always set 2085 INodeDirectory newNode = new INodeDirectory(dirNode); 2086 INodeDirectory parent = (INodeDirectory)inodes[inodes.length-2]; 2087 dirNode = newNode; 2088 parent.replaceChild(newNode); 2089 } 2090 } else { 2091 // a non-quota directory; so replace it with a directory with quota 2092 INodeDirectoryWithQuota newNode = 2093 new INodeDirectoryWithQuota(nsQuota, dsQuota, dirNode); 2094 // non-root directory node; parent != null 2095 INodeDirectory parent = (INodeDirectory)inodes[inodes.length-2]; 2096 dirNode = newNode; 2097 parent.replaceChild(newNode); 2098 } 2099 return (oldNsQuota != nsQuota || oldDsQuota != dsQuota) ? dirNode : null; 2100 } 2101 } 2102 2103 /** 2104 * See {@link ClientProtocol#setQuota(String, long, long)} for the 2105 * contract. 2106 * @see #unprotectedSetQuota(String, long, long) 2107 */ 2108 void setQuota(String src, long nsQuota, long dsQuota) 2109 throws FileNotFoundException, QuotaExceededException, 2110 UnresolvedLinkException { 2111 writeLock(); 2112 try { 2113 INodeDirectory dir = unprotectedSetQuota(src, nsQuota, dsQuota); 2114 if (dir != null) { 2115 fsImage.getEditLog().logSetQuota(src, dir.getNsQuota(), 2116 dir.getDsQuota()); 2117 } 2118 } finally { 2119 writeUnlock(); 2120 } 2121 } 2122 2123 long totalInodes() { 2124 readLock(); 2125 try { 2126 return rootDir.numItemsInTree(); 2127 } finally { 2128 readUnlock(); 2129 } 2130 } 2131 2132 /** 2133 * Sets the access time on the file/directory. Logs it in the transaction log. 2134 */ 2135 void setTimes(String src, INode inode, long mtime, long atime, boolean force) { 2136 boolean status = false; 2137 writeLock(); 2138 try { 2139 status = unprotectedSetTimes(src, inode, mtime, atime, force); 2140 } finally { 2141 writeUnlock(); 2142 } 2143 if (status) { 2144 fsImage.getEditLog().logTimes(src, mtime, atime); 2145 } 2146 } 2147 2148 boolean unprotectedSetTimes(String src, long mtime, long atime, boolean force) 2149 throws UnresolvedLinkException { 2150 assert hasWriteLock(); 2151 INode inode = getINode(src); 2152 return unprotectedSetTimes(src, inode, mtime, atime, force); 2153 } 2154 2155 private boolean unprotectedSetTimes(String src, INode inode, long mtime, 2156 long atime, boolean force) { 2157 assert hasWriteLock(); 2158 boolean status = false; 2159 if (mtime != -1) { 2160 inode.setModificationTimeForce(mtime); 2161 status = true; 2162 } 2163 if (atime != -1) { 2164 long inodeTime = inode.getAccessTime(); 2165 2166 // if the last access time update was within the last precision interval, then 2167 // no need to store access time 2168 if (atime <= inodeTime + getFSNamesystem().getAccessTimePrecision() && !force) { 2169 status = false; 2170 } else { 2171 inode.setAccessTime(atime); 2172 status = true; 2173 } 2174 } 2175 return status; 2176 } 2177 2178 /** 2179 * Reset the entire namespace tree. 2180 */ 2181 void reset() { 2182 writeLock(); 2183 try { 2184 setReady(false); 2185 rootDir = new INodeDirectoryWithQuota(INodeDirectory.ROOT_NAME, 2186 getFSNamesystem().createFsOwnerPermissions(new FsPermission((short)0755)), 2187 Integer.MAX_VALUE, -1); 2188 nameCache.reset(); 2189 } finally { 2190 writeUnlock(); 2191 } 2192 } 2193 2194 /** 2195 * create an hdfs file status from an inode 2196 * 2197 * @param path the local name 2198 * @param node inode 2199 * @param needLocation if block locations need to be included or not 2200 * @return a file status 2201 * @throws IOException if any error occurs 2202 */ 2203 private HdfsFileStatus createFileStatus(byte[] path, INode node, 2204 boolean needLocation) throws IOException { 2205 if (needLocation) { 2206 return createLocatedFileStatus(path, node); 2207 } else { 2208 return createFileStatus(path, node); 2209 } 2210 } 2211 /** 2212 * Create FileStatus by file INode 2213 */ 2214 private HdfsFileStatus createFileStatus(byte[] path, INode node) { 2215 long size = 0; // length is zero for directories 2216 short replication = 0; 2217 long blocksize = 0; 2218 if (node instanceof INodeFile) { 2219 INodeFile fileNode = (INodeFile)node; 2220 size = fileNode.computeFileSize(true); 2221 replication = fileNode.getReplication(); 2222 blocksize = fileNode.getPreferredBlockSize(); 2223 } 2224 return new HdfsFileStatus( 2225 size, 2226 node.isDirectory(), 2227 replication, 2228 blocksize, 2229 node.getModificationTime(), 2230 node.getAccessTime(), 2231 node.getFsPermission(), 2232 node.getUserName(), 2233 node.getGroupName(), 2234 node.isLink() ? ((INodeSymlink)node).getSymlink() : null, 2235 path); 2236 } 2237 2238 /** 2239 * Create FileStatus with location info by file INode 2240 */ 2241 private HdfsLocatedFileStatus createLocatedFileStatus( 2242 byte[] path, INode node) throws IOException { 2243 assert hasReadLock(); 2244 long size = 0; // length is zero for directories 2245 short replication = 0; 2246 long blocksize = 0; 2247 LocatedBlocks loc = null; 2248 if (node instanceof INodeFile) { 2249 INodeFile fileNode = (INodeFile)node; 2250 size = fileNode.computeFileSize(true); 2251 replication = fileNode.getReplication(); 2252 blocksize = fileNode.getPreferredBlockSize(); 2253 loc = getFSNamesystem().getBlockManager().createLocatedBlocks( 2254 fileNode.getBlocks(), fileNode.computeFileSize(false), 2255 fileNode.isUnderConstruction(), 0L, size, false); 2256 if (loc==null) { 2257 loc = new LocatedBlocks(); 2258 } 2259 } 2260 return new HdfsLocatedFileStatus( 2261 size, 2262 node.isDirectory(), 2263 replication, 2264 blocksize, 2265 node.getModificationTime(), 2266 node.getAccessTime(), 2267 node.getFsPermission(), 2268 node.getUserName(), 2269 node.getGroupName(), 2270 node.isLink() ? ((INodeSymlink)node).getSymlink() : null, 2271 path, 2272 loc); 2273 } 2274 2275 2276 /** 2277 * Add the given symbolic link to the fs. Record it in the edits log. 2278 */ 2279 INodeSymlink addSymlink(String path, String target, 2280 PermissionStatus dirPerms, boolean createParent) 2281 throws UnresolvedLinkException, FileAlreadyExistsException, 2282 QuotaExceededException, IOException { 2283 waitForReady(); 2284 2285 final long modTime = now(); 2286 if (createParent) { 2287 final String parent = new Path(path).getParent().toString(); 2288 if (!mkdirs(parent, dirPerms, true, modTime)) { 2289 return null; 2290 } 2291 } 2292 final String userName = dirPerms.getUserName(); 2293 INodeSymlink newNode = null; 2294 writeLock(); 2295 try { 2296 newNode = unprotectedSymlink(path, target, modTime, modTime, 2297 new PermissionStatus(userName, null, FsPermission.getDefault())); 2298 } finally { 2299 writeUnlock(); 2300 } 2301 if (newNode == null) { 2302 NameNode.stateChangeLog.info("DIR* FSDirectory.addSymlink: " 2303 +"failed to add "+path 2304 +" to the file system"); 2305 return null; 2306 } 2307 fsImage.getEditLog().logSymlink(path, target, modTime, modTime, newNode); 2308 2309 if(NameNode.stateChangeLog.isDebugEnabled()) { 2310 NameNode.stateChangeLog.debug("DIR* FSDirectory.addSymlink: " 2311 +path+" is added to the file system"); 2312 } 2313 return newNode; 2314 } 2315 2316 /** 2317 * Add the specified path into the namespace. Invoked from edit log processing. 2318 */ 2319 INodeSymlink unprotectedSymlink(String path, String target, long modTime, 2320 long atime, PermissionStatus perm) 2321 throws UnresolvedLinkException { 2322 assert hasWriteLock(); 2323 INodeSymlink newNode = new INodeSymlink(target, modTime, atime, perm); 2324 try { 2325 newNode = addNode(path, newNode, UNKNOWN_DISK_SPACE); 2326 } catch (UnresolvedLinkException e) { 2327 /* All UnresolvedLinkExceptions should have been resolved by now, but we 2328 * should re-throw them in case that changes so they are not swallowed 2329 * by catching IOException below. 2330 */ 2331 throw e; 2332 } catch (IOException e) { 2333 return null; 2334 } 2335 return newNode; 2336 } 2337 2338 /** 2339 * Caches frequently used file names to reuse file name objects and 2340 * reduce heap size. 2341 */ 2342 void cacheName(INode inode) { 2343 // Name is cached only for files 2344 if (inode.isDirectory() || inode.isLink()) { 2345 return; 2346 } 2347 ByteArray name = new ByteArray(inode.getLocalNameBytes()); 2348 name = nameCache.put(name); 2349 if (name != null) { 2350 inode.setLocalName(name.getBytes()); 2351 } 2352 } 2353 2354}