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; 019 020 import java.io.IOException; 021 import java.util.List; 022 023 import org.apache.hadoop.fs.permission.FsAction; 024 import org.apache.hadoop.fs.permission.FsPermission; 025 import org.apache.hadoop.fs.permission.PermissionStatus; 026 import org.apache.hadoop.hdfs.protocol.Block; 027 import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfo; 028 import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfoUnderConstruction; 029 030 /** I-node for closed file. */ 031 public class INodeFile extends INode { 032 static final FsPermission UMASK = FsPermission.createImmutable((short)0111); 033 034 //Number of bits for Block size 035 static final short BLOCKBITS = 48; 036 037 //Header mask 64-bit representation 038 //Format: [16 bits for replication][48 bits for PreferredBlockSize] 039 static final long HEADERMASK = 0xffffL << BLOCKBITS; 040 041 protected long header; 042 043 protected BlockInfo blocks[] = null; 044 045 INodeFile(PermissionStatus permissions, 046 int nrBlocks, short replication, long modificationTime, 047 long atime, long preferredBlockSize) { 048 this(permissions, new BlockInfo[nrBlocks], replication, 049 modificationTime, atime, preferredBlockSize); 050 } 051 052 protected INodeFile() { 053 blocks = null; 054 header = 0; 055 } 056 057 protected INodeFile(PermissionStatus permissions, BlockInfo[] blklist, 058 short replication, long modificationTime, 059 long atime, long preferredBlockSize) { 060 super(permissions, modificationTime, atime); 061 this.setReplication(replication); 062 this.setPreferredBlockSize(preferredBlockSize); 063 blocks = blklist; 064 } 065 066 /** 067 * Set the {@link FsPermission} of this {@link INodeFile}. 068 * Since this is a file, 069 * the {@link FsAction#EXECUTE} action, if any, is ignored. 070 */ 071 protected void setPermission(FsPermission permission) { 072 super.setPermission(permission.applyUMask(UMASK)); 073 } 074 075 public boolean isDirectory() { 076 return false; 077 } 078 079 /** 080 * Get block replication for the file 081 * @return block replication value 082 */ 083 public short getReplication() { 084 return (short) ((header & HEADERMASK) >> BLOCKBITS); 085 } 086 087 public void setReplication(short replication) { 088 if(replication <= 0) 089 throw new IllegalArgumentException("Unexpected value for the replication"); 090 header = ((long)replication << BLOCKBITS) | (header & ~HEADERMASK); 091 } 092 093 /** 094 * Get preferred block size for the file 095 * @return preferred block size in bytes 096 */ 097 public long getPreferredBlockSize() { 098 return header & ~HEADERMASK; 099 } 100 101 public void setPreferredBlockSize(long preferredBlkSize) 102 { 103 if((preferredBlkSize < 0) || (preferredBlkSize > ~HEADERMASK )) 104 throw new IllegalArgumentException("Unexpected value for the block size"); 105 header = (header & HEADERMASK) | (preferredBlkSize & ~HEADERMASK); 106 } 107 108 /** 109 * Get file blocks 110 * @return file blocks 111 */ 112 public BlockInfo[] getBlocks() { 113 return this.blocks; 114 } 115 116 /** 117 * append array of blocks to this.blocks 118 */ 119 void appendBlocks(INodeFile [] inodes, int totalAddedBlocks) { 120 int size = this.blocks.length; 121 122 BlockInfo[] newlist = new BlockInfo[size + totalAddedBlocks]; 123 System.arraycopy(this.blocks, 0, newlist, 0, size); 124 125 for(INodeFile in: inodes) { 126 System.arraycopy(in.blocks, 0, newlist, size, in.blocks.length); 127 size += in.blocks.length; 128 } 129 130 for(BlockInfo bi: newlist) { 131 bi.setINode(this); 132 } 133 this.blocks = newlist; 134 } 135 136 /** 137 * add a block to the block list 138 */ 139 void addBlock(BlockInfo newblock) { 140 if (this.blocks == null) { 141 this.blocks = new BlockInfo[1]; 142 this.blocks[0] = newblock; 143 } else { 144 int size = this.blocks.length; 145 BlockInfo[] newlist = new BlockInfo[size + 1]; 146 System.arraycopy(this.blocks, 0, newlist, 0, size); 147 newlist[size] = newblock; 148 this.blocks = newlist; 149 } 150 } 151 152 /** 153 * Set file block 154 */ 155 public void setBlock(int idx, BlockInfo blk) { 156 this.blocks[idx] = blk; 157 } 158 159 int collectSubtreeBlocksAndClear(List<Block> v) { 160 parent = null; 161 if(blocks != null && v != null) { 162 for (BlockInfo blk : blocks) { 163 v.add(blk); 164 blk.setINode(null); 165 } 166 } 167 blocks = null; 168 return 1; 169 } 170 171 /** {@inheritDoc} */ 172 long[] computeContentSummary(long[] summary) { 173 summary[0] += computeFileSize(true); 174 summary[1]++; 175 summary[3] += diskspaceConsumed(); 176 return summary; 177 } 178 179 /** Compute file size. 180 * May or may not include BlockInfoUnderConstruction. 181 */ 182 long computeFileSize(boolean includesBlockInfoUnderConstruction) { 183 if (blocks == null || blocks.length == 0) { 184 return 0; 185 } 186 final int last = blocks.length - 1; 187 //check if the last block is BlockInfoUnderConstruction 188 long bytes = blocks[last] instanceof BlockInfoUnderConstruction 189 && !includesBlockInfoUnderConstruction? 190 0: blocks[last].getNumBytes(); 191 for(int i = 0; i < last; i++) { 192 bytes += blocks[i].getNumBytes(); 193 } 194 return bytes; 195 } 196 197 198 @Override 199 DirCounts spaceConsumedInTree(DirCounts counts) { 200 counts.nsCount += 1; 201 counts.dsCount += diskspaceConsumed(); 202 return counts; 203 } 204 205 long diskspaceConsumed() { 206 return diskspaceConsumed(blocks); 207 } 208 209 long diskspaceConsumed(Block[] blkArr) { 210 long size = 0; 211 if(blkArr == null) 212 return 0; 213 214 for (Block blk : blkArr) { 215 if (blk != null) { 216 size += blk.getNumBytes(); 217 } 218 } 219 /* If the last block is being written to, use prefferedBlockSize 220 * rather than the actual block size. 221 */ 222 if (blkArr.length > 0 && blkArr[blkArr.length-1] != null && 223 isUnderConstruction()) { 224 size += getPreferredBlockSize() - blkArr[blkArr.length-1].getNumBytes(); 225 } 226 return size * getReplication(); 227 } 228 229 /** 230 * Return the penultimate allocated block for this file. 231 */ 232 BlockInfo getPenultimateBlock() { 233 if (blocks == null || blocks.length <= 1) { 234 return null; 235 } 236 return blocks[blocks.length - 2]; 237 } 238 239 /** 240 * Get the last block of the file. 241 * Make sure it has the right type. 242 */ 243 public <T extends BlockInfo> T getLastBlock() throws IOException { 244 if (blocks == null || blocks.length == 0) 245 return null; 246 T returnBlock = null; 247 try { 248 @SuppressWarnings("unchecked") // ClassCastException is caught below 249 T tBlock = (T)blocks[blocks.length - 1]; 250 returnBlock = tBlock; 251 } catch(ClassCastException cce) { 252 throw new IOException("Unexpected last block type: " 253 + blocks[blocks.length - 1].getClass().getSimpleName()); 254 } 255 return returnBlock; 256 } 257 258 /** @return the number of blocks */ 259 public int numBlocks() { 260 return blocks == null ? 0 : blocks.length; 261 } 262 }