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 */
018 package org.apache.hadoop.hdfs.server.namenode.snapshot;
019
020 import java.io.DataInput;
021 import java.io.IOException;
022 import java.text.SimpleDateFormat;
023 import java.util.Arrays;
024 import java.util.Comparator;
025 import java.util.Date;
026
027 import org.apache.hadoop.classification.InterfaceAudience;
028 import org.apache.hadoop.fs.Path;
029 import org.apache.hadoop.hdfs.DFSUtil;
030 import org.apache.hadoop.hdfs.protocol.HdfsConstants;
031 import org.apache.hadoop.hdfs.server.namenode.AclFeature;
032 import org.apache.hadoop.hdfs.server.namenode.FSImageFormat;
033 import org.apache.hadoop.hdfs.server.namenode.INode;
034 import org.apache.hadoop.hdfs.server.namenode.INodeDirectory;
035 import org.apache.hadoop.hdfs.util.ReadOnlyList;
036
037 import com.google.common.collect.Iterables;
038 import com.google.common.collect.Lists;
039
040 /** Snapshot of a sub-tree in the namesystem. */
041 @InterfaceAudience.Private
042 public class Snapshot implements Comparable<byte[]> {
043 /**
044 * This id is used to indicate the current state (vs. snapshots)
045 */
046 public static final int CURRENT_STATE_ID = Integer.MAX_VALUE - 1;
047 public static final int NO_SNAPSHOT_ID = -1;
048
049 /**
050 * The pattern for generating the default snapshot name.
051 * E.g. s20130412-151029.033
052 */
053 private static final String DEFAULT_SNAPSHOT_NAME_PATTERN = "'s'yyyyMMdd-HHmmss.SSS";
054
055 public static String generateDefaultSnapshotName() {
056 return new SimpleDateFormat(DEFAULT_SNAPSHOT_NAME_PATTERN).format(new Date());
057 }
058
059 public static String getSnapshotPath(String snapshottableDir,
060 String snapshotRelativePath) {
061 final StringBuilder b = new StringBuilder(snapshottableDir);
062 if (b.charAt(b.length() - 1) != Path.SEPARATOR_CHAR) {
063 b.append(Path.SEPARATOR);
064 }
065 return b.append(HdfsConstants.DOT_SNAPSHOT_DIR)
066 .append(Path.SEPARATOR)
067 .append(snapshotRelativePath)
068 .toString();
069 }
070
071 /**
072 * Get the name of the given snapshot.
073 * @param s The given snapshot.
074 * @return The name of the snapshot, or an empty string if {@code s} is null
075 */
076 static String getSnapshotName(Snapshot s) {
077 return s != null ? s.getRoot().getLocalName() : "";
078 }
079
080 public static int getSnapshotId(Snapshot s) {
081 return s == null ? CURRENT_STATE_ID : s.getId();
082 }
083
084 /**
085 * Compare snapshot with IDs, where null indicates the current status thus
086 * is greater than any non-null snapshot.
087 */
088 public static final Comparator<Snapshot> ID_COMPARATOR
089 = new Comparator<Snapshot>() {
090 @Override
091 public int compare(Snapshot left, Snapshot right) {
092 return ID_INTEGER_COMPARATOR.compare(Snapshot.getSnapshotId(left),
093 Snapshot.getSnapshotId(right));
094 }
095 };
096
097 /**
098 * Compare snapshot with IDs, where null indicates the current status thus
099 * is greater than any non-null ID.
100 */
101 public static final Comparator<Integer> ID_INTEGER_COMPARATOR
102 = new Comparator<Integer>() {
103 @Override
104 public int compare(Integer left, Integer right) {
105 // Snapshot.CURRENT_STATE_ID means the current state, thus should be the
106 // largest
107 return left - right;
108 }
109 };
110
111 /**
112 * Find the latest snapshot that 1) covers the given inode (which means the
113 * snapshot was either taken on the inode or taken on an ancestor of the
114 * inode), and 2) was taken before the given snapshot (if the given snapshot
115 * is not null).
116 *
117 * @param inode the given inode that the returned snapshot needs to cover
118 * @param anchor the returned snapshot should be taken before this given id.
119 * @return id of the latest snapshot that covers the given inode and was taken
120 * before the the given snapshot (if it is not null).
121 */
122 public static int findLatestSnapshot(INode inode, final int anchor) {
123 int latest = NO_SNAPSHOT_ID;
124 for(; inode != null; inode = inode.getParent()) {
125 if (inode.isDirectory()) {
126 final INodeDirectory dir = inode.asDirectory();
127 if (dir.isWithSnapshot()) {
128 latest = dir.getDiffs().updatePrior(anchor, latest);
129 }
130 }
131 }
132 return latest;
133 }
134
135 static Snapshot read(DataInput in, FSImageFormat.Loader loader)
136 throws IOException {
137 final int snapshotId = in.readInt();
138 final INode root = loader.loadINodeWithLocalName(false, in, false);
139 return new Snapshot(snapshotId, root.asDirectory(), null);
140 }
141
142 /** The root directory of the snapshot. */
143 static public class Root extends INodeDirectory {
144 Root(INodeDirectory other) {
145 // Always preserve ACL.
146 super(other, false, Lists.newArrayList(
147 Iterables.filter(Arrays.asList(other.getFeatures()), AclFeature.class))
148 .toArray(new Feature[0]));
149 }
150
151 @Override
152 public ReadOnlyList<INode> getChildrenList(int snapshotId) {
153 return getParent().getChildrenList(snapshotId);
154 }
155
156 @Override
157 public INode getChild(byte[] name, int snapshotId) {
158 return getParent().getChild(name, snapshotId);
159 }
160
161 @Override
162 public String getFullPathName() {
163 return getSnapshotPath(getParent().getFullPathName(), getLocalName());
164 }
165 }
166
167 /** Snapshot ID. */
168 private final int id;
169 /** The root directory of the snapshot. */
170 private final Root root;
171
172 Snapshot(int id, String name, INodeDirectorySnapshottable dir) {
173 this(id, dir, dir);
174 this.root.setLocalName(DFSUtil.string2Bytes(name));
175 }
176
177 Snapshot(int id, INodeDirectory dir, INodeDirectorySnapshottable parent) {
178 this.id = id;
179 this.root = new Root(dir);
180
181 this.root.setParent(parent);
182 }
183
184 public int getId() {
185 return id;
186 }
187
188 /** @return the root directory of the snapshot. */
189 public Root getRoot() {
190 return root;
191 }
192
193 @Override
194 public int compareTo(byte[] bytes) {
195 return root.compareTo(bytes);
196 }
197
198 @Override
199 public boolean equals(Object that) {
200 if (this == that) {
201 return true;
202 } else if (that == null || !(that instanceof Snapshot)) {
203 return false;
204 }
205 return this.id == ((Snapshot)that).id;
206 }
207
208 @Override
209 public int hashCode() {
210 return id;
211 }
212
213 @Override
214 public String toString() {
215 return getClass().getSimpleName() + "." + root.getLocalName() + "(id=" + id + ")";
216 }
217 }