/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hdfs.tools.offlineImageViewer; import java.io.DataInputStream; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.hdfs.protocol.DatanodeInfo.AdminStates; import org.apache.hadoop.hdfs.protocol.LayoutVersion; import org.apache.hadoop.hdfs.protocol.LayoutVersion.Feature; import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; import org.apache.hadoop.hdfs.server.namenode.FSImageSerialization; import org.apache.hadoop.hdfs.server.namenode.INodeId; import org.apache.hadoop.hdfs.tools.offlineImageViewer.ImageVisitor.ImageElement; import org.apache.hadoop.io.Text; import org.apache.hadoop.io.WritableUtils; import org.apache.hadoop.io.compress.CompressionCodec; import org.apache.hadoop.io.compress.CompressionCodecFactory; import org.apache.hadoop.security.token.delegation.DelegationKey; /** * ImageLoaderCurrent processes Hadoop FSImage files and walks over * them using a provided ImageVisitor, calling the visitor at each element * enumerated below. * * The only difference between v18 and v19 was the utilization of the * stickybit. Therefore, the same viewer can reader either format. * * Versions -19 fsimage layout (with changes from -16 up): * Image version (int) * Namepsace ID (int) * NumFiles (long) * Generation stamp (long) * INodes (count = NumFiles) * INode * Path (String) * Replication (short) * Modification Time (long as date) * Access Time (long) // added in -16 * Block size (long) * Num blocks (int) * Blocks (count = Num blocks) * Block * Block ID (long) * Num bytes (long) * Generation stamp (long) * Namespace Quota (long) * Diskspace Quota (long) // added in -18 * Permissions * Username (String) * Groupname (String) * OctalPerms (short -> String) // Modified in -19 * Symlink (String) // added in -23 * NumINodesUnderConstruction (int) * INodesUnderConstruction (count = NumINodesUnderConstruction) * INodeUnderConstruction * Path (bytes as string) * Replication (short) * Modification time (long as date) * Preferred block size (long) * Num blocks (int) * Blocks * Block * Block ID (long) * Num bytes (long) * Generation stamp (long) * Permissions * Username (String) * Groupname (String) * OctalPerms (short -> String) * Client Name (String) * Client Machine (String) * NumLocations (int) * DatanodeDescriptors (count = numLocations) // not loaded into memory * short // but still in file * long * string * long * int * string * string * enum * CurrentDelegationKeyId (int) * NumDelegationKeys (int) * DelegationKeys (count = NumDelegationKeys) * DelegationKeyLength (vint) * DelegationKey (bytes) * DelegationTokenSequenceNumber (int) * NumDelegationTokens (int) * DelegationTokens (count = NumDelegationTokens) * DelegationTokenIdentifier * owner (String) * renewer (String) * realUser (String) * issueDate (vlong) * maxDate (vlong) * sequenceNumber (vint) * masterKeyId (vint) * expiryTime (long) * */ class ImageLoaderCurrent implements ImageLoader { protected final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); private static int[] versions = { -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47 }; private int imageVersion = 0; private final Map<Long, String> subtreeMap = new HashMap<Long, String>(); private final Map<Long, String> dirNodeMap = new HashMap<Long, String>(); /* (non-Javadoc) * @see ImageLoader#canProcessVersion(int) */ @Override public boolean canLoadVersion(int version) { for(int v : versions) if(v == version) return true; return false; } /* (non-Javadoc) * @see ImageLoader#processImage(java.io.DataInputStream, ImageVisitor, boolean) */ @Override public void loadImage(DataInputStream in, ImageVisitor v, boolean skipBlocks) throws IOException { boolean done = false; try { v.start(); v.visitEnclosingElement(ImageElement.FS_IMAGE); imageVersion = in.readInt(); if( !canLoadVersion(imageVersion)) throw new IOException("Cannot process fslayout version " + imageVersion); v.visit(ImageElement.IMAGE_VERSION, imageVersion); v.visit(ImageElement.NAMESPACE_ID, in.readInt()); long numInodes = in.readLong(); v.visit(ImageElement.GENERATION_STAMP, in.readLong()); if (LayoutVersion.supports(Feature.SEQUENTIAL_BLOCK_ID, imageVersion)) { v.visit(ImageElement.GENERATION_STAMP_V2, in.readLong()); v.visit(ImageElement.GENERATION_STAMP_V1_LIMIT, in.readLong()); v.visit(ImageElement.LAST_ALLOCATED_BLOCK_ID, in.readLong()); } if (LayoutVersion.supports(Feature.STORED_TXIDS, imageVersion)) { v.visit(ImageElement.TRANSACTION_ID, in.readLong()); } if (LayoutVersion.supports(Feature.ADD_INODE_ID, imageVersion)) { v.visit(ImageElement.LAST_INODE_ID, in.readLong()); } boolean supportSnapshot = LayoutVersion.supports(Feature.SNAPSHOT, imageVersion); if (supportSnapshot) { v.visit(ImageElement.SNAPSHOT_COUNTER, in.readInt()); int numSnapshots = in.readInt(); v.visit(ImageElement.NUM_SNAPSHOTS_TOTAL, numSnapshots); for (int i = 0; i < numSnapshots; i++) { processSnapshot(in, v); } } if (LayoutVersion.supports(Feature.FSIMAGE_COMPRESSION, imageVersion)) { boolean isCompressed = in.readBoolean(); v.visit(ImageElement.IS_COMPRESSED, String.valueOf(isCompressed)); if (isCompressed) { String codecClassName = Text.readString(in); v.visit(ImageElement.COMPRESS_CODEC, codecClassName); CompressionCodecFactory codecFac = new CompressionCodecFactory( new Configuration()); CompressionCodec codec = codecFac.getCodecByClassName(codecClassName); if (codec == null) { throw new IOException("Image compression codec not supported: " + codecClassName); } in = new DataInputStream(codec.createInputStream(in)); } } processINodes(in, v, numInodes, skipBlocks, supportSnapshot); subtreeMap.clear(); dirNodeMap.clear(); processINodesUC(in, v, skipBlocks); if (LayoutVersion.supports(Feature.DELEGATION_TOKEN, imageVersion)) { processDelegationTokens(in, v); } v.leaveEnclosingElement(); // FSImage done = true; } finally { if (done) { v.finish(); } else { v.finishAbnormally(); } } } /** * Process the Delegation Token related section in fsimage. * * @param in DataInputStream to process * @param v Visitor to walk over records */ private void processDelegationTokens(DataInputStream in, ImageVisitor v) throws IOException { v.visit(ImageElement.CURRENT_DELEGATION_KEY_ID, in.readInt()); int numDKeys = in.readInt(); v.visitEnclosingElement(ImageElement.DELEGATION_KEYS, ImageElement.NUM_DELEGATION_KEYS, numDKeys); for(int i =0; i < numDKeys; i++) { DelegationKey key = new DelegationKey(); key.readFields(in); v.visit(ImageElement.DELEGATION_KEY, key.toString()); } v.leaveEnclosingElement(); v.visit(ImageElement.DELEGATION_TOKEN_SEQUENCE_NUMBER, in.readInt()); int numDTokens = in.readInt(); v.visitEnclosingElement(ImageElement.DELEGATION_TOKENS, ImageElement.NUM_DELEGATION_TOKENS, numDTokens); for(int i=0; i<numDTokens; i++){ DelegationTokenIdentifier id = new DelegationTokenIdentifier(); id.readFields(in); long expiryTime = in.readLong(); v.visitEnclosingElement(ImageElement.DELEGATION_TOKEN_IDENTIFIER); v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_KIND, id.getKind().toString()); v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_SEQNO, id.getSequenceNumber()); v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_OWNER, id.getOwner().toString()); v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_RENEWER, id.getRenewer().toString()); v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_REALUSER, id.getRealUser().toString()); v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_ISSUE_DATE, id.getIssueDate()); v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_MAX_DATE, id.getMaxDate()); v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_EXPIRY_TIME, expiryTime); v.visit(ImageElement.DELEGATION_TOKEN_IDENTIFIER_MASTER_KEY_ID, id.getMasterKeyId()); v.leaveEnclosingElement(); // DELEGATION_TOKEN_IDENTIFIER } v.leaveEnclosingElement(); // DELEGATION_TOKENS } /** * Process the INodes under construction section of the fsimage. * * @param in DataInputStream to process * @param v Visitor to walk over inodes * @param skipBlocks Walk over each block? */ private void processINodesUC(DataInputStream in, ImageVisitor v, boolean skipBlocks) throws IOException { int numINUC = in.readInt(); v.visitEnclosingElement(ImageElement.INODES_UNDER_CONSTRUCTION, ImageElement.NUM_INODES_UNDER_CONSTRUCTION, numINUC); for(int i = 0; i < numINUC; i++) { v.visitEnclosingElement(ImageElement.INODE_UNDER_CONSTRUCTION); byte [] name = FSImageSerialization.readBytes(in); String n = new String(name, "UTF8"); v.visit(ImageElement.INODE_PATH, n); if (LayoutVersion.supports(Feature.ADD_INODE_ID, imageVersion)) { long inodeId = in.readLong(); v.visit(ImageElement.INODE_ID, inodeId); } v.visit(ImageElement.REPLICATION, in.readShort()); v.visit(ImageElement.MODIFICATION_TIME, formatDate(in.readLong())); v.visit(ImageElement.PREFERRED_BLOCK_SIZE, in.readLong()); int numBlocks = in.readInt(); processBlocks(in, v, numBlocks, skipBlocks); processPermission(in, v); v.visit(ImageElement.CLIENT_NAME, FSImageSerialization.readString(in)); v.visit(ImageElement.CLIENT_MACHINE, FSImageSerialization.readString(in)); // Skip over the datanode descriptors, which are still stored in the // file but are not used by the datanode or loaded into memory int numLocs = in.readInt(); for(int j = 0; j < numLocs; j++) { in.readShort(); in.readLong(); in.readLong(); in.readLong(); in.readInt(); FSImageSerialization.readString(in); FSImageSerialization.readString(in); WritableUtils.readEnum(in, AdminStates.class); } v.leaveEnclosingElement(); // INodeUnderConstruction } v.leaveEnclosingElement(); // INodesUnderConstruction } /** * Process the blocks section of the fsimage. * * @param in Datastream to process * @param v Visitor to walk over inodes * @param skipBlocks Walk over each block? */ private void processBlocks(DataInputStream in, ImageVisitor v, int numBlocks, boolean skipBlocks) throws IOException { v.visitEnclosingElement(ImageElement.BLOCKS, ImageElement.NUM_BLOCKS, numBlocks); // directory or symlink or reference node, no blocks to process if(numBlocks < 0) { v.leaveEnclosingElement(); // Blocks return; } if(skipBlocks) { int bytesToSkip = ((Long.SIZE * 3 /* fields */) / 8 /*bits*/) * numBlocks; if(in.skipBytes(bytesToSkip) != bytesToSkip) throw new IOException("Error skipping over blocks"); } else { for(int j = 0; j < numBlocks; j++) { v.visitEnclosingElement(ImageElement.BLOCK); v.visit(ImageElement.BLOCK_ID, in.readLong()); v.visit(ImageElement.NUM_BYTES, in.readLong()); v.visit(ImageElement.GENERATION_STAMP, in.readLong()); v.leaveEnclosingElement(); // Block } } v.leaveEnclosingElement(); // Blocks } /** * Extract the INode permissions stored in the fsimage file. * * @param in Datastream to process * @param v Visitor to walk over inodes */ private void processPermission(DataInputStream in, ImageVisitor v) throws IOException { v.visitEnclosingElement(ImageElement.PERMISSIONS); v.visit(ImageElement.USER_NAME, Text.readString(in)); v.visit(ImageElement.GROUP_NAME, Text.readString(in)); FsPermission fsp = new FsPermission(in.readShort()); v.visit(ImageElement.PERMISSION_STRING, fsp.toString()); v.leaveEnclosingElement(); // Permissions } /** * Process the INode records stored in the fsimage. * * @param in Datastream to process * @param v Visitor to walk over INodes * @param numInodes Number of INodes stored in file * @param skipBlocks Process all the blocks within the INode? * @param supportSnapshot Whether or not the imageVersion supports snapshot * @throws VisitException * @throws IOException */ private void processINodes(DataInputStream in, ImageVisitor v, long numInodes, boolean skipBlocks, boolean supportSnapshot) throws IOException { v.visitEnclosingElement(ImageElement.INODES, ImageElement.NUM_INODES, numInodes); if (LayoutVersion.supports(Feature.FSIMAGE_NAME_OPTIMIZATION, imageVersion)) { if (!supportSnapshot) { processLocalNameINodes(in, v, numInodes, skipBlocks); } else { processLocalNameINodesWithSnapshot(in, v, skipBlocks); } } else { // full path name processFullNameINodes(in, v, numInodes, skipBlocks); } v.leaveEnclosingElement(); // INodes } /** * Process image with full path name * * @param in image stream * @param v visitor * @param numInodes number of indoes to read * @param skipBlocks skip blocks or not * @throws IOException if there is any error occurs */ private void processLocalNameINodes(DataInputStream in, ImageVisitor v, long numInodes, boolean skipBlocks) throws IOException { // process root processINode(in, v, skipBlocks, "", false); numInodes--; while (numInodes > 0) { numInodes -= processDirectory(in, v, skipBlocks); } } private int processDirectory(DataInputStream in, ImageVisitor v, boolean skipBlocks) throws IOException { String parentName = FSImageSerialization.readString(in); return processChildren(in, v, skipBlocks, parentName); } /** * Process image with local path name and snapshot support * * @param in image stream * @param v visitor * @param skipBlocks skip blocks or not */ private void processLocalNameINodesWithSnapshot(DataInputStream in, ImageVisitor v, boolean skipBlocks) throws IOException { // process root processINode(in, v, skipBlocks, "", false); processDirectoryWithSnapshot(in, v, skipBlocks); } /** * Process directories when snapshot is supported. */ private void processDirectoryWithSnapshot(DataInputStream in, ImageVisitor v, boolean skipBlocks) throws IOException { // 1. load dir node id long inodeId = in.readLong(); String dirName = dirNodeMap.get(inodeId); String oldValue = subtreeMap.put(inodeId, dirName); if (oldValue != null) { // the subtree has been visited return; } // 2. load possible snapshots processSnapshots(in, v, dirName); // 3. load children nodes processChildren(in, v, skipBlocks, dirName); // 4. load possible directory diff list processDirectoryDiffList(in, v, dirName); // recursively process sub-directories final int numSubTree = in.readInt(); for (int i = 0; i < numSubTree; i++) { processDirectoryWithSnapshot(in, v, skipBlocks); } } /** * Process snapshots of a snapshottable directory */ private void processSnapshots(DataInputStream in, ImageVisitor v, String rootName) throws IOException { final int numSnapshots = in.readInt(); if (numSnapshots >= 0) { v.visitEnclosingElement(ImageElement.SNAPSHOTS, ImageElement.NUM_SNAPSHOTS, numSnapshots); for (int i = 0; i < numSnapshots; i++) { // process snapshot v.visitEnclosingElement(ImageElement.SNAPSHOT); v.visit(ImageElement.SNAPSHOT_ID, in.readInt()); v.leaveEnclosingElement(); } v.visit(ImageElement.SNAPSHOT_QUOTA, in.readInt()); v.leaveEnclosingElement(); } } private void processSnapshot(DataInputStream in, ImageVisitor v) throws IOException { v.visitEnclosingElement(ImageElement.SNAPSHOT); v.visit(ImageElement.SNAPSHOT_ID, in.readInt()); // process root of snapshot v.visitEnclosingElement(ImageElement.SNAPSHOT_ROOT); processINode(in, v, true, "", false); v.leaveEnclosingElement(); v.leaveEnclosingElement(); } private void processDirectoryDiffList(DataInputStream in, ImageVisitor v, String currentINodeName) throws IOException { final int numDirDiff = in.readInt(); if (numDirDiff >= 0) { v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFFS, ImageElement.NUM_SNAPSHOT_DIR_DIFF, numDirDiff); for (int i = 0; i < numDirDiff; i++) { // process directory diffs in reverse chronological oder processDirectoryDiff(in, v, currentINodeName); } v.leaveEnclosingElement(); } } private void processDirectoryDiff(DataInputStream in, ImageVisitor v, String currentINodeName) throws IOException { v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFF); int snapshotId = in.readInt(); v.visit(ImageElement.SNAPSHOT_DIFF_SNAPSHOTID, snapshotId); v.visit(ImageElement.SNAPSHOT_DIR_DIFF_CHILDREN_SIZE, in.readInt()); // process snapshotINode boolean useRoot = in.readBoolean(); if (!useRoot) { if (in.readBoolean()) { v.visitEnclosingElement(ImageElement.SNAPSHOT_INODE_DIRECTORY_ATTRIBUTES); if (LayoutVersion.supports(Feature.OPTIMIZE_SNAPSHOT_INODES, imageVersion)) { processINodeDirectoryAttributes(in, v, currentINodeName); } else { processINode(in, v, true, currentINodeName, true); } v.leaveEnclosingElement(); } } // process createdList int createdSize = in.readInt(); v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFF_CREATEDLIST, ImageElement.SNAPSHOT_DIR_DIFF_CREATEDLIST_SIZE, createdSize); for (int i = 0; i < createdSize; i++) { String createdNode = FSImageSerialization.readString(in); v.visit(ImageElement.SNAPSHOT_DIR_DIFF_CREATED_INODE, createdNode); } v.leaveEnclosingElement(); // process deletedList int deletedSize = in.readInt(); v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFF_DELETEDLIST, ImageElement.SNAPSHOT_DIR_DIFF_DELETEDLIST_SIZE, deletedSize); for (int i = 0; i < deletedSize; i++) { v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFF_DELETED_INODE); processINode(in, v, false, currentINodeName, true); v.leaveEnclosingElement(); } v.leaveEnclosingElement(); v.leaveEnclosingElement(); } private void processINodeDirectoryAttributes(DataInputStream in, ImageVisitor v, String parentName) throws IOException { final String pathName = readINodePath(in, parentName); v.visit(ImageElement.INODE_PATH, pathName); processPermission(in, v); v.visit(ImageElement.MODIFICATION_TIME, formatDate(in.readLong())); v.visit(ImageElement.NS_QUOTA, in.readLong()); v.visit(ImageElement.DS_QUOTA, in.readLong()); } /** Process children under a directory */ private int processChildren(DataInputStream in, ImageVisitor v, boolean skipBlocks, String parentName) throws IOException { int numChildren = in.readInt(); for (int i = 0; i < numChildren; i++) { processINode(in, v, skipBlocks, parentName, false); } return numChildren; } /** * Process image with full path name * * @param in image stream * @param v visitor * @param numInodes number of indoes to read * @param skipBlocks skip blocks or not * @throws IOException if there is any error occurs */ private void processFullNameINodes(DataInputStream in, ImageVisitor v, long numInodes, boolean skipBlocks) throws IOException { for(long i = 0; i < numInodes; i++) { processINode(in, v, skipBlocks, null, false); } } private String readINodePath(DataInputStream in, String parentName) throws IOException { String pathName = FSImageSerialization.readString(in); if (parentName != null) { // local name pathName = "/" + pathName; if (!"/".equals(parentName)) { // children of non-root directory pathName = parentName + pathName; } } return pathName; } /** * Process an INode * * @param in image stream * @param v visitor * @param skipBlocks skip blocks or not * @param parentName the name of its parent node * @param isSnapshotCopy whether or not the inode is a snapshot copy * @throws IOException */ private void processINode(DataInputStream in, ImageVisitor v, boolean skipBlocks, String parentName, boolean isSnapshotCopy) throws IOException { boolean supportSnapshot = LayoutVersion.supports(Feature.SNAPSHOT, imageVersion); boolean supportInodeId = LayoutVersion.supports(Feature.ADD_INODE_ID, imageVersion); v.visitEnclosingElement(ImageElement.INODE); final String pathName = readINodePath(in, parentName); v.visit(ImageElement.INODE_PATH, pathName); long inodeId = INodeId.GRANDFATHER_INODE_ID; if (supportInodeId) { inodeId = in.readLong(); v.visit(ImageElement.INODE_ID, inodeId); } v.visit(ImageElement.REPLICATION, in.readShort()); v.visit(ImageElement.MODIFICATION_TIME, formatDate(in.readLong())); if(LayoutVersion.supports(Feature.FILE_ACCESS_TIME, imageVersion)) v.visit(ImageElement.ACCESS_TIME, formatDate(in.readLong())); v.visit(ImageElement.BLOCK_SIZE, in.readLong()); int numBlocks = in.readInt(); processBlocks(in, v, numBlocks, skipBlocks); if (numBlocks >= 0) { // File if (supportSnapshot) { // process file diffs processFileDiffList(in, v, parentName); if (isSnapshotCopy) { boolean underConstruction = in.readBoolean(); if (underConstruction) { v.visit(ImageElement.CLIENT_NAME, FSImageSerialization.readString(in)); v.visit(ImageElement.CLIENT_MACHINE, FSImageSerialization.readString(in)); } } } processPermission(in, v); } else if (numBlocks == -1) { // Directory if (supportSnapshot && supportInodeId) { dirNodeMap.put(inodeId, pathName); } v.visit(ImageElement.NS_QUOTA, numBlocks == -1 ? in.readLong() : -1); if (LayoutVersion.supports(Feature.DISKSPACE_QUOTA, imageVersion)) v.visit(ImageElement.DS_QUOTA, numBlocks == -1 ? in.readLong() : -1); if (supportSnapshot) { boolean snapshottable = in.readBoolean(); if (!snapshottable) { boolean withSnapshot = in.readBoolean(); v.visit(ImageElement.IS_WITHSNAPSHOT_DIR, Boolean.toString(withSnapshot)); } else { v.visit(ImageElement.IS_SNAPSHOTTABLE_DIR, Boolean.toString(snapshottable)); } } processPermission(in, v); } else if (numBlocks == -2) { v.visit(ImageElement.SYMLINK, Text.readString(in)); } else if (numBlocks == -3) { // reference node final boolean isWithName = in.readBoolean(); int snapshotId = in.readInt(); if (isWithName) { v.visit(ImageElement.SNAPSHOT_LAST_SNAPSHOT_ID, snapshotId); } else { v.visit(ImageElement.SNAPSHOT_DST_SNAPSHOT_ID, snapshotId); } final boolean firstReferred = in.readBoolean(); if (firstReferred) { v.visitEnclosingElement(ImageElement.SNAPSHOT_REF_INODE); processINode(in, v, skipBlocks, parentName, isSnapshotCopy); v.leaveEnclosingElement(); // referred inode } else { v.visit(ImageElement.SNAPSHOT_REF_INODE_ID, in.readLong()); } } v.leaveEnclosingElement(); // INode } private void processINodeFileAttributes(DataInputStream in, ImageVisitor v, String parentName) throws IOException { final String pathName = readINodePath(in, parentName); v.visit(ImageElement.INODE_PATH, pathName); processPermission(in, v); v.visit(ImageElement.MODIFICATION_TIME, formatDate(in.readLong())); if(LayoutVersion.supports(Feature.FILE_ACCESS_TIME, imageVersion)) { v.visit(ImageElement.ACCESS_TIME, formatDate(in.readLong())); } v.visit(ImageElement.REPLICATION, in.readShort()); v.visit(ImageElement.BLOCK_SIZE, in.readLong()); } private void processFileDiffList(DataInputStream in, ImageVisitor v, String currentINodeName) throws IOException { final int size = in.readInt(); if (size >= 0) { v.visitEnclosingElement(ImageElement.SNAPSHOT_FILE_DIFFS, ImageElement.NUM_SNAPSHOT_FILE_DIFF, size); for (int i = 0; i < size; i++) { processFileDiff(in, v, currentINodeName); } v.leaveEnclosingElement(); } } private void processFileDiff(DataInputStream in, ImageVisitor v, String currentINodeName) throws IOException { int snapshotId = in.readInt(); v.visitEnclosingElement(ImageElement.SNAPSHOT_FILE_DIFF, ImageElement.SNAPSHOT_DIFF_SNAPSHOTID, snapshotId); v.visit(ImageElement.SNAPSHOT_FILE_SIZE, in.readLong()); if (in.readBoolean()) { v.visitEnclosingElement(ImageElement.SNAPSHOT_INODE_FILE_ATTRIBUTES); if (LayoutVersion.supports(Feature.OPTIMIZE_SNAPSHOT_INODES, imageVersion)) { processINodeFileAttributes(in, v, currentINodeName); } else { processINode(in, v, true, currentINodeName, true); } v.leaveEnclosingElement(); } v.leaveEnclosingElement(); } /** * Helper method to format dates during processing. * @param date Date as read from image file * @return String version of date format */ private String formatDate(long date) { return dateFormat.format(new Date(date)); } }