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.DataOutput;
021import java.io.IOException;
022import java.util.Arrays;
023
024import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfo;
025import org.apache.hadoop.hdfs.server.namenode.FSImageSerialization;
026import org.apache.hadoop.hdfs.server.namenode.INode;
027import org.apache.hadoop.hdfs.server.namenode.INode.BlocksMapUpdateInfo;
028import org.apache.hadoop.hdfs.server.namenode.INodeFile;
029import org.apache.hadoop.hdfs.server.namenode.INodeFileAttributes;
030import org.apache.hadoop.hdfs.server.namenode.snapshot.SnapshotFSImageFormat.ReferenceMap;
031
032/**
033 * The difference of an {@link INodeFile} between two snapshots.
034 */
035public class FileDiff extends
036    AbstractINodeDiff<INodeFile, INodeFileAttributes, FileDiff> {
037
038  /** The file size at snapshot creation time. */
039  private final long fileSize;
040  /** A copy of the INodeFile block list. Used in truncate. */
041  private BlockInfo[] blocks;
042
043  FileDiff(int snapshotId, INodeFile file) {
044    super(snapshotId, null, null);
045    fileSize = file.computeFileSize();
046    blocks = null;
047  }
048
049  /** Constructor used by FSImage loading */
050  FileDiff(int snapshotId, INodeFileAttributes snapshotINode,
051      FileDiff posteriorDiff, long fileSize) {
052    super(snapshotId, snapshotINode, posteriorDiff);
053    this.fileSize = fileSize;
054    blocks = null;
055  }
056
057  /** @return the file size in the snapshot. */
058  public long getFileSize() {
059    return fileSize;
060  }
061
062  /**
063   * Copy block references into the snapshot
064   * up to the current {@link #fileSize}.
065   * Should be done only once.
066   */
067  public void setBlocks(BlockInfo[] blocks) {
068    if(this.blocks != null)
069      return;
070    int numBlocks = 0;
071    for(long s = 0; numBlocks < blocks.length && s < fileSize; numBlocks++)
072      s += blocks[numBlocks].getNumBytes();
073    this.blocks = Arrays.copyOf(blocks, numBlocks);
074  }
075
076  public BlockInfo[] getBlocks() {
077    return blocks;
078  }
079
080  @Override
081  void combinePosteriorAndCollectBlocks(
082      INode.ReclaimContext reclaimContext, INodeFile currentINode,
083      FileDiff posterior) {
084    FileWithSnapshotFeature sf = currentINode.getFileWithSnapshotFeature();
085    assert sf != null : "FileWithSnapshotFeature is null";
086    sf.updateQuotaAndCollectBlocks(reclaimContext, currentINode, posterior);
087  }
088  
089  @Override
090  public String toString() {
091    return super.toString() + " fileSize=" + fileSize + ", rep="
092        + (snapshotINode == null? "?": snapshotINode.getFileReplication());
093  }
094
095  @Override
096  void write(DataOutput out, ReferenceMap referenceMap) throws IOException {
097    writeSnapshot(out);
098    out.writeLong(fileSize);
099
100    // write snapshotINode
101    if (snapshotINode != null) {
102      out.writeBoolean(true);
103      FSImageSerialization.writeINodeFileAttributes(snapshotINode, out);
104    } else {
105      out.writeBoolean(false);
106    }
107  }
108
109  @Override
110  void destroyDiffAndCollectBlocks(INode.ReclaimContext reclaimContext,
111      INodeFile currentINode) {
112    currentINode.getFileWithSnapshotFeature().updateQuotaAndCollectBlocks(
113        reclaimContext, currentINode, this);
114  }
115
116  public void destroyAndCollectSnapshotBlocks(
117      BlocksMapUpdateInfo collectedBlocks) {
118    if (blocks == null || collectedBlocks == null) {
119      return;
120    }
121    for (BlockInfo blk : blocks) {
122      collectedBlocks.addDeleteBlock(blk);
123    }
124    blocks = null;
125  }
126}