001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hdfs.server.namenode.snapshot; 019 020import java.io.DataInput; 021import java.io.DataOutput; 022import java.io.IOException; 023import java.text.SimpleDateFormat; 024import java.util.Comparator; 025import java.util.Date; 026 027import org.apache.hadoop.classification.InterfaceAudience; 028import org.apache.hadoop.fs.Path; 029import org.apache.hadoop.hdfs.DFSUtil; 030import org.apache.hadoop.hdfs.protocol.HdfsConstants; 031import org.apache.hadoop.hdfs.server.namenode.FSImageFormat; 032import org.apache.hadoop.hdfs.server.namenode.FSImageSerialization; 033import org.apache.hadoop.hdfs.server.namenode.INode; 034import org.apache.hadoop.hdfs.server.namenode.INodeDirectory; 035import org.apache.hadoop.hdfs.util.ReadOnlyList; 036 037/** Snapshot of a sub-tree in the namesystem. */ 038@InterfaceAudience.Private 039public class Snapshot implements Comparable<byte[]> { 040 public static final int INVALID_ID = -1; 041 042 /** 043 * The pattern for generating the default snapshot name. 044 * E.g. s20130412-151029.033 045 */ 046 private static final String DEFAULT_SNAPSHOT_NAME_PATTERN = "'s'yyyyMMdd-HHmmss.SSS"; 047 048 public static String generateDefaultSnapshotName() { 049 return new SimpleDateFormat(DEFAULT_SNAPSHOT_NAME_PATTERN).format(new Date()); 050 } 051 052 public static String getSnapshotPath(String snapshottableDir, 053 String snapshotRelativePath) { 054 final StringBuilder b = new StringBuilder(snapshottableDir); 055 if (b.charAt(b.length() - 1) != Path.SEPARATOR_CHAR) { 056 b.append(Path.SEPARATOR); 057 } 058 return b.append(HdfsConstants.DOT_SNAPSHOT_DIR) 059 .append(Path.SEPARATOR) 060 .append(snapshotRelativePath) 061 .toString(); 062 } 063 064 /** 065 * Get the name of the given snapshot. 066 * @param s The given snapshot. 067 * @return The name of the snapshot, or an empty string if {@code s} is null 068 */ 069 static String getSnapshotName(Snapshot s) { 070 return s != null ? s.getRoot().getLocalName() : ""; 071 } 072 073 /** 074 * Compare snapshot with IDs, where null indicates the current status thus 075 * is greater than any non-null snapshot. 076 */ 077 public static final Comparator<Snapshot> ID_COMPARATOR 078 = new Comparator<Snapshot>() { 079 @Override 080 public int compare(Snapshot left, Snapshot right) { 081 return ID_INTEGER_COMPARATOR.compare( 082 left == null? null: left.getId(), 083 right == null? null: right.getId()); 084 } 085 }; 086 087 /** 088 * Compare snapshot with IDs, where null indicates the current status thus 089 * is greater than any non-null ID. 090 */ 091 public static final Comparator<Integer> ID_INTEGER_COMPARATOR 092 = new Comparator<Integer>() { 093 @Override 094 public int compare(Integer left, Integer right) { 095 // null means the current state, thus should be the largest 096 if (left == null) { 097 return right == null? 0: 1; 098 } else { 099 return right == null? -1: left - right; 100 } 101 } 102 }; 103 104 /** 105 * Find the latest snapshot that 1) covers the given inode (which means the 106 * snapshot was either taken on the inode or taken on an ancestor of the 107 * inode), and 2) was taken before the given snapshot (if the given snapshot 108 * is not null). 109 * 110 * @param inode the given inode that the returned snapshot needs to cover 111 * @param anchor the returned snapshot should be taken before this snapshot. 112 * @return the latest snapshot covers the given inode and was taken before the 113 * the given snapshot (if it is not null). 114 */ 115 public static Snapshot findLatestSnapshot(INode inode, Snapshot anchor) { 116 Snapshot latest = null; 117 for(; inode != null; inode = inode.getParent()) { 118 if (inode.isDirectory()) { 119 final INodeDirectory dir = inode.asDirectory(); 120 if (dir instanceof INodeDirectoryWithSnapshot) { 121 latest = ((INodeDirectoryWithSnapshot) dir).getDiffs().updatePrior( 122 anchor, latest); 123 } 124 } 125 } 126 return latest; 127 } 128 129 static Snapshot read(DataInput in, FSImageFormat.Loader loader) 130 throws IOException { 131 final int snapshotId = in.readInt(); 132 final INode root = loader.loadINodeWithLocalName(false, in, false); 133 return new Snapshot(snapshotId, root.asDirectory(), null); 134 } 135 136 /** The root directory of the snapshot. */ 137 static public class Root extends INodeDirectory { 138 Root(INodeDirectory other) { 139 super(other, false); 140 } 141 142 @Override 143 public ReadOnlyList<INode> getChildrenList(Snapshot snapshot) { 144 return getParent().getChildrenList(snapshot); 145 } 146 147 @Override 148 public INode getChild(byte[] name, Snapshot snapshot) { 149 return getParent().getChild(name, snapshot); 150 } 151 152 @Override 153 public String getFullPathName() { 154 return getSnapshotPath(getParent().getFullPathName(), getLocalName()); 155 } 156 } 157 158 /** Snapshot ID. */ 159 private final int id; 160 /** The root directory of the snapshot. */ 161 private final Root root; 162 163 Snapshot(int id, String name, INodeDirectorySnapshottable dir) { 164 this(id, dir, dir); 165 this.root.setLocalName(DFSUtil.string2Bytes(name)); 166 } 167 168 Snapshot(int id, INodeDirectory dir, INodeDirectorySnapshottable parent) { 169 this.id = id; 170 this.root = new Root(dir); 171 172 this.root.setParent(parent); 173 } 174 175 public int getId() { 176 return id; 177 } 178 179 /** @return the root directory of the snapshot. */ 180 public Root getRoot() { 181 return root; 182 } 183 184 @Override 185 public int compareTo(byte[] bytes) { 186 return root.compareTo(bytes); 187 } 188 189 @Override 190 public boolean equals(Object that) { 191 if (this == that) { 192 return true; 193 } else if (that == null || !(that instanceof Snapshot)) { 194 return false; 195 } 196 return this.id == ((Snapshot)that).id; 197 } 198 199 @Override 200 public int hashCode() { 201 return id; 202 } 203 204 @Override 205 public String toString() { 206 return getClass().getSimpleName() + "." + root.getLocalName() + "(id=" + id + ")"; 207 } 208 209 /** Serialize the fields to out */ 210 void write(DataOutput out) throws IOException { 211 out.writeInt(id); 212 // write root 213 FSImageSerialization.writeINodeDirectory(root, out); 214 } 215}