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.fs.StorageType;
040import org.apache.hadoop.fs.XAttr;
041import org.apache.hadoop.hdfs.protocol.Block;
042import org.apache.hadoop.hdfs.protocol.HdfsConstants;
043import org.apache.hadoop.hdfs.protocol.proto.HdfsProtos.BlockProto;
044import org.apache.hadoop.hdfs.protocolPB.PBHelper;
045import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfoContiguous;
046import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfoContiguousUnderConstruction;
047import org.apache.hadoop.hdfs.server.blockmanagement.BlockManager;
048import org.apache.hadoop.hdfs.server.namenode.FSImageFormatProtobuf.LoaderContext;
049import org.apache.hadoop.hdfs.server.namenode.FSImageFormatProtobuf.SaverContext;
050import org.apache.hadoop.hdfs.server.namenode.FsImageProto.FileSummary;
051import org.apache.hadoop.hdfs.server.namenode.FsImageProto.FilesUnderConstructionSection.FileUnderConstructionEntry;
052import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeDirectorySection;
053import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeSection;
054import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeSection.AclFeatureProto;
055import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeSection.XAttrCompactProto;
056import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeSection.XAttrFeatureProto;
057import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeSection.QuotaByStorageTypeEntryProto;
058import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeSection.QuotaByStorageTypeFeatureProto;
059import org.apache.hadoop.hdfs.server.namenode.snapshot.Snapshot;
060import org.apache.hadoop.hdfs.util.EnumCounters;
061import org.apache.hadoop.hdfs.util.ReadOnlyList;
062
063import com.google.common.base.Preconditions;
064import com.google.common.collect.ImmutableList;
065import com.google.protobuf.ByteString;
066
067@InterfaceAudience.Private
068public final class FSImageFormatPBINode {
069  private final static long USER_GROUP_STRID_MASK = (1 << 24) - 1;
070  private final static int USER_STRID_OFFSET = 40;
071  private final static int GROUP_STRID_OFFSET = 16;
072  private static final Log LOG = LogFactory.getLog(FSImageFormatPBINode.class);
073
074  private static final int ACL_ENTRY_NAME_MASK = (1 << 24) - 1;
075  private static final int ACL_ENTRY_NAME_OFFSET = 6;
076  private static final int ACL_ENTRY_TYPE_OFFSET = 3;
077  private static final int ACL_ENTRY_SCOPE_OFFSET = 5;
078  private static final int ACL_ENTRY_PERM_MASK = 7;
079  private static final int ACL_ENTRY_TYPE_MASK = 3;
080  private static final int ACL_ENTRY_SCOPE_MASK = 1;
081  private static final FsAction[] FSACTION_VALUES = FsAction.values();
082  private static final AclEntryScope[] ACL_ENTRY_SCOPE_VALUES = AclEntryScope
083      .values();
084  private static final AclEntryType[] ACL_ENTRY_TYPE_VALUES = AclEntryType
085      .values();
086  
087  private static final int XATTR_NAMESPACE_MASK = 3;
088  private static final int XATTR_NAMESPACE_OFFSET = 30;
089  private static final int XATTR_NAME_MASK = (1 << 24) - 1;
090  private static final int XATTR_NAME_OFFSET = 6;
091
092  /* See the comments in fsimage.proto for an explanation of the following. */
093  private static final int XATTR_NAMESPACE_EXT_OFFSET = 5;
094  private static final int XATTR_NAMESPACE_EXT_MASK = 1;
095
096  private static final XAttr.NameSpace[] XATTR_NAMESPACE_VALUES =
097      XAttr.NameSpace.values();
098  
099
100  public final static class Loader {
101    public static PermissionStatus loadPermission(long id,
102        final String[] stringTable) {
103      short perm = (short) (id & ((1 << GROUP_STRID_OFFSET) - 1));
104      int gsid = (int) ((id >> GROUP_STRID_OFFSET) & USER_GROUP_STRID_MASK);
105      int usid = (int) ((id >> USER_STRID_OFFSET) & USER_GROUP_STRID_MASK);
106      return new PermissionStatus(stringTable[usid], stringTable[gsid],
107          new FsPermission(perm));
108    }
109
110    public static ImmutableList<AclEntry> loadAclEntries(
111        AclFeatureProto proto, final String[] stringTable) {
112      ImmutableList.Builder<AclEntry> b = ImmutableList.builder();
113      for (int v : proto.getEntriesList()) {
114        int p = v & ACL_ENTRY_PERM_MASK;
115        int t = (v >> ACL_ENTRY_TYPE_OFFSET) & ACL_ENTRY_TYPE_MASK;
116        int s = (v >> ACL_ENTRY_SCOPE_OFFSET) & ACL_ENTRY_SCOPE_MASK;
117        int nid = (v >> ACL_ENTRY_NAME_OFFSET) & ACL_ENTRY_NAME_MASK;
118        String name = stringTable[nid];
119        b.add(new AclEntry.Builder().setName(name)
120            .setPermission(FSACTION_VALUES[p])
121            .setScope(ACL_ENTRY_SCOPE_VALUES[s])
122            .setType(ACL_ENTRY_TYPE_VALUES[t]).build());
123      }
124      return b.build();
125    }
126    
127    public static ImmutableList<XAttr> loadXAttrs(
128        XAttrFeatureProto proto, final String[] stringTable) {
129      ImmutableList.Builder<XAttr> b = ImmutableList.builder();
130      for (XAttrCompactProto xAttrCompactProto : proto.getXAttrsList()) {
131        int v = xAttrCompactProto.getName();
132        int nid = (v >> XATTR_NAME_OFFSET) & XATTR_NAME_MASK;
133        int ns = (v >> XATTR_NAMESPACE_OFFSET) & XATTR_NAMESPACE_MASK;
134        ns |=
135            ((v >> XATTR_NAMESPACE_EXT_OFFSET) & XATTR_NAMESPACE_EXT_MASK) << 2;
136        String name = stringTable[nid];
137        byte[] value = null;
138        if (xAttrCompactProto.getValue() != null) {
139          value = xAttrCompactProto.getValue().toByteArray();
140        }
141        b.add(new XAttr.Builder().setNameSpace(XATTR_NAMESPACE_VALUES[ns])
142            .setName(name).setValue(value).build());
143      }
144      
145      return b.build();
146    }
147
148    public static ImmutableList<QuotaByStorageTypeEntry> loadQuotaByStorageTypeEntries(
149      QuotaByStorageTypeFeatureProto proto) {
150      ImmutableList.Builder<QuotaByStorageTypeEntry> b = ImmutableList.builder();
151      for (QuotaByStorageTypeEntryProto quotaEntry : proto.getQuotasList()) {
152        StorageType type = PBHelper.convertStorageType(quotaEntry.getStorageType());
153        long quota = quotaEntry.getQuota();
154        b.add(new QuotaByStorageTypeEntry.Builder().setStorageType(type)
155            .setQuota(quota).build());
156      }
157      return b.build();
158    }
159
160    public static INodeDirectory loadINodeDirectory(INodeSection.INode n,
161        LoaderContext state) {
162      assert n.getType() == INodeSection.INode.Type.DIRECTORY;
163      INodeSection.INodeDirectory d = n.getDirectory();
164
165      final PermissionStatus permissions = loadPermission(d.getPermission(),
166          state.getStringTable());
167      final INodeDirectory dir = new INodeDirectory(n.getId(), n.getName()
168          .toByteArray(), permissions, d.getModificationTime());
169      final long nsQuota = d.getNsQuota(), dsQuota = d.getDsQuota();
170      if (nsQuota >= 0 || dsQuota >= 0) {
171        dir.addDirectoryWithQuotaFeature(new DirectoryWithQuotaFeature.Builder().
172            nameSpaceQuota(nsQuota).storageSpaceQuota(dsQuota).build());
173      }
174      EnumCounters<StorageType> typeQuotas = null;
175      if (d.hasTypeQuotas()) {
176        ImmutableList<QuotaByStorageTypeEntry> qes =
177            loadQuotaByStorageTypeEntries(d.getTypeQuotas());
178        typeQuotas = new EnumCounters<StorageType>(StorageType.class,
179            HdfsConstants.QUOTA_RESET);
180        for (QuotaByStorageTypeEntry qe : qes) {
181          if (qe.getQuota() >= 0 && qe.getStorageType() != null &&
182              qe.getStorageType().supportTypeQuota()) {
183            typeQuotas.set(qe.getStorageType(), qe.getQuota());
184          }
185        }
186
187        if (typeQuotas.anyGreaterOrEqual(0)) {
188          DirectoryWithQuotaFeature q = dir.getDirectoryWithQuotaFeature();
189          if (q == null) {
190            dir.addDirectoryWithQuotaFeature(new DirectoryWithQuotaFeature.
191                Builder().typeQuotas(typeQuotas).build());
192          } else {
193            q.setQuota(typeQuotas);
194          }
195        }
196      }
197
198      if (d.hasAcl()) {
199        int[] entries = AclEntryStatusFormat.toInt(loadAclEntries(
200            d.getAcl(), state.getStringTable()));
201        dir.addAclFeature(new AclFeature(entries));
202      }
203      if (d.hasXAttrs()) {
204        dir.addXAttrFeature(new XAttrFeature(
205            loadXAttrs(d.getXAttrs(), state.getStringTable())));
206      }
207      return dir;
208    }
209
210    public static void updateBlocksMap(INodeFile file, BlockManager bm) {
211      // Add file->block mapping
212      final BlockInfoContiguous[] blocks = file.getBlocks();
213      if (blocks != null) {
214        for (int i = 0; i < blocks.length; i++) {
215          file.setBlock(i, bm.addBlockCollection(blocks[i], file));
216        }
217      }
218    }
219
220    private final FSDirectory dir;
221    private final FSNamesystem fsn;
222    private final FSImageFormatProtobuf.Loader parent;
223
224    Loader(FSNamesystem fsn, final FSImageFormatProtobuf.Loader parent) {
225      this.fsn = fsn;
226      this.dir = fsn.dir;
227      this.parent = parent;
228    }
229
230    void loadINodeDirectorySection(InputStream in) throws IOException {
231      final List<INodeReference> refList = parent.getLoaderContext()
232          .getRefList();
233      while (true) {
234        INodeDirectorySection.DirEntry e = INodeDirectorySection.DirEntry
235            .parseDelimitedFrom(in);
236        // note that in is a LimitedInputStream
237        if (e == null) {
238          break;
239        }
240        INodeDirectory p = dir.getInode(e.getParent()).asDirectory();
241        for (long id : e.getChildrenList()) {
242          INode child = dir.getInode(id);
243          addToParent(p, child);
244        }
245        for (int refId : e.getRefChildrenList()) {
246          INodeReference ref = refList.get(refId);
247          addToParent(p, ref);
248        }
249      }
250    }
251
252    void loadINodeSection(InputStream in) throws IOException {
253      INodeSection s = INodeSection.parseDelimitedFrom(in);
254      fsn.dir.resetLastInodeId(s.getLastInodeId());
255      LOG.info("Loading " + s.getNumInodes() + " INodes.");
256      for (int i = 0; i < s.getNumInodes(); ++i) {
257        INodeSection.INode p = INodeSection.INode.parseDelimitedFrom(in);
258        if (p.getId() == INodeId.ROOT_INODE_ID) {
259          loadRootINode(p);
260        } else {
261          INode n = loadINode(p);
262          dir.addToInodeMap(n);
263        }
264      }
265    }
266
267    /**
268     * Load the under-construction files section, and update the lease map
269     */
270    void loadFilesUnderConstructionSection(InputStream in) throws IOException {
271      while (true) {
272        FileUnderConstructionEntry entry = FileUnderConstructionEntry
273            .parseDelimitedFrom(in);
274        if (entry == null) {
275          break;
276        }
277        // update the lease manager
278        INodeFile file = dir.getInode(entry.getInodeId()).asFile();
279        FileUnderConstructionFeature uc = file.getFileUnderConstructionFeature();
280        Preconditions.checkState(uc != null); // file must be under-construction
281        fsn.leaseManager.addLease(uc.getClientName(), entry.getFullPath());
282      }
283    }
284
285    private void addToParent(INodeDirectory parent, INode child) {
286      if (parent == dir.rootDir && FSDirectory.isReservedName(child)) {
287        throw new HadoopIllegalArgumentException("File name \""
288            + child.getLocalName() + "\" is reserved. Please "
289            + " change the name of the existing file or directory to another "
290            + "name before upgrading to this release.");
291      }
292      // NOTE: This does not update space counts for parents
293      if (!parent.addChild(child)) {
294        return;
295      }
296      dir.cacheName(child);
297
298      if (child.isFile()) {
299        updateBlocksMap(child.asFile(), fsn.getBlockManager());
300      }
301    }
302
303    private INode loadINode(INodeSection.INode n) {
304      switch (n.getType()) {
305      case FILE:
306        return loadINodeFile(n);
307      case DIRECTORY:
308        return loadINodeDirectory(n, parent.getLoaderContext());
309      case SYMLINK:
310        return loadINodeSymlink(n);
311      default:
312        break;
313      }
314      return null;
315    }
316
317    private INodeFile loadINodeFile(INodeSection.INode n) {
318      assert n.getType() == INodeSection.INode.Type.FILE;
319      INodeSection.INodeFile f = n.getFile();
320      List<BlockProto> bp = f.getBlocksList();
321      short replication = (short) f.getReplication();
322      LoaderContext state = parent.getLoaderContext();
323
324      BlockInfoContiguous[] blocks = new BlockInfoContiguous[bp.size()];
325      for (int i = 0, e = bp.size(); i < e; ++i) {
326        blocks[i] = new BlockInfoContiguous(PBHelper.convert(bp.get(i)), replication);
327      }
328      final PermissionStatus permissions = loadPermission(f.getPermission(),
329          parent.getLoaderContext().getStringTable());
330
331      final INodeFile file = new INodeFile(n.getId(),
332          n.getName().toByteArray(), permissions, f.getModificationTime(),
333          f.getAccessTime(), blocks, replication, f.getPreferredBlockSize(),
334          (byte)f.getStoragePolicyID());
335
336      if (f.hasAcl()) {
337        int[] entries = AclEntryStatusFormat.toInt(loadAclEntries(
338            f.getAcl(), state.getStringTable()));
339        file.addAclFeature(new AclFeature(entries));
340      }
341      
342      if (f.hasXAttrs()) {
343        file.addXAttrFeature(new XAttrFeature(
344            loadXAttrs(f.getXAttrs(), state.getStringTable())));
345      }
346
347      // under-construction information
348      if (f.hasFileUC()) {
349        INodeSection.FileUnderConstructionFeature uc = f.getFileUC();
350        file.toUnderConstruction(uc.getClientName(), uc.getClientMachine());
351        if (blocks.length > 0) {
352          BlockInfoContiguous lastBlk = file.getLastBlock();
353          // replace the last block of file
354          file.setBlock(file.numBlocks() - 1, new BlockInfoContiguousUnderConstruction(
355              lastBlk, replication));
356        }
357      }
358      return file;
359    }
360
361
362    private INodeSymlink loadINodeSymlink(INodeSection.INode n) {
363      assert n.getType() == INodeSection.INode.Type.SYMLINK;
364      INodeSection.INodeSymlink s = n.getSymlink();
365      final PermissionStatus permissions = loadPermission(s.getPermission(),
366          parent.getLoaderContext().getStringTable());
367      INodeSymlink sym = new INodeSymlink(n.getId(), n.getName().toByteArray(),
368          permissions, s.getModificationTime(), s.getAccessTime(),
369          s.getTarget().toStringUtf8());
370      return sym;
371    }
372
373    private void loadRootINode(INodeSection.INode p) {
374      INodeDirectory root = loadINodeDirectory(p, parent.getLoaderContext());
375      final QuotaCounts q = root.getQuotaCounts();
376      final long nsQuota = q.getNameSpace();
377      final long dsQuota = q.getStorageSpace();
378      if (nsQuota != -1 || dsQuota != -1) {
379        dir.rootDir.getDirectoryWithQuotaFeature().setQuota(nsQuota, dsQuota);
380      }
381      final EnumCounters<StorageType> typeQuotas = q.getTypeSpaces();
382      if (typeQuotas.anyGreaterOrEqual(0)) {
383        dir.rootDir.getDirectoryWithQuotaFeature().setQuota(typeQuotas);
384      }
385      dir.rootDir.cloneModificationTime(root);
386      dir.rootDir.clonePermissionStatus(root);
387      final AclFeature af = root.getFeature(AclFeature.class);
388      if (af != null) {
389        dir.rootDir.addAclFeature(af);
390      }
391      // root dir supports having extended attributes according to POSIX
392      final XAttrFeature f = root.getXAttrFeature();
393      if (f != null) {
394        dir.rootDir.addXAttrFeature(f);
395      }
396      dir.addRootDirToEncryptionZone(f);
397    }
398  }
399
400  public final static class Saver {
401    private static long buildPermissionStatus(INodeAttributes n,
402        final SaverContext.DeduplicationMap<String> stringMap) {
403      long userId = stringMap.getId(n.getUserName());
404      long groupId = stringMap.getId(n.getGroupName());
405      return ((userId & USER_GROUP_STRID_MASK) << USER_STRID_OFFSET)
406          | ((groupId & USER_GROUP_STRID_MASK) << GROUP_STRID_OFFSET)
407          | n.getFsPermissionShort();
408    }
409
410    private static AclFeatureProto.Builder buildAclEntries(AclFeature f,
411        final SaverContext.DeduplicationMap<String> map) {
412      AclFeatureProto.Builder b = AclFeatureProto.newBuilder();
413      for (int pos = 0, e; pos < f.getEntriesSize(); pos++) {
414        e = f.getEntryAt(pos);
415        int nameId = map.getId(AclEntryStatusFormat.getName(e));
416        int v = ((nameId & ACL_ENTRY_NAME_MASK) << ACL_ENTRY_NAME_OFFSET)
417            | (AclEntryStatusFormat.getType(e).ordinal() << ACL_ENTRY_TYPE_OFFSET)
418            | (AclEntryStatusFormat.getScope(e).ordinal() << ACL_ENTRY_SCOPE_OFFSET)
419            | (AclEntryStatusFormat.getPermission(e).ordinal());
420        b.addEntries(v);
421      }
422      return b;
423    }
424    
425    private static XAttrFeatureProto.Builder buildXAttrs(XAttrFeature f,
426        final SaverContext.DeduplicationMap<String> stringMap) {
427      XAttrFeatureProto.Builder b = XAttrFeatureProto.newBuilder();
428      for (XAttr a : f.getXAttrs()) {
429        XAttrCompactProto.Builder xAttrCompactBuilder = XAttrCompactProto.
430            newBuilder();
431        int nsOrd = a.getNameSpace().ordinal();
432        Preconditions.checkArgument(nsOrd < 8, "Too many namespaces.");
433        int v = ((nsOrd & XATTR_NAMESPACE_MASK) << XATTR_NAMESPACE_OFFSET)
434            | ((stringMap.getId(a.getName()) & XATTR_NAME_MASK) <<
435                XATTR_NAME_OFFSET);
436        v |= (((nsOrd >> 2) & XATTR_NAMESPACE_EXT_MASK) <<
437            XATTR_NAMESPACE_EXT_OFFSET);
438        xAttrCompactBuilder.setName(v);
439        if (a.getValue() != null) {
440          xAttrCompactBuilder.setValue(PBHelper.getByteString(a.getValue()));
441        }
442        b.addXAttrs(xAttrCompactBuilder.build());
443      }
444      
445      return b;
446    }
447
448    private static QuotaByStorageTypeFeatureProto.Builder
449        buildQuotaByStorageTypeEntries(QuotaCounts q) {
450      QuotaByStorageTypeFeatureProto.Builder b =
451          QuotaByStorageTypeFeatureProto.newBuilder();
452      for (StorageType t: StorageType.getTypesSupportingQuota()) {
453        if (q.getTypeSpace(t) >= 0) {
454          QuotaByStorageTypeEntryProto.Builder eb =
455              QuotaByStorageTypeEntryProto.newBuilder().
456              setStorageType(PBHelper.convertStorageType(t)).
457              setQuota(q.getTypeSpace(t));
458          b.addQuotas(eb);
459        }
460      }
461      return b;
462    }
463
464    public static INodeSection.INodeFile.Builder buildINodeFile(
465        INodeFileAttributes file, final SaverContext state) {
466      INodeSection.INodeFile.Builder b = INodeSection.INodeFile.newBuilder()
467          .setAccessTime(file.getAccessTime())
468          .setModificationTime(file.getModificationTime())
469          .setPermission(buildPermissionStatus(file, state.getStringMap()))
470          .setPreferredBlockSize(file.getPreferredBlockSize())
471          .setReplication(file.getFileReplication())
472          .setStoragePolicyID(file.getLocalStoragePolicyID());
473
474      AclFeature f = file.getAclFeature();
475      if (f != null) {
476        b.setAcl(buildAclEntries(f, state.getStringMap()));
477      }
478      XAttrFeature xAttrFeature = file.getXAttrFeature();
479      if (xAttrFeature != null) {
480        b.setXAttrs(buildXAttrs(xAttrFeature, state.getStringMap()));
481      }
482      return b;
483    }
484
485    public static INodeSection.INodeDirectory.Builder buildINodeDirectory(
486        INodeDirectoryAttributes dir, final SaverContext state) {
487      QuotaCounts quota = dir.getQuotaCounts();
488      INodeSection.INodeDirectory.Builder b = INodeSection.INodeDirectory
489          .newBuilder().setModificationTime(dir.getModificationTime())
490          .setNsQuota(quota.getNameSpace())
491          .setDsQuota(quota.getStorageSpace())
492          .setPermission(buildPermissionStatus(dir, state.getStringMap()));
493
494      if (quota.getTypeSpaces().anyGreaterOrEqual(0)) {
495        b.setTypeQuotas(buildQuotaByStorageTypeEntries(quota));
496      }
497
498      AclFeature f = dir.getAclFeature();
499      if (f != null) {
500        b.setAcl(buildAclEntries(f, state.getStringMap()));
501      }
502      XAttrFeature xAttrFeature = dir.getXAttrFeature();
503      if (xAttrFeature != null) {
504        b.setXAttrs(buildXAttrs(xAttrFeature, state.getStringMap()));
505      }
506      return b;
507    }
508
509    private final FSNamesystem fsn;
510    private final FileSummary.Builder summary;
511    private final SaveNamespaceContext context;
512    private final FSImageFormatProtobuf.Saver parent;
513
514    Saver(FSImageFormatProtobuf.Saver parent, FileSummary.Builder summary) {
515      this.parent = parent;
516      this.summary = summary;
517      this.context = parent.getContext();
518      this.fsn = context.getSourceNamesystem();
519    }
520
521    void serializeINodeDirectorySection(OutputStream out) throws IOException {
522      Iterator<INodeWithAdditionalFields> iter = fsn.getFSDirectory()
523          .getINodeMap().getMapIterator();
524      final ArrayList<INodeReference> refList = parent.getSaverContext()
525          .getRefList();
526      int i = 0;
527      while (iter.hasNext()) {
528        INodeWithAdditionalFields n = iter.next();
529        if (!n.isDirectory()) {
530          continue;
531        }
532
533        ReadOnlyList<INode> children = n.asDirectory().getChildrenList(
534            Snapshot.CURRENT_STATE_ID);
535        if (children.size() > 0) {
536          INodeDirectorySection.DirEntry.Builder b = INodeDirectorySection.
537              DirEntry.newBuilder().setParent(n.getId());
538          for (INode inode : children) {
539            if (!inode.isReference()) {
540              b.addChildren(inode.getId());
541            } else {
542              refList.add(inode.asReference());
543              b.addRefChildren(refList.size() - 1);
544            }
545          }
546          INodeDirectorySection.DirEntry e = b.build();
547          e.writeDelimitedTo(out);
548        }
549
550        ++i;
551        if (i % FSImageFormatProtobuf.Saver.CHECK_CANCEL_INTERVAL == 0) {
552          context.checkCancelled();
553        }
554      }
555      parent.commitSection(summary,
556          FSImageFormatProtobuf.SectionName.INODE_DIR);
557    }
558
559    void serializeINodeSection(OutputStream out) throws IOException {
560      INodeMap inodesMap = fsn.dir.getINodeMap();
561
562      INodeSection.Builder b = INodeSection.newBuilder()
563          .setLastInodeId(fsn.dir.getLastInodeId()).setNumInodes(inodesMap.size());
564      INodeSection s = b.build();
565      s.writeDelimitedTo(out);
566
567      int i = 0;
568      Iterator<INodeWithAdditionalFields> iter = inodesMap.getMapIterator();
569      while (iter.hasNext()) {
570        INodeWithAdditionalFields n = iter.next();
571        save(out, n);
572        ++i;
573        if (i % FSImageFormatProtobuf.Saver.CHECK_CANCEL_INTERVAL == 0) {
574          context.checkCancelled();
575        }
576      }
577      parent.commitSection(summary, FSImageFormatProtobuf.SectionName.INODE);
578    }
579
580    void serializeFilesUCSection(OutputStream out) throws IOException {
581      Map<String, INodeFile> ucMap = fsn.getFilesUnderConstruction();
582      for (Map.Entry<String, INodeFile> entry : ucMap.entrySet()) {
583        String path = entry.getKey();
584        INodeFile file = entry.getValue();
585        FileUnderConstructionEntry.Builder b = FileUnderConstructionEntry
586            .newBuilder().setInodeId(file.getId()).setFullPath(path);
587        FileUnderConstructionEntry e = b.build();
588        e.writeDelimitedTo(out);
589      }
590      parent.commitSection(summary,
591          FSImageFormatProtobuf.SectionName.FILES_UNDERCONSTRUCTION);
592    }
593
594    private void save(OutputStream out, INode n) throws IOException {
595      if (n.isDirectory()) {
596        save(out, n.asDirectory());
597      } else if (n.isFile()) {
598        save(out, n.asFile());
599      } else if (n.isSymlink()) {
600        save(out, n.asSymlink());
601      }
602    }
603
604    private void save(OutputStream out, INodeDirectory n) throws IOException {
605      INodeSection.INodeDirectory.Builder b = buildINodeDirectory(n,
606          parent.getSaverContext());
607      INodeSection.INode r = buildINodeCommon(n)
608          .setType(INodeSection.INode.Type.DIRECTORY).setDirectory(b).build();
609      r.writeDelimitedTo(out);
610    }
611
612    private void save(OutputStream out, INodeFile n) throws IOException {
613      INodeSection.INodeFile.Builder b = buildINodeFile(n,
614          parent.getSaverContext());
615
616      if (n.getBlocks() != null) {
617        for (Block block : n.getBlocks()) {
618          b.addBlocks(PBHelper.convert(block));
619        }
620      }
621
622      FileUnderConstructionFeature uc = n.getFileUnderConstructionFeature();
623      if (uc != null) {
624        INodeSection.FileUnderConstructionFeature f =
625            INodeSection.FileUnderConstructionFeature
626            .newBuilder().setClientName(uc.getClientName())
627            .setClientMachine(uc.getClientMachine()).build();
628        b.setFileUC(f);
629      }
630
631      INodeSection.INode r = buildINodeCommon(n)
632          .setType(INodeSection.INode.Type.FILE).setFile(b).build();
633      r.writeDelimitedTo(out);
634    }
635
636    private void save(OutputStream out, INodeSymlink n) throws IOException {
637      SaverContext state = parent.getSaverContext();
638      INodeSection.INodeSymlink.Builder b = INodeSection.INodeSymlink
639          .newBuilder()
640          .setPermission(buildPermissionStatus(n, state.getStringMap()))
641          .setTarget(ByteString.copyFrom(n.getSymlink()))
642          .setModificationTime(n.getModificationTime())
643          .setAccessTime(n.getAccessTime());
644
645      INodeSection.INode r = buildINodeCommon(n)
646          .setType(INodeSection.INode.Type.SYMLINK).setSymlink(b).build();
647      r.writeDelimitedTo(out);
648    }
649
650    private final INodeSection.INode.Builder buildINodeCommon(INode n) {
651      return INodeSection.INode.newBuilder()
652          .setId(n.getId())
653          .setName(ByteString.copyFrom(n.getLocalNameBytes()));
654    }
655  }
656
657  private FSImageFormatPBINode() {
658  }
659}