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