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.IOException; 022import java.text.SimpleDateFormat; 023import java.util.Arrays; 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.AclFeature; 032import org.apache.hadoop.hdfs.server.namenode.FSImageFormat; 033import org.apache.hadoop.hdfs.server.namenode.INode; 034import org.apache.hadoop.hdfs.server.namenode.INodeDirectory; 035import org.apache.hadoop.hdfs.util.ReadOnlyList; 036 037import com.google.common.collect.Iterables; 038import com.google.common.collect.Lists; 039 040/** Snapshot of a sub-tree in the namesystem. */ 041@InterfaceAudience.Private 042public 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}