1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 package org.apache.hadoop.hdfs.tools.offlineImageViewer; 19 20 import java.io.DataInputStream; 21 import java.io.IOException; 22 import java.text.DateFormat; 23 import java.text.SimpleDateFormat; 24 import java.util.Date; 25 import java.util.HashMap; 26 import java.util.Map; 27 28 import org.apache.hadoop.conf.Configuration; 29 import org.apache.hadoop.fs.permission.FsPermission; 30 import org.apache.hadoop.hdfs.protocol.DatanodeInfo.AdminStates; 31 import org.apache.hadoop.hdfs.protocol.LayoutFlags; 32 import org.apache.hadoop.hdfs.protocol.LayoutVersion.Feature; 33 import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; 34 import org.apache.hadoop.hdfs.server.namenode.FSImageSerialization; 35 import org.apache.hadoop.hdfs.server.namenode.INodeId; 36 import org.apache.hadoop.hdfs.server.namenode.NameNodeLayoutVersion; 37 import org.apache.hadoop.hdfs.tools.offlineImageViewer.ImageVisitor.ImageElement; 38 import org.apache.hadoop.io.Text; 39 import org.apache.hadoop.io.WritableUtils; 40 import org.apache.hadoop.io.compress.CompressionCodec; 41 import org.apache.hadoop.io.compress.CompressionCodecFactory; 42 import org.apache.hadoop.security.token.delegation.DelegationKey; 43 44 /** 45 * ImageLoaderCurrent processes Hadoop FSImage files and walks over 46 * them using a provided ImageVisitor, calling the visitor at each element 47 * enumerated below. 48 * 49 * The only difference between v18 and v19 was the utilization of the 50 * stickybit. Therefore, the same viewer can reader either format. 51 * 52 * Versions -19 fsimage layout (with changes from -16 up): 53 * Image version (int) 54 * Namepsace ID (int) 55 * NumFiles (long) 56 * Generation stamp (long) 57 * INodes (count = NumFiles) 58 * INode 59 * Path (String) 60 * Replication (short) 61 * Modification Time (long as date) 62 * Access Time (long) // added in -16 63 * Block size (long) 64 * Num blocks (int) 65 * Blocks (count = Num blocks) 66 * Block 67 * Block ID (long) 68 * Num bytes (long) 69 * Generation stamp (long) 70 * Namespace Quota (long) 71 * Diskspace Quota (long) // added in -18 72 * Permissions 73 * Username (String) 74 * Groupname (String) 75 * OctalPerms (short -> String) // Modified in -19 76 * Symlink (String) // added in -23 77 * NumINodesUnderConstruction (int) 78 * INodesUnderConstruction (count = NumINodesUnderConstruction) 79 * INodeUnderConstruction 80 * Path (bytes as string) 81 * Replication (short) 82 * Modification time (long as date) 83 * Preferred block size (long) 84 * Num blocks (int) 85 * Blocks 86 * Block 87 * Block ID (long) 88 * Num bytes (long) 89 * Generation stamp (long) 90 * Permissions 91 * Username (String) 92 * Groupname (String) 93 * OctalPerms (short -> String) 94 * Client Name (String) 95 * Client Machine (String) 96 * NumLocations (int) 97 * DatanodeDescriptors (count = numLocations) // not loaded into memory 98 * short // but still in file 99 * long 100 * string 101 * long 102 * int 103 * string 104 * string 105 * enum 106 * CurrentDelegationKeyId (int) 107 * NumDelegationKeys (int) 108 * DelegationKeys (count = NumDelegationKeys) 109 * DelegationKeyLength (vint) 110 * DelegationKey (bytes) 111 * DelegationTokenSequenceNumber (int) 112 * NumDelegationTokens (int) 113 * DelegationTokens (count = NumDelegationTokens) 114 * DelegationTokenIdentifier 115 * owner (String) 116 * renewer (String) 117 * realUser (String) 118 * issueDate (vlong) 119 * maxDate (vlong) 120 * sequenceNumber (vint) 121 * masterKeyId (vint) 122 * expiryTime (long) 123 * 124 */ 125 class ImageLoaderCurrent implements ImageLoader { 126 protected final DateFormat dateFormat = 127 new SimpleDateFormat("yyyy-MM-dd HH:mm"); 128 private static int[] versions = { -16, -17, -18, -19, -20, -21, -22, -23, 129 -24, -25, -26, -27, -28, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, 130 -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51 }; 131 private int imageVersion = 0; 132 133 private final Map<Long, Boolean> subtreeMap = new HashMap<Long, Boolean>(); 134 private final Map<Long, String> dirNodeMap = new HashMap<Long, String>(); 135 136 /* (non-Javadoc) 137 * @see ImageLoader#canProcessVersion(int) 138 */ 139 @Override canLoadVersion(int version)140 public boolean canLoadVersion(int version) { 141 for(int v : versions) 142 if(v == version) return true; 143 144 return false; 145 } 146 147 /* (non-Javadoc) 148 * @see ImageLoader#processImage(java.io.DataInputStream, ImageVisitor, boolean) 149 */ 150 @Override loadImage(DataInputStream in, ImageVisitor v, boolean skipBlocks)151 public void loadImage(DataInputStream in, ImageVisitor v, 152 boolean skipBlocks) throws IOException { 153 boolean done = false; 154 try { 155 v.start(); 156 v.visitEnclosingElement(ImageElement.FS_IMAGE); 157 158 imageVersion = in.readInt(); 159 if( !canLoadVersion(imageVersion)) 160 throw new IOException("Cannot process fslayout version " + imageVersion); 161 if (NameNodeLayoutVersion.supports(Feature.ADD_LAYOUT_FLAGS, imageVersion)) { 162 LayoutFlags.read(in); 163 } 164 165 v.visit(ImageElement.IMAGE_VERSION, imageVersion); 166 v.visit(ImageElement.NAMESPACE_ID, in.readInt()); 167 168 long numInodes = in.readLong(); 169 170 v.visit(ImageElement.GENERATION_STAMP, in.readLong()); 171 172 if (NameNodeLayoutVersion.supports(Feature.SEQUENTIAL_BLOCK_ID, imageVersion)) { 173 v.visit(ImageElement.GENERATION_STAMP_V2, in.readLong()); 174 v.visit(ImageElement.GENERATION_STAMP_V1_LIMIT, in.readLong()); 175 v.visit(ImageElement.LAST_ALLOCATED_BLOCK_ID, in.readLong()); 176 } 177 178 if (NameNodeLayoutVersion.supports(Feature.STORED_TXIDS, imageVersion)) { 179 v.visit(ImageElement.TRANSACTION_ID, in.readLong()); 180 } 181 182 if (NameNodeLayoutVersion.supports(Feature.ADD_INODE_ID, imageVersion)) { 183 v.visit(ImageElement.LAST_INODE_ID, in.readLong()); 184 } 185 186 boolean supportSnapshot = NameNodeLayoutVersion.supports(Feature.SNAPSHOT, 187 imageVersion); 188 if (supportSnapshot) { 189 v.visit(ImageElement.SNAPSHOT_COUNTER, in.readInt()); 190 int numSnapshots = in.readInt(); 191 v.visit(ImageElement.NUM_SNAPSHOTS_TOTAL, numSnapshots); 192 for (int i = 0; i < numSnapshots; i++) { 193 processSnapshot(in, v); 194 } 195 } 196 197 if (NameNodeLayoutVersion.supports(Feature.FSIMAGE_COMPRESSION, imageVersion)) { 198 boolean isCompressed = in.readBoolean(); 199 v.visit(ImageElement.IS_COMPRESSED, String.valueOf(isCompressed)); 200 if (isCompressed) { 201 String codecClassName = Text.readString(in); 202 v.visit(ImageElement.COMPRESS_CODEC, codecClassName); 203 CompressionCodecFactory codecFac = new CompressionCodecFactory( 204 new Configuration()); 205 CompressionCodec codec = codecFac.getCodecByClassName(codecClassName); 206 if (codec == null) { 207 throw new IOException("Image compression codec not supported: " 208 + codecClassName); 209 } 210 in = new DataInputStream(codec.createInputStream(in)); 211 } 212 } 213 processINodes(in, v, numInodes, skipBlocks, supportSnapshot); 214 subtreeMap.clear(); 215 dirNodeMap.clear(); 216 217 processINodesUC(in, v, skipBlocks); 218 219 if (NameNodeLayoutVersion.supports(Feature.DELEGATION_TOKEN, imageVersion)) { 220 processDelegationTokens(in, v); 221 } 222 223 if (NameNodeLayoutVersion.supports(Feature.CACHING, imageVersion)) { 224 processCacheManagerState(in, v); 225 } 226 v.leaveEnclosingElement(); // FSImage 227 done = true; 228 } finally { 229 if (done) { 230 v.finish(); 231 } else { 232 v.finishAbnormally(); 233 } 234 } 235 } 236 237 /** 238 * Process CacheManager state from the fsimage. 239 */ processCacheManagerState(DataInputStream in, ImageVisitor v)240 private void processCacheManagerState(DataInputStream in, ImageVisitor v) 241 throws IOException { 242 v.visit(ImageElement.CACHE_NEXT_ENTRY_ID, in.readLong()); 243 final int numPools = in.readInt(); 244 for (int i=0; i<numPools; i++) { 245 v.visit(ImageElement.CACHE_POOL_NAME, Text.readString(in)); 246 processCachePoolPermission(in, v); 247 v.visit(ImageElement.CACHE_POOL_WEIGHT, in.readInt()); 248 } 249 final int numEntries = in.readInt(); 250 for (int i=0; i<numEntries; i++) { 251 v.visit(ImageElement.CACHE_ENTRY_PATH, Text.readString(in)); 252 v.visit(ImageElement.CACHE_ENTRY_REPLICATION, in.readShort()); 253 v.visit(ImageElement.CACHE_ENTRY_POOL_NAME, Text.readString(in)); 254 } 255 } 256 /** 257 * Process the Delegation Token related section in fsimage. 258 * 259 * @param in DataInputStream to process 260 * @param v Visitor to walk over records 261 */ processDelegationTokens(DataInputStream in, ImageVisitor v)262 private void processDelegationTokens(DataInputStream in, ImageVisitor v) 263 throws IOException { 264 v.visit(ImageElement.CURRENT_DELEGATION_KEY_ID, in.readInt()); 265 int numDKeys = in.readInt(); 266 v.visitEnclosingElement(ImageElement.DELEGATION_KEYS, 267 ImageElement.NUM_DELEGATION_KEYS, numDKeys); 268 for(int i =0; i < numDKeys; i++) { 269 DelegationKey key = new DelegationKey(); 270 key.readFields(in); 271 v.visit(ImageElement.DELEGATION_KEY, key.toString()); 272 } 273 v.leaveEnclosingElement(); 274 v.visit(ImageElement.DELEGATION_TOKEN_SEQUENCE_NUMBER, in.readInt()); 275 int numDTokens = in.readInt(); 276 v.visitEnclosingElement(ImageElement.DELEGATION_TOKENS, 277 ImageElement.NUM_DELEGATION_TOKENS, numDTokens); 278 for(int i=0; i<numDTokens; i++){ 279 DelegationTokenIdentifier id = new DelegationTokenIdentifier(); 280 id.readFields(in); 281 long expiryTime = in.readLong(); 282 v.visitEnclosingElement(ImageElement.DELEGATION_TOKEN_IDENTIFIER); 283 v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_KIND, 284 id.getKind().toString()); 285 v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_SEQNO, 286 id.getSequenceNumber()); 287 v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_OWNER, 288 id.getOwner().toString()); 289 v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_RENEWER, 290 id.getRenewer().toString()); 291 v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_REALUSER, 292 id.getRealUser().toString()); 293 v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_ISSUE_DATE, 294 id.getIssueDate()); 295 v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_MAX_DATE, 296 id.getMaxDate()); 297 v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_EXPIRY_TIME, 298 expiryTime); 299 v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_MASTER_KEY_ID, 300 id.getMasterKeyId()); 301 v.leaveEnclosingElement(); // DELEGATION_TOKEN_IDENTIFIER 302 } 303 v.leaveEnclosingElement(); // DELEGATION_TOKENS 304 } 305 306 /** 307 * Process the INodes under construction section of the fsimage. 308 * 309 * @param in DataInputStream to process 310 * @param v Visitor to walk over inodes 311 * @param skipBlocks Walk over each block? 312 */ processINodesUC(DataInputStream in, ImageVisitor v, boolean skipBlocks)313 private void processINodesUC(DataInputStream in, ImageVisitor v, 314 boolean skipBlocks) throws IOException { 315 int numINUC = in.readInt(); 316 317 v.visitEnclosingElement(ImageElement.INODES_UNDER_CONSTRUCTION, 318 ImageElement.NUM_INODES_UNDER_CONSTRUCTION, numINUC); 319 320 for(int i = 0; i < numINUC; i++) { 321 v.visitEnclosingElement(ImageElement.INODE_UNDER_CONSTRUCTION); 322 byte [] name = FSImageSerialization.readBytes(in); 323 String n = new String(name, "UTF8"); 324 v.visit(ImageElement.INODE_PATH, n); 325 326 if (NameNodeLayoutVersion.supports(Feature.ADD_INODE_ID, imageVersion)) { 327 long inodeId = in.readLong(); 328 v.visit(ImageElement.INODE_ID, inodeId); 329 } 330 331 v.visit(ImageElement.REPLICATION, in.readShort()); 332 v.visit(ImageElement.MODIFICATION_TIME, formatDate(in.readLong())); 333 334 v.visit(ImageElement.PREFERRED_BLOCK_SIZE, in.readLong()); 335 int numBlocks = in.readInt(); 336 processBlocks(in, v, numBlocks, skipBlocks); 337 338 processPermission(in, v); 339 v.visit(ImageElement.CLIENT_NAME, FSImageSerialization.readString(in)); 340 v.visit(ImageElement.CLIENT_MACHINE, FSImageSerialization.readString(in)); 341 342 // Skip over the datanode descriptors, which are still stored in the 343 // file but are not used by the datanode or loaded into memory 344 int numLocs = in.readInt(); 345 for(int j = 0; j < numLocs; j++) { 346 in.readShort(); 347 in.readLong(); 348 in.readLong(); 349 in.readLong(); 350 in.readInt(); 351 FSImageSerialization.readString(in); 352 FSImageSerialization.readString(in); 353 WritableUtils.readEnum(in, AdminStates.class); 354 } 355 356 v.leaveEnclosingElement(); // INodeUnderConstruction 357 } 358 359 v.leaveEnclosingElement(); // INodesUnderConstruction 360 } 361 362 /** 363 * Process the blocks section of the fsimage. 364 * 365 * @param in Datastream to process 366 * @param v Visitor to walk over inodes 367 * @param skipBlocks Walk over each block? 368 */ processBlocks(DataInputStream in, ImageVisitor v, int numBlocks, boolean skipBlocks)369 private void processBlocks(DataInputStream in, ImageVisitor v, 370 int numBlocks, boolean skipBlocks) throws IOException { 371 v.visitEnclosingElement(ImageElement.BLOCKS, 372 ImageElement.NUM_BLOCKS, numBlocks); 373 374 // directory or symlink or reference node, no blocks to process 375 if(numBlocks < 0) { 376 v.leaveEnclosingElement(); // Blocks 377 return; 378 } 379 380 if(skipBlocks) { 381 int bytesToSkip = ((Long.SIZE * 3 /* fields */) / 8 /*bits*/) * numBlocks; 382 if(in.skipBytes(bytesToSkip) != bytesToSkip) 383 throw new IOException("Error skipping over blocks"); 384 385 } else { 386 for(int j = 0; j < numBlocks; j++) { 387 v.visitEnclosingElement(ImageElement.BLOCK); 388 v.visit(ImageElement.BLOCK_ID, in.readLong()); 389 v.visit(ImageElement.NUM_BYTES, in.readLong()); 390 v.visit(ImageElement.GENERATION_STAMP, in.readLong()); 391 v.leaveEnclosingElement(); // Block 392 } 393 } 394 v.leaveEnclosingElement(); // Blocks 395 } 396 397 /** 398 * Extract the INode permissions stored in the fsimage file. 399 * 400 * @param in Datastream to process 401 * @param v Visitor to walk over inodes 402 */ processPermission(DataInputStream in, ImageVisitor v)403 private void processPermission(DataInputStream in, ImageVisitor v) 404 throws IOException { 405 v.visitEnclosingElement(ImageElement.PERMISSIONS); 406 v.visit(ImageElement.USER_NAME, Text.readString(in)); 407 v.visit(ImageElement.GROUP_NAME, Text.readString(in)); 408 FsPermission fsp = new FsPermission(in.readShort()); 409 v.visit(ImageElement.PERMISSION_STRING, fsp.toString()); 410 v.leaveEnclosingElement(); // Permissions 411 } 412 413 /** 414 * Extract CachePool permissions stored in the fsimage file. 415 * 416 * @param in Datastream to process 417 * @param v Visitor to walk over inodes 418 */ processCachePoolPermission(DataInputStream in, ImageVisitor v)419 private void processCachePoolPermission(DataInputStream in, ImageVisitor v) 420 throws IOException { 421 v.visitEnclosingElement(ImageElement.PERMISSIONS); 422 v.visit(ImageElement.CACHE_POOL_OWNER_NAME, Text.readString(in)); 423 v.visit(ImageElement.CACHE_POOL_GROUP_NAME, Text.readString(in)); 424 FsPermission fsp = new FsPermission(in.readShort()); 425 v.visit(ImageElement.CACHE_POOL_PERMISSION_STRING, fsp.toString()); 426 v.leaveEnclosingElement(); // Permissions 427 } 428 429 /** 430 * Process the INode records stored in the fsimage. 431 * 432 * @param in Datastream to process 433 * @param v Visitor to walk over INodes 434 * @param numInodes Number of INodes stored in file 435 * @param skipBlocks Process all the blocks within the INode? 436 * @param supportSnapshot Whether or not the imageVersion supports snapshot 437 * @throws VisitException 438 * @throws IOException 439 */ processINodes(DataInputStream in, ImageVisitor v, long numInodes, boolean skipBlocks, boolean supportSnapshot)440 private void processINodes(DataInputStream in, ImageVisitor v, 441 long numInodes, boolean skipBlocks, boolean supportSnapshot) 442 throws IOException { 443 v.visitEnclosingElement(ImageElement.INODES, 444 ImageElement.NUM_INODES, numInodes); 445 446 if (NameNodeLayoutVersion.supports(Feature.FSIMAGE_NAME_OPTIMIZATION, imageVersion)) { 447 if (!supportSnapshot) { 448 processLocalNameINodes(in, v, numInodes, skipBlocks); 449 } else { 450 processLocalNameINodesWithSnapshot(in, v, skipBlocks); 451 } 452 } else { // full path name 453 processFullNameINodes(in, v, numInodes, skipBlocks); 454 } 455 456 457 v.leaveEnclosingElement(); // INodes 458 } 459 460 /** 461 * Process image with full path name 462 * 463 * @param in image stream 464 * @param v visitor 465 * @param numInodes number of indoes to read 466 * @param skipBlocks skip blocks or not 467 * @throws IOException if there is any error occurs 468 */ processLocalNameINodes(DataInputStream in, ImageVisitor v, long numInodes, boolean skipBlocks)469 private void processLocalNameINodes(DataInputStream in, ImageVisitor v, 470 long numInodes, boolean skipBlocks) throws IOException { 471 // process root 472 processINode(in, v, skipBlocks, "", false); 473 numInodes--; 474 while (numInodes > 0) { 475 numInodes -= processDirectory(in, v, skipBlocks); 476 } 477 } 478 processDirectory(DataInputStream in, ImageVisitor v, boolean skipBlocks)479 private int processDirectory(DataInputStream in, ImageVisitor v, 480 boolean skipBlocks) throws IOException { 481 String parentName = FSImageSerialization.readString(in); 482 return processChildren(in, v, skipBlocks, parentName); 483 } 484 485 /** 486 * Process image with local path name and snapshot support 487 * 488 * @param in image stream 489 * @param v visitor 490 * @param skipBlocks skip blocks or not 491 */ processLocalNameINodesWithSnapshot(DataInputStream in, ImageVisitor v, boolean skipBlocks)492 private void processLocalNameINodesWithSnapshot(DataInputStream in, 493 ImageVisitor v, boolean skipBlocks) throws IOException { 494 // process root 495 processINode(in, v, skipBlocks, "", false); 496 processDirectoryWithSnapshot(in, v, skipBlocks); 497 } 498 499 /** 500 * Process directories when snapshot is supported. 501 */ processDirectoryWithSnapshot(DataInputStream in, ImageVisitor v, boolean skipBlocks)502 private void processDirectoryWithSnapshot(DataInputStream in, ImageVisitor v, 503 boolean skipBlocks) throws IOException { 504 // 1. load dir node id 505 long inodeId = in.readLong(); 506 507 String dirName = dirNodeMap.remove(inodeId); 508 Boolean visitedRef = subtreeMap.get(inodeId); 509 if (visitedRef != null) { 510 if (visitedRef.booleanValue()) { // the subtree has been visited 511 return; 512 } else { // first time to visit 513 subtreeMap.put(inodeId, true); 514 } 515 } // else the dir is not linked by a RefNode, thus cannot be revisited 516 517 // 2. load possible snapshots 518 processSnapshots(in, v, dirName); 519 // 3. load children nodes 520 processChildren(in, v, skipBlocks, dirName); 521 // 4. load possible directory diff list 522 processDirectoryDiffList(in, v, dirName); 523 // recursively process sub-directories 524 final int numSubTree = in.readInt(); 525 for (int i = 0; i < numSubTree; i++) { 526 processDirectoryWithSnapshot(in, v, skipBlocks); 527 } 528 } 529 530 /** 531 * Process snapshots of a snapshottable directory 532 */ processSnapshots(DataInputStream in, ImageVisitor v, String rootName)533 private void processSnapshots(DataInputStream in, ImageVisitor v, 534 String rootName) throws IOException { 535 final int numSnapshots = in.readInt(); 536 if (numSnapshots >= 0) { 537 v.visitEnclosingElement(ImageElement.SNAPSHOTS, 538 ImageElement.NUM_SNAPSHOTS, numSnapshots); 539 for (int i = 0; i < numSnapshots; i++) { 540 // process snapshot 541 v.visitEnclosingElement(ImageElement.SNAPSHOT); 542 v.visit(ImageElement.SNAPSHOT_ID, in.readInt()); 543 v.leaveEnclosingElement(); 544 } 545 v.visit(ImageElement.SNAPSHOT_QUOTA, in.readInt()); 546 v.leaveEnclosingElement(); 547 } 548 } 549 processSnapshot(DataInputStream in, ImageVisitor v)550 private void processSnapshot(DataInputStream in, ImageVisitor v) 551 throws IOException { 552 v.visitEnclosingElement(ImageElement.SNAPSHOT); 553 v.visit(ImageElement.SNAPSHOT_ID, in.readInt()); 554 // process root of snapshot 555 v.visitEnclosingElement(ImageElement.SNAPSHOT_ROOT); 556 processINode(in, v, true, "", false); 557 v.leaveEnclosingElement(); 558 v.leaveEnclosingElement(); 559 } 560 processDirectoryDiffList(DataInputStream in, ImageVisitor v, String currentINodeName)561 private void processDirectoryDiffList(DataInputStream in, ImageVisitor v, 562 String currentINodeName) throws IOException { 563 final int numDirDiff = in.readInt(); 564 if (numDirDiff >= 0) { 565 v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFFS, 566 ImageElement.NUM_SNAPSHOT_DIR_DIFF, numDirDiff); 567 for (int i = 0; i < numDirDiff; i++) { 568 // process directory diffs in reverse chronological oder 569 processDirectoryDiff(in, v, currentINodeName); 570 } 571 v.leaveEnclosingElement(); 572 } 573 } 574 processDirectoryDiff(DataInputStream in, ImageVisitor v, String currentINodeName)575 private void processDirectoryDiff(DataInputStream in, ImageVisitor v, 576 String currentINodeName) throws IOException { 577 v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFF); 578 int snapshotId = in.readInt(); 579 v.visit(ImageElement.SNAPSHOT_DIFF_SNAPSHOTID, snapshotId); 580 v.visit(ImageElement.SNAPSHOT_DIR_DIFF_CHILDREN_SIZE, in.readInt()); 581 582 // process snapshotINode 583 boolean useRoot = in.readBoolean(); 584 if (!useRoot) { 585 if (in.readBoolean()) { 586 v.visitEnclosingElement(ImageElement.SNAPSHOT_INODE_DIRECTORY_ATTRIBUTES); 587 if (NameNodeLayoutVersion.supports(Feature.OPTIMIZE_SNAPSHOT_INODES, imageVersion)) { 588 processINodeDirectoryAttributes(in, v, currentINodeName); 589 } else { 590 processINode(in, v, true, currentINodeName, true); 591 } 592 v.leaveEnclosingElement(); 593 } 594 } 595 596 // process createdList 597 int createdSize = in.readInt(); 598 v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFF_CREATEDLIST, 599 ImageElement.SNAPSHOT_DIR_DIFF_CREATEDLIST_SIZE, createdSize); 600 for (int i = 0; i < createdSize; i++) { 601 String createdNode = FSImageSerialization.readString(in); 602 v.visit(ImageElement.SNAPSHOT_DIR_DIFF_CREATED_INODE, createdNode); 603 } 604 v.leaveEnclosingElement(); 605 606 // process deletedList 607 int deletedSize = in.readInt(); 608 v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFF_DELETEDLIST, 609 ImageElement.SNAPSHOT_DIR_DIFF_DELETEDLIST_SIZE, deletedSize); 610 for (int i = 0; i < deletedSize; i++) { 611 v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFF_DELETED_INODE); 612 processINode(in, v, false, currentINodeName, true); 613 v.leaveEnclosingElement(); 614 } 615 v.leaveEnclosingElement(); 616 v.leaveEnclosingElement(); 617 } 618 processINodeDirectoryAttributes(DataInputStream in, ImageVisitor v, String parentName)619 private void processINodeDirectoryAttributes(DataInputStream in, ImageVisitor v, 620 String parentName) throws IOException { 621 final String pathName = readINodePath(in, parentName); 622 v.visit(ImageElement.INODE_PATH, pathName); 623 processPermission(in, v); 624 v.visit(ImageElement.MODIFICATION_TIME, formatDate(in.readLong())); 625 626 v.visit(ImageElement.NS_QUOTA, in.readLong()); 627 v.visit(ImageElement.DS_QUOTA, in.readLong()); 628 } 629 630 /** Process children under a directory */ processChildren(DataInputStream in, ImageVisitor v, boolean skipBlocks, String parentName)631 private int processChildren(DataInputStream in, ImageVisitor v, 632 boolean skipBlocks, String parentName) throws IOException { 633 int numChildren = in.readInt(); 634 for (int i = 0; i < numChildren; i++) { 635 processINode(in, v, skipBlocks, parentName, false); 636 } 637 return numChildren; 638 } 639 640 /** 641 * Process image with full path name 642 * 643 * @param in image stream 644 * @param v visitor 645 * @param numInodes number of indoes to read 646 * @param skipBlocks skip blocks or not 647 * @throws IOException if there is any error occurs 648 */ processFullNameINodes(DataInputStream in, ImageVisitor v, long numInodes, boolean skipBlocks)649 private void processFullNameINodes(DataInputStream in, ImageVisitor v, 650 long numInodes, boolean skipBlocks) throws IOException { 651 for(long i = 0; i < numInodes; i++) { 652 processINode(in, v, skipBlocks, null, false); 653 } 654 } 655 readINodePath(DataInputStream in, String parentName)656 private String readINodePath(DataInputStream in, String parentName) 657 throws IOException { 658 String pathName = FSImageSerialization.readString(in); 659 if (parentName != null) { // local name 660 pathName = "/" + pathName; 661 if (!"/".equals(parentName)) { // children of non-root directory 662 pathName = parentName + pathName; 663 } 664 } 665 return pathName; 666 } 667 668 /** 669 * Process an INode 670 * 671 * @param in image stream 672 * @param v visitor 673 * @param skipBlocks skip blocks or not 674 * @param parentName the name of its parent node 675 * @param isSnapshotCopy whether or not the inode is a snapshot copy 676 * @throws IOException 677 */ processINode(DataInputStream in, ImageVisitor v, boolean skipBlocks, String parentName, boolean isSnapshotCopy)678 private void processINode(DataInputStream in, ImageVisitor v, 679 boolean skipBlocks, String parentName, boolean isSnapshotCopy) 680 throws IOException { 681 boolean supportSnapshot = 682 NameNodeLayoutVersion.supports(Feature.SNAPSHOT, imageVersion); 683 boolean supportInodeId = 684 NameNodeLayoutVersion.supports(Feature.ADD_INODE_ID, imageVersion); 685 686 v.visitEnclosingElement(ImageElement.INODE); 687 final String pathName = readINodePath(in, parentName); 688 v.visit(ImageElement.INODE_PATH, pathName); 689 690 long inodeId = INodeId.GRANDFATHER_INODE_ID; 691 if (supportInodeId) { 692 inodeId = in.readLong(); 693 v.visit(ImageElement.INODE_ID, inodeId); 694 } 695 v.visit(ImageElement.REPLICATION, in.readShort()); 696 v.visit(ImageElement.MODIFICATION_TIME, formatDate(in.readLong())); 697 if(NameNodeLayoutVersion.supports(Feature.FILE_ACCESS_TIME, imageVersion)) 698 v.visit(ImageElement.ACCESS_TIME, formatDate(in.readLong())); 699 v.visit(ImageElement.BLOCK_SIZE, in.readLong()); 700 int numBlocks = in.readInt(); 701 702 processBlocks(in, v, numBlocks, skipBlocks); 703 704 if (numBlocks >= 0) { // File 705 if (supportSnapshot) { 706 // make sure subtreeMap only contains entry for directory 707 subtreeMap.remove(inodeId); 708 // process file diffs 709 processFileDiffList(in, v, parentName); 710 if (isSnapshotCopy) { 711 boolean underConstruction = in.readBoolean(); 712 if (underConstruction) { 713 v.visit(ImageElement.CLIENT_NAME, 714 FSImageSerialization.readString(in)); 715 v.visit(ImageElement.CLIENT_MACHINE, 716 FSImageSerialization.readString(in)); 717 } 718 } 719 } 720 processPermission(in, v); 721 } else if (numBlocks == -1) { // Directory 722 if (supportSnapshot && supportInodeId) { 723 dirNodeMap.put(inodeId, pathName); 724 } 725 v.visit(ImageElement.NS_QUOTA, numBlocks == -1 ? in.readLong() : -1); 726 if (NameNodeLayoutVersion.supports(Feature.DISKSPACE_QUOTA, imageVersion)) 727 v.visit(ImageElement.DS_QUOTA, numBlocks == -1 ? in.readLong() : -1); 728 if (supportSnapshot) { 729 boolean snapshottable = in.readBoolean(); 730 if (!snapshottable) { 731 boolean withSnapshot = in.readBoolean(); 732 v.visit(ImageElement.IS_WITHSNAPSHOT_DIR, Boolean.toString(withSnapshot)); 733 } else { 734 v.visit(ImageElement.IS_SNAPSHOTTABLE_DIR, Boolean.toString(snapshottable)); 735 } 736 } 737 processPermission(in, v); 738 } else if (numBlocks == -2) { 739 v.visit(ImageElement.SYMLINK, Text.readString(in)); 740 processPermission(in, v); 741 } else if (numBlocks == -3) { // reference node 742 final boolean isWithName = in.readBoolean(); 743 int snapshotId = in.readInt(); 744 if (isWithName) { 745 v.visit(ImageElement.SNAPSHOT_LAST_SNAPSHOT_ID, snapshotId); 746 } else { 747 v.visit(ImageElement.SNAPSHOT_DST_SNAPSHOT_ID, snapshotId); 748 } 749 750 final boolean firstReferred = in.readBoolean(); 751 if (firstReferred) { 752 // if a subtree is linked by multiple "parents", the corresponding dir 753 // must be referred by a reference node. we put the reference node into 754 // the subtreeMap here and let its value be false. when we later visit 755 // the subtree for the first time, we change the value to true. 756 subtreeMap.put(inodeId, false); 757 v.visitEnclosingElement(ImageElement.SNAPSHOT_REF_INODE); 758 processINode(in, v, skipBlocks, parentName, isSnapshotCopy); 759 v.leaveEnclosingElement(); // referred inode 760 } else { 761 v.visit(ImageElement.SNAPSHOT_REF_INODE_ID, in.readLong()); 762 } 763 } 764 765 v.leaveEnclosingElement(); // INode 766 } 767 processINodeFileAttributes(DataInputStream in, ImageVisitor v, String parentName)768 private void processINodeFileAttributes(DataInputStream in, ImageVisitor v, 769 String parentName) throws IOException { 770 final String pathName = readINodePath(in, parentName); 771 v.visit(ImageElement.INODE_PATH, pathName); 772 processPermission(in, v); 773 v.visit(ImageElement.MODIFICATION_TIME, formatDate(in.readLong())); 774 if(NameNodeLayoutVersion.supports(Feature.FILE_ACCESS_TIME, imageVersion)) { 775 v.visit(ImageElement.ACCESS_TIME, formatDate(in.readLong())); 776 } 777 778 v.visit(ImageElement.REPLICATION, in.readShort()); 779 v.visit(ImageElement.BLOCK_SIZE, in.readLong()); 780 } 781 processFileDiffList(DataInputStream in, ImageVisitor v, String currentINodeName)782 private void processFileDiffList(DataInputStream in, ImageVisitor v, 783 String currentINodeName) throws IOException { 784 final int size = in.readInt(); 785 if (size >= 0) { 786 v.visitEnclosingElement(ImageElement.SNAPSHOT_FILE_DIFFS, 787 ImageElement.NUM_SNAPSHOT_FILE_DIFF, size); 788 for (int i = 0; i < size; i++) { 789 processFileDiff(in, v, currentINodeName); 790 } 791 v.leaveEnclosingElement(); 792 } 793 } 794 processFileDiff(DataInputStream in, ImageVisitor v, String currentINodeName)795 private void processFileDiff(DataInputStream in, ImageVisitor v, 796 String currentINodeName) throws IOException { 797 int snapshotId = in.readInt(); 798 v.visitEnclosingElement(ImageElement.SNAPSHOT_FILE_DIFF, 799 ImageElement.SNAPSHOT_DIFF_SNAPSHOTID, snapshotId); 800 v.visit(ImageElement.SNAPSHOT_FILE_SIZE, in.readLong()); 801 if (in.readBoolean()) { 802 v.visitEnclosingElement(ImageElement.SNAPSHOT_INODE_FILE_ATTRIBUTES); 803 if (NameNodeLayoutVersion.supports(Feature.OPTIMIZE_SNAPSHOT_INODES, imageVersion)) { 804 processINodeFileAttributes(in, v, currentINodeName); 805 } else { 806 processINode(in, v, true, currentINodeName, true); 807 } 808 v.leaveEnclosingElement(); 809 } 810 v.leaveEnclosingElement(); 811 } 812 813 /** 814 * Helper method to format dates during processing. 815 * @param date Date as read from image file 816 * @return String version of date format 817 */ formatDate(long date)818 private String formatDate(long date) { 819 return dateFormat.format(new Date(date)); 820 } 821 } 822