/** * 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.server.namenode; import java.util.zip.CheckedInputStream; import java.util.zip.Checksum; import java.util.EnumMap; import org.apache.hadoop.fs.ChecksumException; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; import org.apache.hadoop.fs.Options.Rename; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.fs.permission.PermissionStatus; import org.apache.hadoop.hdfs.protocol.Block; import org.apache.hadoop.hdfs.protocol.DatanodeID; import org.apache.hadoop.hdfs.protocol.LayoutVersion; import org.apache.hadoop.hdfs.protocol.LayoutVersion.Feature; import org.apache.hadoop.hdfs.server.blockmanagement.DatanodeDescriptor; import org.apache.hadoop.hdfs.server.common.GenerationStamp; import org.apache.hadoop.util.PureJavaCrc32; import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.*; import org.apache.hadoop.security.token.delegation.DelegationKey; import org.apache.hadoop.io.BytesWritable; import org.apache.hadoop.io.DataOutputBuffer; import org.apache.hadoop.io.ArrayWritable; import org.apache.hadoop.io.Writable; import org.apache.hadoop.io.WritableFactories; import org.apache.hadoop.io.WritableFactory; import org.apache.hadoop.hdfs.DeprecatedUTF8; import java.io.DataInput; import java.io.DataOutput; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.EOFException; /** * Helper classes for reading the ops from an InputStream. * All ops derive from FSEditLogOp and are only * instantiated from Reader#readOp() */ @InterfaceAudience.Private @InterfaceStability.Unstable public abstract class FSEditLogOp { final FSEditLogOpCodes opCode; long txid; @SuppressWarnings("deprecation") private static ThreadLocal<EnumMap<FSEditLogOpCodes, FSEditLogOp>> opInstances = new ThreadLocal<EnumMap<FSEditLogOpCodes, FSEditLogOp>>() { @Override protected EnumMap<FSEditLogOpCodes, FSEditLogOp> initialValue() { EnumMap<FSEditLogOpCodes, FSEditLogOp> instances = new EnumMap<FSEditLogOpCodes, FSEditLogOp>(FSEditLogOpCodes.class); instances.put(OP_ADD, new AddOp()); instances.put(OP_CLOSE, new CloseOp()); instances.put(OP_SET_REPLICATION, new SetReplicationOp()); instances.put(OP_CONCAT_DELETE, new ConcatDeleteOp()); instances.put(OP_RENAME_OLD, new RenameOldOp()); instances.put(OP_DELETE, new DeleteOp()); instances.put(OP_MKDIR, new MkdirOp()); instances.put(OP_SET_GENSTAMP, new SetGenstampOp()); instances.put(OP_DATANODE_ADD, new DatanodeAddOp()); instances.put(OP_DATANODE_REMOVE, new DatanodeRemoveOp()); instances.put(OP_SET_PERMISSIONS, new SetPermissionsOp()); instances.put(OP_SET_OWNER, new SetOwnerOp()); instances.put(OP_SET_NS_QUOTA, new SetNSQuotaOp()); instances.put(OP_CLEAR_NS_QUOTA, new ClearNSQuotaOp()); instances.put(OP_SET_QUOTA, new SetQuotaOp()); instances.put(OP_TIMES, new TimesOp()); instances.put(OP_SYMLINK, new SymlinkOp()); instances.put(OP_RENAME, new RenameOp()); instances.put(OP_REASSIGN_LEASE, new ReassignLeaseOp()); instances.put(OP_GET_DELEGATION_TOKEN, new GetDelegationTokenOp()); instances.put(OP_RENEW_DELEGATION_TOKEN, new RenewDelegationTokenOp()); instances.put(OP_CANCEL_DELEGATION_TOKEN, new CancelDelegationTokenOp()); instances.put(OP_UPDATE_MASTER_KEY, new UpdateMasterKeyOp()); instances.put(OP_START_LOG_SEGMENT, new LogSegmentOp(OP_START_LOG_SEGMENT)); instances.put(OP_END_LOG_SEGMENT, new LogSegmentOp(OP_END_LOG_SEGMENT)); return instances; } }; /** * Constructor for an EditLog Op. EditLog ops cannot be constructed * directly, but only through Reader#readOp. */ private FSEditLogOp(FSEditLogOpCodes opCode) { this.opCode = opCode; this.txid = 0; } public void setTransactionId(long txid) { this.txid = txid; } abstract void readFields(DataInputStream in, int logVersion) throws IOException; abstract void writeFields(DataOutputStream out) throws IOException; @SuppressWarnings("unchecked") static abstract class AddCloseOp extends FSEditLogOp { int length; String path; short replication; long mtime; long atime; long blockSize; Block[] blocks; PermissionStatus permissions; String clientName; String clientMachine; //final DatanodeDescriptor[] dataNodeDescriptors; UNUSED private AddCloseOp(FSEditLogOpCodes opCode) { super(opCode); assert(opCode == OP_ADD || opCode == OP_CLOSE); } <T extends AddCloseOp> T setPath(String path) { this.path = path; return (T)this; } <T extends AddCloseOp> T setReplication(short replication) { this.replication = replication; return (T)this; } <T extends AddCloseOp> T setModificationTime(long mtime) { this.mtime = mtime; return (T)this; } <T extends AddCloseOp> T setAccessTime(long atime) { this.atime = atime; return (T)this; } <T extends AddCloseOp> T setBlockSize(long blockSize) { this.blockSize = blockSize; return (T)this; } <T extends AddCloseOp> T setBlocks(Block[] blocks) { this.blocks = blocks; return (T)this; } <T extends AddCloseOp> T setPermissionStatus(PermissionStatus permissions) { this.permissions = permissions; return (T)this; } <T extends AddCloseOp> T setClientName(String clientName) { this.clientName = clientName; return (T)this; } <T extends AddCloseOp> T setClientMachine(String clientMachine) { this.clientMachine = clientMachine; return (T)this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeString(path, out); FSImageSerialization.writeShort(replication, out); FSImageSerialization.writeLong(mtime, out); FSImageSerialization.writeLong(atime, out); FSImageSerialization.writeLong(blockSize, out); new ArrayWritable(Block.class, blocks).write(out); permissions.write(out); if (this.opCode == OP_ADD) { FSImageSerialization.writeString(clientName,out); FSImageSerialization.writeString(clientMachine,out); } } @Override void readFields(DataInputStream in, int logVersion) throws IOException { // versions > 0 support per file replication // get name and replication if (!LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.length = in.readInt(); } if (-7 == logVersion && length != 3|| -17 < logVersion && logVersion < -7 && length != 4 || (logVersion <= -17 && length != 5 && !LayoutVersion.supports( Feature.EDITLOG_OP_OPTIMIZATION, logVersion))) { throw new IOException("Incorrect data format." + " logVersion is " + logVersion + " but writables.length is " + length + ". "); } this.path = FSImageSerialization.readString(in); if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.replication = FSImageSerialization.readShort(in); this.mtime = FSImageSerialization.readLong(in); } else { this.replication = readShort(in); this.mtime = readLong(in); } if (LayoutVersion.supports(Feature.FILE_ACCESS_TIME, logVersion)) { if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.atime = FSImageSerialization.readLong(in); } else { this.atime = readLong(in); } } else { this.atime = 0; } if (logVersion < -7) { if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.blockSize = FSImageSerialization.readLong(in); } else { this.blockSize = readLong(in); } } else { this.blockSize = 0; } // get blocks this.blocks = readBlocks(in, logVersion); if (logVersion <= -11) { this.permissions = PermissionStatus.read(in); } else { this.permissions = null; } // clientname, clientMachine and block locations of last block. if (this.opCode == OP_ADD && logVersion <= -12) { this.clientName = FSImageSerialization.readString(in); this.clientMachine = FSImageSerialization.readString(in); if (-13 <= logVersion) { readDatanodeDescriptorArray(in); } } else { this.clientName = ""; this.clientMachine = ""; } } /** This method is defined for compatibility reason. */ private static DatanodeDescriptor[] readDatanodeDescriptorArray(DataInput in) throws IOException { DatanodeDescriptor[] locations = new DatanodeDescriptor[in.readInt()]; for (int i = 0; i < locations.length; i++) { locations[i] = new DatanodeDescriptor(); locations[i].readFieldsFromFSEditLog(in); } return locations; } private static Block[] readBlocks( DataInputStream in, int logVersion) throws IOException { int numBlocks = in.readInt(); Block[] blocks = new Block[numBlocks]; for (int i = 0; i < numBlocks; i++) { Block blk = new Block(); if (logVersion <= -14) { blk.readFields(in); } else { BlockTwo oldblk = new BlockTwo(); oldblk.readFields(in); blk.set(oldblk.blkid, oldblk.len, GenerationStamp.GRANDFATHER_GENERATION_STAMP); } blocks[i] = blk; } return blocks; } } static class AddOp extends AddCloseOp { private AddOp() { super(OP_ADD); } static AddOp getInstance() { return (AddOp)opInstances.get().get(OP_ADD); } } static class CloseOp extends AddCloseOp { private CloseOp() { super(OP_CLOSE); } static CloseOp getInstance() { return (CloseOp)opInstances.get().get(OP_CLOSE); } } static class SetReplicationOp extends FSEditLogOp { String path; short replication; private SetReplicationOp() { super(OP_SET_REPLICATION); } static SetReplicationOp getInstance() { return (SetReplicationOp)opInstances.get() .get(OP_SET_REPLICATION); } SetReplicationOp setPath(String path) { this.path = path; return this; } SetReplicationOp setReplication(short replication) { this.replication = replication; return this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeString(path, out); FSImageSerialization.writeShort(replication, out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { this.path = FSImageSerialization.readString(in); if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.replication = FSImageSerialization.readShort(in); } else { this.replication = readShort(in); } } } static class ConcatDeleteOp extends FSEditLogOp { int length; String trg; String[] srcs; long timestamp; private ConcatDeleteOp() { super(OP_CONCAT_DELETE); } static ConcatDeleteOp getInstance() { return (ConcatDeleteOp)opInstances.get() .get(OP_CONCAT_DELETE); } ConcatDeleteOp setTarget(String trg) { this.trg = trg; return this; } ConcatDeleteOp setSources(String[] srcs) { this.srcs = srcs; return this; } ConcatDeleteOp setTimestamp(long timestamp) { this.timestamp = timestamp; return this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeString(trg, out); DeprecatedUTF8 info[] = new DeprecatedUTF8[srcs.length]; int idx = 0; for(int i=0; i<srcs.length; i++) { info[idx++] = new DeprecatedUTF8(srcs[i]); } new ArrayWritable(DeprecatedUTF8.class, info).write(out); FSImageSerialization.writeLong(timestamp, out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { if (!LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.length = in.readInt(); if (length < 3) { // trg, srcs.., timestamp throw new IOException("Incorrect data format. " + "Concat delete operation."); } } this.trg = FSImageSerialization.readString(in); int srcSize = 0; if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { srcSize = in.readInt(); } else { srcSize = this.length - 1 - 1; // trg and timestamp } this.srcs = new String [srcSize]; for(int i=0; i<srcSize;i++) { srcs[i]= FSImageSerialization.readString(in); } if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.timestamp = FSImageSerialization.readLong(in); } else { this.timestamp = readLong(in); } } } static class RenameOldOp extends FSEditLogOp { int length; String src; String dst; long timestamp; private RenameOldOp() { super(OP_RENAME_OLD); } static RenameOldOp getInstance() { return (RenameOldOp)opInstances.get() .get(OP_RENAME_OLD); } RenameOldOp setSource(String src) { this.src = src; return this; } RenameOldOp setDestination(String dst) { this.dst = dst; return this; } RenameOldOp setTimestamp(long timestamp) { this.timestamp = timestamp; return this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeString(src, out); FSImageSerialization.writeString(dst, out); FSImageSerialization.writeLong(timestamp, out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { if (!LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.length = in.readInt(); if (this.length != 3) { throw new IOException("Incorrect data format. " + "Old rename operation."); } } this.src = FSImageSerialization.readString(in); this.dst = FSImageSerialization.readString(in); if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.timestamp = FSImageSerialization.readLong(in); } else { this.timestamp = readLong(in); } } } static class DeleteOp extends FSEditLogOp { int length; String path; long timestamp; private DeleteOp() { super(OP_DELETE); } static DeleteOp getInstance() { return (DeleteOp)opInstances.get() .get(OP_DELETE); } DeleteOp setPath(String path) { this.path = path; return this; } DeleteOp setTimestamp(long timestamp) { this.timestamp = timestamp; return this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeString(path, out); FSImageSerialization.writeLong(timestamp, out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { if (!LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.length = in.readInt(); if (this.length != 2) { throw new IOException("Incorrect data format. " + "delete operation."); } } this.path = FSImageSerialization.readString(in); if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.timestamp = FSImageSerialization.readLong(in); } else { this.timestamp = readLong(in); } } } static class MkdirOp extends FSEditLogOp { int length; String path; long timestamp; PermissionStatus permissions; private MkdirOp() { super(OP_MKDIR); } static MkdirOp getInstance() { return (MkdirOp)opInstances.get() .get(OP_MKDIR); } MkdirOp setPath(String path) { this.path = path; return this; } MkdirOp setTimestamp(long timestamp) { this.timestamp = timestamp; return this; } MkdirOp setPermissionStatus(PermissionStatus permissions) { this.permissions = permissions; return this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeString(path, out); FSImageSerialization.writeLong(timestamp, out); // mtime FSImageSerialization.writeLong(timestamp, out); // atime, unused at this permissions.write(out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { if (!LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.length = in.readInt(); } if (-17 < logVersion && length != 2 || logVersion <= -17 && length != 3 && !LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { throw new IOException("Incorrect data format. " + "Mkdir operation."); } this.path = FSImageSerialization.readString(in); if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.timestamp = FSImageSerialization.readLong(in); } else { this.timestamp = readLong(in); } // The disk format stores atimes for directories as well. // However, currently this is not being updated/used because of // performance reasons. if (LayoutVersion.supports(Feature.FILE_ACCESS_TIME, logVersion)) { /* unused this.atime = */ if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { FSImageSerialization.readLong(in); } else { readLong(in); } } if (logVersion <= -11) { this.permissions = PermissionStatus.read(in); } else { this.permissions = null; } } } static class SetGenstampOp extends FSEditLogOp { long genStamp; private SetGenstampOp() { super(OP_SET_GENSTAMP); } static SetGenstampOp getInstance() { return (SetGenstampOp)opInstances.get() .get(OP_SET_GENSTAMP); } SetGenstampOp setGenerationStamp(long genStamp) { this.genStamp = genStamp; return this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeLong(genStamp, out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { this.genStamp = FSImageSerialization.readLong(in); } } @SuppressWarnings("deprecation") static class DatanodeAddOp extends FSEditLogOp { private DatanodeAddOp() { super(OP_DATANODE_ADD); } static DatanodeAddOp getInstance() { return (DatanodeAddOp)opInstances.get() .get(OP_DATANODE_ADD); } @Override void writeFields(DataOutputStream out) throws IOException { throw new IOException("Deprecated, should not write"); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { //Datanodes are not persistent any more. FSImageSerialization.DatanodeImage.skipOne(in); } } @SuppressWarnings("deprecation") static class DatanodeRemoveOp extends FSEditLogOp { private DatanodeRemoveOp() { super(OP_DATANODE_REMOVE); } static DatanodeRemoveOp getInstance() { return (DatanodeRemoveOp)opInstances.get() .get(OP_DATANODE_REMOVE); } @Override void writeFields(DataOutputStream out) throws IOException { throw new IOException("Deprecated, should not write"); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { DatanodeID nodeID = new DatanodeID(); nodeID.readFields(in); //Datanodes are not persistent any more. } } static class SetPermissionsOp extends FSEditLogOp { String src; FsPermission permissions; private SetPermissionsOp() { super(OP_SET_PERMISSIONS); } static SetPermissionsOp getInstance() { return (SetPermissionsOp)opInstances.get() .get(OP_SET_PERMISSIONS); } SetPermissionsOp setSource(String src) { this.src = src; return this; } SetPermissionsOp setPermissions(FsPermission permissions) { this.permissions = permissions; return this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeString(src, out); permissions.write(out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { this.src = FSImageSerialization.readString(in); this.permissions = FsPermission.read(in); } } static class SetOwnerOp extends FSEditLogOp { String src; String username; String groupname; private SetOwnerOp() { super(OP_SET_OWNER); } static SetOwnerOp getInstance() { return (SetOwnerOp)opInstances.get() .get(OP_SET_OWNER); } SetOwnerOp setSource(String src) { this.src = src; return this; } SetOwnerOp setUser(String username) { this.username = username; return this; } SetOwnerOp setGroup(String groupname) { this.groupname = groupname; return this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeString(src, out); FSImageSerialization.writeString(username == null ? "" : username, out); FSImageSerialization.writeString(groupname == null ? "" : groupname, out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { this.src = FSImageSerialization.readString(in); this.username = FSImageSerialization.readString_EmptyAsNull(in); this.groupname = FSImageSerialization.readString_EmptyAsNull(in); } } static class SetNSQuotaOp extends FSEditLogOp { String src; long nsQuota; private SetNSQuotaOp() { super(OP_SET_NS_QUOTA); } static SetNSQuotaOp getInstance() { return (SetNSQuotaOp)opInstances.get() .get(OP_SET_NS_QUOTA); } @Override void writeFields(DataOutputStream out) throws IOException { throw new IOException("Deprecated"); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { this.src = FSImageSerialization.readString(in); this.nsQuota = FSImageSerialization.readLong(in); } } static class ClearNSQuotaOp extends FSEditLogOp { String src; private ClearNSQuotaOp() { super(OP_CLEAR_NS_QUOTA); } static ClearNSQuotaOp getInstance() { return (ClearNSQuotaOp)opInstances.get() .get(OP_CLEAR_NS_QUOTA); } @Override void writeFields(DataOutputStream out) throws IOException { throw new IOException("Deprecated"); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { this.src = FSImageSerialization.readString(in); } } static class SetQuotaOp extends FSEditLogOp { String src; long nsQuota; long dsQuota; private SetQuotaOp() { super(OP_SET_QUOTA); } static SetQuotaOp getInstance() { return (SetQuotaOp)opInstances.get() .get(OP_SET_QUOTA); } SetQuotaOp setSource(String src) { this.src = src; return this; } SetQuotaOp setNSQuota(long nsQuota) { this.nsQuota = nsQuota; return this; } SetQuotaOp setDSQuota(long dsQuota) { this.dsQuota = dsQuota; return this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeString(src, out); FSImageSerialization.writeLong(nsQuota, out); FSImageSerialization.writeLong(dsQuota, out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { this.src = FSImageSerialization.readString(in); this.nsQuota = FSImageSerialization.readLong(in); this.dsQuota = FSImageSerialization.readLong(in); } } static class TimesOp extends FSEditLogOp { int length; String path; long mtime; long atime; private TimesOp() { super(OP_TIMES); } static TimesOp getInstance() { return (TimesOp)opInstances.get() .get(OP_TIMES); } TimesOp setPath(String path) { this.path = path; return this; } TimesOp setModificationTime(long mtime) { this.mtime = mtime; return this; } TimesOp setAccessTime(long atime) { this.atime = atime; return this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeString(path, out); FSImageSerialization.writeLong(mtime, out); FSImageSerialization.writeLong(atime, out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { if (!LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.length = in.readInt(); if (length != 3) { throw new IOException("Incorrect data format. " + "times operation."); } } this.path = FSImageSerialization.readString(in); if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.mtime = FSImageSerialization.readLong(in); this.atime = FSImageSerialization.readLong(in); } else { this.mtime = readLong(in); this.atime = readLong(in); } } } static class SymlinkOp extends FSEditLogOp { int length; String path; String value; long mtime; long atime; PermissionStatus permissionStatus; private SymlinkOp() { super(OP_SYMLINK); } static SymlinkOp getInstance() { return (SymlinkOp)opInstances.get() .get(OP_SYMLINK); } SymlinkOp setPath(String path) { this.path = path; return this; } SymlinkOp setValue(String value) { this.value = value; return this; } SymlinkOp setModificationTime(long mtime) { this.mtime = mtime; return this; } SymlinkOp setAccessTime(long atime) { this.atime = atime; return this; } SymlinkOp setPermissionStatus(PermissionStatus permissionStatus) { this.permissionStatus = permissionStatus; return this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeString(path, out); FSImageSerialization.writeString(value, out); FSImageSerialization.writeLong(mtime, out); FSImageSerialization.writeLong(atime, out); permissionStatus.write(out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { if (!LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.length = in.readInt(); if (this.length != 4) { throw new IOException("Incorrect data format. " + "symlink operation."); } } this.path = FSImageSerialization.readString(in); this.value = FSImageSerialization.readString(in); if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.mtime = FSImageSerialization.readLong(in); this.atime = FSImageSerialization.readLong(in); } else { this.mtime = readLong(in); this.atime = readLong(in); } this.permissionStatus = PermissionStatus.read(in); } } static class RenameOp extends FSEditLogOp { int length; String src; String dst; long timestamp; Rename[] options; private RenameOp() { super(OP_RENAME); } static RenameOp getInstance() { return (RenameOp)opInstances.get() .get(OP_RENAME); } RenameOp setSource(String src) { this.src = src; return this; } RenameOp setDestination(String dst) { this.dst = dst; return this; } RenameOp setTimestamp(long timestamp) { this.timestamp = timestamp; return this; } RenameOp setOptions(Rename[] options) { this.options = options; return this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeString(src, out); FSImageSerialization.writeString(dst, out); FSImageSerialization.writeLong(timestamp, out); toBytesWritable(options).write(out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { if (!LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.length = in.readInt(); if (this.length != 3) { throw new IOException("Incorrect data format. " + "Rename operation."); } } this.src = FSImageSerialization.readString(in); this.dst = FSImageSerialization.readString(in); if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.timestamp = FSImageSerialization.readLong(in); } else { this.timestamp = readLong(in); } this.options = readRenameOptions(in); } private static Rename[] readRenameOptions(DataInputStream in) throws IOException { BytesWritable writable = new BytesWritable(); writable.readFields(in); byte[] bytes = writable.getBytes(); Rename[] options = new Rename[bytes.length]; for (int i = 0; i < bytes.length; i++) { options[i] = Rename.valueOf(bytes[i]); } return options; } static BytesWritable toBytesWritable(Rename... options) { byte[] bytes = new byte[options.length]; for (int i = 0; i < options.length; i++) { bytes[i] = options[i].value(); } return new BytesWritable(bytes); } } static class ReassignLeaseOp extends FSEditLogOp { String leaseHolder; String path; String newHolder; private ReassignLeaseOp() { super(OP_REASSIGN_LEASE); } static ReassignLeaseOp getInstance() { return (ReassignLeaseOp)opInstances.get() .get(OP_REASSIGN_LEASE); } ReassignLeaseOp setLeaseHolder(String leaseHolder) { this.leaseHolder = leaseHolder; return this; } ReassignLeaseOp setPath(String path) { this.path = path; return this; } ReassignLeaseOp setNewHolder(String newHolder) { this.newHolder = newHolder; return this; } @Override void writeFields(DataOutputStream out) throws IOException { FSImageSerialization.writeString(leaseHolder, out); FSImageSerialization.writeString(path, out); FSImageSerialization.writeString(newHolder, out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { this.leaseHolder = FSImageSerialization.readString(in); this.path = FSImageSerialization.readString(in); this.newHolder = FSImageSerialization.readString(in); } } static class GetDelegationTokenOp extends FSEditLogOp { DelegationTokenIdentifier token; long expiryTime; private GetDelegationTokenOp() { super(OP_GET_DELEGATION_TOKEN); } static GetDelegationTokenOp getInstance() { return (GetDelegationTokenOp)opInstances.get() .get(OP_GET_DELEGATION_TOKEN); } GetDelegationTokenOp setDelegationTokenIdentifier( DelegationTokenIdentifier token) { this.token = token; return this; } GetDelegationTokenOp setExpiryTime(long expiryTime) { this.expiryTime = expiryTime; return this; } @Override void writeFields(DataOutputStream out) throws IOException { token.write(out); FSImageSerialization.writeLong(expiryTime, out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { this.token = new DelegationTokenIdentifier(); this.token.readFields(in); if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.expiryTime = FSImageSerialization.readLong(in); } else { this.expiryTime = readLong(in); } } } static class RenewDelegationTokenOp extends FSEditLogOp { DelegationTokenIdentifier token; long expiryTime; private RenewDelegationTokenOp() { super(OP_RENEW_DELEGATION_TOKEN); } static RenewDelegationTokenOp getInstance() { return (RenewDelegationTokenOp)opInstances.get() .get(OP_RENEW_DELEGATION_TOKEN); } RenewDelegationTokenOp setDelegationTokenIdentifier( DelegationTokenIdentifier token) { this.token = token; return this; } RenewDelegationTokenOp setExpiryTime(long expiryTime) { this.expiryTime = expiryTime; return this; } @Override void writeFields(DataOutputStream out) throws IOException { token.write(out); FSImageSerialization.writeLong(expiryTime, out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { this.token = new DelegationTokenIdentifier(); this.token.readFields(in); if (LayoutVersion.supports(Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) { this.expiryTime = FSImageSerialization.readLong(in); } else { this.expiryTime = readLong(in); } } } static class CancelDelegationTokenOp extends FSEditLogOp { DelegationTokenIdentifier token; private CancelDelegationTokenOp() { super(OP_CANCEL_DELEGATION_TOKEN); } static CancelDelegationTokenOp getInstance() { return (CancelDelegationTokenOp)opInstances.get() .get(OP_CANCEL_DELEGATION_TOKEN); } CancelDelegationTokenOp setDelegationTokenIdentifier( DelegationTokenIdentifier token) { this.token = token; return this; } @Override void writeFields(DataOutputStream out) throws IOException { token.write(out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { this.token = new DelegationTokenIdentifier(); this.token.readFields(in); } } static class UpdateMasterKeyOp extends FSEditLogOp { DelegationKey key; private UpdateMasterKeyOp() { super(OP_UPDATE_MASTER_KEY); } static UpdateMasterKeyOp getInstance() { return (UpdateMasterKeyOp)opInstances.get() .get(OP_UPDATE_MASTER_KEY); } UpdateMasterKeyOp setDelegationKey(DelegationKey key) { this.key = key; return this; } @Override void writeFields(DataOutputStream out) throws IOException { key.write(out); } @Override void readFields(DataInputStream in, int logVersion) throws IOException { this.key = new DelegationKey(); this.key.readFields(in); } } static class LogSegmentOp extends FSEditLogOp { private LogSegmentOp(FSEditLogOpCodes code) { super(code); assert code == OP_START_LOG_SEGMENT || code == OP_END_LOG_SEGMENT : "Bad op: " + code; } static LogSegmentOp getInstance(FSEditLogOpCodes code) { return (LogSegmentOp)opInstances.get().get(code); } public void readFields(DataInputStream in, int logVersion) throws IOException { // no data stored in these ops yet } @Override void writeFields(DataOutputStream out) throws IOException { // no data stored } } static class InvalidOp extends FSEditLogOp { private InvalidOp() { super(OP_INVALID); } static InvalidOp getInstance() { return (InvalidOp)opInstances.get().get(OP_INVALID); } @Override void writeFields(DataOutputStream out) throws IOException { } @Override void readFields(DataInputStream in, int logVersion) throws IOException { // nothing to read } } static private short readShort(DataInputStream in) throws IOException { return Short.parseShort(FSImageSerialization.readString(in)); } static private long readLong(DataInputStream in) throws IOException { return Long.parseLong(FSImageSerialization.readString(in)); } /** * A class to read in blocks stored in the old format. The only two * fields in the block were blockid and length. */ static class BlockTwo implements Writable { long blkid; long len; static { // register a ctor WritableFactories.setFactory (BlockTwo.class, new WritableFactory() { public Writable newInstance() { return new BlockTwo(); } }); } BlockTwo() { blkid = 0; len = 0; } ///////////////////////////////////// // Writable ///////////////////////////////////// public void write(DataOutput out) throws IOException { out.writeLong(blkid); out.writeLong(len); } public void readFields(DataInput in) throws IOException { this.blkid = in.readLong(); this.len = in.readLong(); } } /** * Class for writing editlog ops */ public static class Writer { private final DataOutputBuffer buf; private final Checksum checksum; public Writer(DataOutputBuffer out) { this.buf = out; this.checksum = new PureJavaCrc32(); } /** * Write an operation to the output stream * * @param op The operation to write * @throws IOException if an error occurs during writing. */ public void writeOp(FSEditLogOp op) throws IOException { int start = buf.getLength(); buf.writeByte(op.opCode.getOpCode()); buf.writeLong(op.txid); op.writeFields(buf); int end = buf.getLength(); checksum.reset(); checksum.update(buf.getData(), start, end-start); int sum = (int)checksum.getValue(); buf.writeInt(sum); } } /** * Class for reading editlog ops from a stream */ public static class Reader { private final DataInputStream in; private final int logVersion; private final Checksum checksum; /** * Construct the reader * @param in The stream to read from. * @param logVersion The version of the data coming from the stream. */ @SuppressWarnings("deprecation") public Reader(DataInputStream in, int logVersion) { this.logVersion = logVersion; if (LayoutVersion.supports(Feature.EDITS_CHESKUM, logVersion)) { this.checksum = new PureJavaCrc32(); } else { this.checksum = null; } if (this.checksum != null) { this.in = new DataInputStream( new CheckedInputStream(in, this.checksum)); } else { this.in = in; } } /** * Read an operation from the input stream. * * Note that the objects returned from this method may be re-used by future * calls to the same method. * * @return the operation read from the stream, or null at the end of the file * @throws IOException on error. */ public FSEditLogOp readOp() throws IOException { if (checksum != null) { checksum.reset(); } in.mark(1); byte opCodeByte; try { opCodeByte = in.readByte(); } catch (EOFException eof) { // EOF at an opcode boundary is expected. return null; } FSEditLogOpCodes opCode = FSEditLogOpCodes.fromByte(opCodeByte); if (opCode == OP_INVALID) { in.reset(); // reset back to end of file if somebody reads it again return null; } FSEditLogOp op = opInstances.get().get(opCode); if (op == null) { throw new IOException("Read invalid opcode " + opCode); } if (LayoutVersion.supports(Feature.STORED_TXIDS, logVersion)) { // Read the txid op.setTransactionId(in.readLong()); } op.readFields(in, logVersion); validateChecksum(in, checksum, op.txid); return op; } /** * Validate a transaction's checksum */ private void validateChecksum(DataInputStream in, Checksum checksum, long txid) throws IOException { if (checksum != null) { int calculatedChecksum = (int)checksum.getValue(); int readChecksum = in.readInt(); // read in checksum if (readChecksum != calculatedChecksum) { throw new ChecksumException( "Transaction is corrupt. Calculated checksum is " + calculatedChecksum + " but read checksum " + readChecksum, txid); } } } } }