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 019package org.apache.hadoop.hdfs.server.namenode; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024import java.util.ArrayList; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.apache.hadoop.HadoopIllegalArgumentException; 032import org.apache.hadoop.classification.InterfaceAudience; 033import org.apache.hadoop.fs.permission.AclEntry; 034import org.apache.hadoop.fs.permission.AclEntryScope; 035import org.apache.hadoop.fs.permission.AclEntryType; 036import org.apache.hadoop.fs.permission.FsAction; 037import org.apache.hadoop.fs.permission.FsPermission; 038import org.apache.hadoop.fs.permission.PermissionStatus; 039import org.apache.hadoop.hdfs.protocol.Block; 040import org.apache.hadoop.hdfs.protocol.proto.HdfsProtos.BlockProto; 041import org.apache.hadoop.hdfs.protocolPB.PBHelper; 042import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfo; 043import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfoUnderConstruction; 044import org.apache.hadoop.hdfs.server.blockmanagement.BlockManager; 045import org.apache.hadoop.hdfs.server.namenode.FSImageFormatProtobuf.LoaderContext; 046import org.apache.hadoop.hdfs.server.namenode.FSImageFormatProtobuf.SaverContext; 047import org.apache.hadoop.hdfs.server.namenode.FsImageProto.FileSummary; 048import org.apache.hadoop.hdfs.server.namenode.FsImageProto.FilesUnderConstructionSection.FileUnderConstructionEntry; 049import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeDirectorySection; 050import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeSection; 051import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeSection.AclFeatureProto; 052import org.apache.hadoop.hdfs.server.namenode.snapshot.Snapshot; 053import org.apache.hadoop.hdfs.util.ReadOnlyList; 054 055import com.google.common.base.Preconditions; 056import com.google.common.collect.ImmutableList; 057import com.google.protobuf.ByteString; 058 059@InterfaceAudience.Private 060public final class FSImageFormatPBINode { 061 private final static long USER_GROUP_STRID_MASK = (1 << 24) - 1; 062 private final static int USER_STRID_OFFSET = 40; 063 private final static int GROUP_STRID_OFFSET = 16; 064 private static final Log LOG = LogFactory.getLog(FSImageFormatPBINode.class); 065 066 private static final int ACL_ENTRY_NAME_MASK = (1 << 24) - 1; 067 private static final int ACL_ENTRY_NAME_OFFSET = 6; 068 private static final int ACL_ENTRY_TYPE_OFFSET = 3; 069 private static final int ACL_ENTRY_SCOPE_OFFSET = 5; 070 private static final int ACL_ENTRY_PERM_MASK = 7; 071 private static final int ACL_ENTRY_TYPE_MASK = 3; 072 private static final int ACL_ENTRY_SCOPE_MASK = 1; 073 private static final FsAction[] FSACTION_VALUES = FsAction.values(); 074 private static final AclEntryScope[] ACL_ENTRY_SCOPE_VALUES = AclEntryScope 075 .values(); 076 private static final AclEntryType[] ACL_ENTRY_TYPE_VALUES = AclEntryType 077 .values(); 078 079 public final static class Loader { 080 public static PermissionStatus loadPermission(long id, 081 final String[] stringTable) { 082 short perm = (short) (id & ((1 << GROUP_STRID_OFFSET) - 1)); 083 int gsid = (int) ((id >> GROUP_STRID_OFFSET) & USER_GROUP_STRID_MASK); 084 int usid = (int) ((id >> USER_STRID_OFFSET) & USER_GROUP_STRID_MASK); 085 return new PermissionStatus(stringTable[usid], stringTable[gsid], 086 new FsPermission(perm)); 087 } 088 089 public static ImmutableList<AclEntry> loadAclEntries( 090 AclFeatureProto proto, final String[] stringTable) { 091 ImmutableList.Builder<AclEntry> b = ImmutableList.builder(); 092 for (int v : proto.getEntriesList()) { 093 int p = v & ACL_ENTRY_PERM_MASK; 094 int t = (v >> ACL_ENTRY_TYPE_OFFSET) & ACL_ENTRY_TYPE_MASK; 095 int s = (v >> ACL_ENTRY_SCOPE_OFFSET) & ACL_ENTRY_SCOPE_MASK; 096 int nid = (v >> ACL_ENTRY_NAME_OFFSET) & ACL_ENTRY_NAME_MASK; 097 String name = stringTable[nid]; 098 b.add(new AclEntry.Builder().setName(name) 099 .setPermission(FSACTION_VALUES[p]) 100 .setScope(ACL_ENTRY_SCOPE_VALUES[s]) 101 .setType(ACL_ENTRY_TYPE_VALUES[t]).build()); 102 } 103 return b.build(); 104 } 105 106 public static INodeDirectory loadINodeDirectory(INodeSection.INode n, 107 LoaderContext state) { 108 assert n.getType() == INodeSection.INode.Type.DIRECTORY; 109 INodeSection.INodeDirectory d = n.getDirectory(); 110 111 final PermissionStatus permissions = loadPermission(d.getPermission(), 112 state.getStringTable()); 113 final INodeDirectory dir = new INodeDirectory(n.getId(), n.getName() 114 .toByteArray(), permissions, d.getModificationTime()); 115 116 final long nsQuota = d.getNsQuota(), dsQuota = d.getDsQuota(); 117 if (nsQuota >= 0 || dsQuota >= 0) { 118 dir.addDirectoryWithQuotaFeature(nsQuota, dsQuota); 119 } 120 121 if (d.hasAcl()) { 122 dir.addAclFeature(new AclFeature(loadAclEntries(d.getAcl(), 123 state.getStringTable()))); 124 } 125 return dir; 126 } 127 128 public static void updateBlocksMap(INodeFile file, BlockManager bm) { 129 // Add file->block mapping 130 final BlockInfo[] blocks = file.getBlocks(); 131 if (blocks != null) { 132 for (int i = 0; i < blocks.length; i++) { 133 file.setBlock(i, bm.addBlockCollection(blocks[i], file)); 134 } 135 } 136 } 137 138 private final FSDirectory dir; 139 private final FSNamesystem fsn; 140 private final FSImageFormatProtobuf.Loader parent; 141 142 Loader(FSNamesystem fsn, final FSImageFormatProtobuf.Loader parent) { 143 this.fsn = fsn; 144 this.dir = fsn.dir; 145 this.parent = parent; 146 } 147 148 void loadINodeDirectorySection(InputStream in) throws IOException { 149 final List<INodeReference> refList = parent.getLoaderContext() 150 .getRefList(); 151 while (true) { 152 INodeDirectorySection.DirEntry e = INodeDirectorySection.DirEntry 153 .parseDelimitedFrom(in); 154 // note that in is a LimitedInputStream 155 if (e == null) { 156 break; 157 } 158 INodeDirectory p = dir.getInode(e.getParent()).asDirectory(); 159 for (long id : e.getChildrenList()) { 160 INode child = dir.getInode(id); 161 addToParent(p, child); 162 } 163 for (int refId : e.getRefChildrenList()) { 164 INodeReference ref = refList.get(refId); 165 addToParent(p, ref); 166 } 167 } 168 } 169 170 void loadINodeSection(InputStream in) throws IOException { 171 INodeSection s = INodeSection.parseDelimitedFrom(in); 172 fsn.resetLastInodeId(s.getLastInodeId()); 173 LOG.info("Loading " + s.getNumInodes() + " INodes."); 174 for (int i = 0; i < s.getNumInodes(); ++i) { 175 INodeSection.INode p = INodeSection.INode.parseDelimitedFrom(in); 176 if (p.getId() == INodeId.ROOT_INODE_ID) { 177 loadRootINode(p); 178 } else { 179 INode n = loadINode(p); 180 dir.addToInodeMap(n); 181 } 182 } 183 } 184 185 /** 186 * Load the under-construction files section, and update the lease map 187 */ 188 void loadFilesUnderConstructionSection(InputStream in) throws IOException { 189 while (true) { 190 FileUnderConstructionEntry entry = FileUnderConstructionEntry 191 .parseDelimitedFrom(in); 192 if (entry == null) { 193 break; 194 } 195 // update the lease manager 196 INodeFile file = dir.getInode(entry.getInodeId()).asFile(); 197 FileUnderConstructionFeature uc = file.getFileUnderConstructionFeature(); 198 Preconditions.checkState(uc != null); // file must be under-construction 199 fsn.leaseManager.addLease(uc.getClientName(), entry.getFullPath()); 200 } 201 } 202 203 private void addToParent(INodeDirectory parent, INode child) { 204 if (parent == dir.rootDir && FSDirectory.isReservedName(child)) { 205 throw new HadoopIllegalArgumentException("File name \"" 206 + child.getLocalName() + "\" is reserved. Please " 207 + " change the name of the existing file or directory to another " 208 + "name before upgrading to this release."); 209 } 210 // NOTE: This does not update space counts for parents 211 if (!parent.addChild(child)) { 212 return; 213 } 214 dir.cacheName(child); 215 216 if (child.isFile()) { 217 updateBlocksMap(child.asFile(), fsn.getBlockManager()); 218 } 219 } 220 221 private INode loadINode(INodeSection.INode n) { 222 switch (n.getType()) { 223 case FILE: 224 return loadINodeFile(n); 225 case DIRECTORY: 226 return loadINodeDirectory(n, parent.getLoaderContext()); 227 case SYMLINK: 228 return loadINodeSymlink(n); 229 default: 230 break; 231 } 232 return null; 233 } 234 235 private INodeFile loadINodeFile(INodeSection.INode n) { 236 assert n.getType() == INodeSection.INode.Type.FILE; 237 INodeSection.INodeFile f = n.getFile(); 238 List<BlockProto> bp = f.getBlocksList(); 239 short replication = (short) f.getReplication(); 240 LoaderContext state = parent.getLoaderContext(); 241 242 BlockInfo[] blocks = new BlockInfo[bp.size()]; 243 for (int i = 0, e = bp.size(); i < e; ++i) { 244 blocks[i] = new BlockInfo(PBHelper.convert(bp.get(i)), replication); 245 } 246 final PermissionStatus permissions = loadPermission(f.getPermission(), 247 parent.getLoaderContext().getStringTable()); 248 249 final INodeFile file = new INodeFile(n.getId(), 250 n.getName().toByteArray(), permissions, f.getModificationTime(), 251 f.getAccessTime(), blocks, replication, f.getPreferredBlockSize()); 252 253 if (f.hasAcl()) { 254 file.addAclFeature(new AclFeature(loadAclEntries(f.getAcl(), 255 state.getStringTable()))); 256 } 257 258 // under-construction information 259 if (f.hasFileUC()) { 260 INodeSection.FileUnderConstructionFeature uc = f.getFileUC(); 261 file.toUnderConstruction(uc.getClientName(), uc.getClientMachine(), 262 null); 263 if (blocks.length > 0) { 264 BlockInfo lastBlk = file.getLastBlock(); 265 // replace the last block of file 266 file.setBlock(file.numBlocks() - 1, new BlockInfoUnderConstruction( 267 lastBlk, replication)); 268 } 269 } 270 return file; 271 } 272 273 274 private INodeSymlink loadINodeSymlink(INodeSection.INode n) { 275 assert n.getType() == INodeSection.INode.Type.SYMLINK; 276 INodeSection.INodeSymlink s = n.getSymlink(); 277 final PermissionStatus permissions = loadPermission(s.getPermission(), 278 parent.getLoaderContext().getStringTable()); 279 INodeSymlink sym = new INodeSymlink(n.getId(), n.getName().toByteArray(), 280 permissions, s.getModificationTime(), s.getAccessTime(), 281 s.getTarget().toStringUtf8()); 282 return sym; 283 } 284 285 private void loadRootINode(INodeSection.INode p) { 286 INodeDirectory root = loadINodeDirectory(p, parent.getLoaderContext()); 287 final Quota.Counts q = root.getQuotaCounts(); 288 final long nsQuota = q.get(Quota.NAMESPACE); 289 final long dsQuota = q.get(Quota.DISKSPACE); 290 if (nsQuota != -1 || dsQuota != -1) { 291 dir.rootDir.getDirectoryWithQuotaFeature().setQuota(nsQuota, dsQuota); 292 } 293 dir.rootDir.cloneModificationTime(root); 294 dir.rootDir.clonePermissionStatus(root); 295 } 296 } 297 298 public final static class Saver { 299 private static long buildPermissionStatus(INodeAttributes n, 300 final SaverContext.DeduplicationMap<String> stringMap) { 301 long userId = stringMap.getId(n.getUserName()); 302 long groupId = stringMap.getId(n.getGroupName()); 303 return ((userId & USER_GROUP_STRID_MASK) << USER_STRID_OFFSET) 304 | ((groupId & USER_GROUP_STRID_MASK) << GROUP_STRID_OFFSET) 305 | n.getFsPermissionShort(); 306 } 307 308 private static AclFeatureProto.Builder buildAclEntries(AclFeature f, 309 final SaverContext.DeduplicationMap<String> map) { 310 AclFeatureProto.Builder b = AclFeatureProto.newBuilder(); 311 for (AclEntry e : f.getEntries()) { 312 int v = ((map.getId(e.getName()) & ACL_ENTRY_NAME_MASK) << ACL_ENTRY_NAME_OFFSET) 313 | (e.getType().ordinal() << ACL_ENTRY_TYPE_OFFSET) 314 | (e.getScope().ordinal() << ACL_ENTRY_SCOPE_OFFSET) 315 | (e.getPermission().ordinal()); 316 b.addEntries(v); 317 } 318 return b; 319 } 320 321 public static INodeSection.INodeFile.Builder buildINodeFile( 322 INodeFileAttributes file, final SaverContext state) { 323 INodeSection.INodeFile.Builder b = INodeSection.INodeFile.newBuilder() 324 .setAccessTime(file.getAccessTime()) 325 .setModificationTime(file.getModificationTime()) 326 .setPermission(buildPermissionStatus(file, state.getStringMap())) 327 .setPreferredBlockSize(file.getPreferredBlockSize()) 328 .setReplication(file.getFileReplication()); 329 330 AclFeature f = file.getAclFeature(); 331 if (f != null) { 332 b.setAcl(buildAclEntries(f, state.getStringMap())); 333 } 334 return b; 335 } 336 337 public static INodeSection.INodeDirectory.Builder buildINodeDirectory( 338 INodeDirectoryAttributes dir, final SaverContext state) { 339 Quota.Counts quota = dir.getQuotaCounts(); 340 INodeSection.INodeDirectory.Builder b = INodeSection.INodeDirectory 341 .newBuilder().setModificationTime(dir.getModificationTime()) 342 .setNsQuota(quota.get(Quota.NAMESPACE)) 343 .setDsQuota(quota.get(Quota.DISKSPACE)) 344 .setPermission(buildPermissionStatus(dir, state.getStringMap())); 345 346 AclFeature f = dir.getAclFeature(); 347 if (f != null) { 348 b.setAcl(buildAclEntries(f, state.getStringMap())); 349 } 350 return b; 351 } 352 353 private final FSNamesystem fsn; 354 private final FileSummary.Builder summary; 355 private final SaveNamespaceContext context; 356 private final FSImageFormatProtobuf.Saver parent; 357 358 Saver(FSImageFormatProtobuf.Saver parent, FileSummary.Builder summary) { 359 this.parent = parent; 360 this.summary = summary; 361 this.context = parent.getContext(); 362 this.fsn = context.getSourceNamesystem(); 363 } 364 365 void serializeINodeDirectorySection(OutputStream out) throws IOException { 366 Iterator<INodeWithAdditionalFields> iter = fsn.getFSDirectory() 367 .getINodeMap().getMapIterator(); 368 final ArrayList<INodeReference> refList = parent.getSaverContext() 369 .getRefList(); 370 int i = 0; 371 while (iter.hasNext()) { 372 INodeWithAdditionalFields n = iter.next(); 373 if (!n.isDirectory()) { 374 continue; 375 } 376 377 ReadOnlyList<INode> children = n.asDirectory().getChildrenList( 378 Snapshot.CURRENT_STATE_ID); 379 if (children.size() > 0) { 380 INodeDirectorySection.DirEntry.Builder b = INodeDirectorySection. 381 DirEntry.newBuilder().setParent(n.getId()); 382 for (INode inode : children) { 383 if (!inode.isReference()) { 384 b.addChildren(inode.getId()); 385 } else { 386 refList.add(inode.asReference()); 387 b.addRefChildren(refList.size() - 1); 388 } 389 } 390 INodeDirectorySection.DirEntry e = b.build(); 391 e.writeDelimitedTo(out); 392 } 393 394 ++i; 395 if (i % FSImageFormatProtobuf.Saver.CHECK_CANCEL_INTERVAL == 0) { 396 context.checkCancelled(); 397 } 398 } 399 parent.commitSection(summary, 400 FSImageFormatProtobuf.SectionName.INODE_DIR); 401 } 402 403 void serializeINodeSection(OutputStream out) throws IOException { 404 INodeMap inodesMap = fsn.dir.getINodeMap(); 405 406 INodeSection.Builder b = INodeSection.newBuilder() 407 .setLastInodeId(fsn.getLastInodeId()).setNumInodes(inodesMap.size()); 408 INodeSection s = b.build(); 409 s.writeDelimitedTo(out); 410 411 int i = 0; 412 Iterator<INodeWithAdditionalFields> iter = inodesMap.getMapIterator(); 413 while (iter.hasNext()) { 414 INodeWithAdditionalFields n = iter.next(); 415 save(out, n); 416 ++i; 417 if (i % FSImageFormatProtobuf.Saver.CHECK_CANCEL_INTERVAL == 0) { 418 context.checkCancelled(); 419 } 420 } 421 parent.commitSection(summary, FSImageFormatProtobuf.SectionName.INODE); 422 } 423 424 void serializeFilesUCSection(OutputStream out) throws IOException { 425 Map<String, INodeFile> ucMap = fsn.getFilesUnderConstruction(); 426 for (Map.Entry<String, INodeFile> entry : ucMap.entrySet()) { 427 String path = entry.getKey(); 428 INodeFile file = entry.getValue(); 429 FileUnderConstructionEntry.Builder b = FileUnderConstructionEntry 430 .newBuilder().setInodeId(file.getId()).setFullPath(path); 431 FileUnderConstructionEntry e = b.build(); 432 e.writeDelimitedTo(out); 433 } 434 parent.commitSection(summary, 435 FSImageFormatProtobuf.SectionName.FILES_UNDERCONSTRUCTION); 436 } 437 438 private void save(OutputStream out, INode n) throws IOException { 439 if (n.isDirectory()) { 440 save(out, n.asDirectory()); 441 } else if (n.isFile()) { 442 save(out, n.asFile()); 443 } else if (n.isSymlink()) { 444 save(out, n.asSymlink()); 445 } 446 } 447 448 private void save(OutputStream out, INodeDirectory n) throws IOException { 449 INodeSection.INodeDirectory.Builder b = buildINodeDirectory(n, 450 parent.getSaverContext()); 451 INodeSection.INode r = buildINodeCommon(n) 452 .setType(INodeSection.INode.Type.DIRECTORY).setDirectory(b).build(); 453 r.writeDelimitedTo(out); 454 } 455 456 private void save(OutputStream out, INodeFile n) throws IOException { 457 INodeSection.INodeFile.Builder b = buildINodeFile(n, 458 parent.getSaverContext()); 459 460 for (Block block : n.getBlocks()) { 461 b.addBlocks(PBHelper.convert(block)); 462 } 463 464 FileUnderConstructionFeature uc = n.getFileUnderConstructionFeature(); 465 if (uc != null) { 466 INodeSection.FileUnderConstructionFeature f = 467 INodeSection.FileUnderConstructionFeature 468 .newBuilder().setClientName(uc.getClientName()) 469 .setClientMachine(uc.getClientMachine()).build(); 470 b.setFileUC(f); 471 } 472 473 INodeSection.INode r = buildINodeCommon(n) 474 .setType(INodeSection.INode.Type.FILE).setFile(b).build(); 475 r.writeDelimitedTo(out); 476 } 477 478 private void save(OutputStream out, INodeSymlink n) throws IOException { 479 SaverContext state = parent.getSaverContext(); 480 INodeSection.INodeSymlink.Builder b = INodeSection.INodeSymlink 481 .newBuilder() 482 .setPermission(buildPermissionStatus(n, state.getStringMap())) 483 .setTarget(ByteString.copyFrom(n.getSymlink())) 484 .setModificationTime(n.getModificationTime()) 485 .setAccessTime(n.getAccessTime()); 486 487 INodeSection.INode r = buildINodeCommon(n) 488 .setType(INodeSection.INode.Type.SYMLINK).setSymlink(b).build(); 489 r.writeDelimitedTo(out); 490 } 491 492 private final INodeSection.INode.Builder buildINodeCommon(INode n) { 493 return INodeSection.INode.newBuilder() 494 .setId(n.getId()) 495 .setName(ByteString.copyFrom(n.getLocalNameBytes())); 496 } 497 } 498 499 private FSImageFormatPBINode() { 500 } 501}