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; 019 020import java.util.Arrays; 021import java.util.List; 022 023import org.apache.hadoop.fs.ContentSummary; 024import org.apache.hadoop.fs.Path; 025import org.apache.hadoop.fs.permission.FsPermission; 026import org.apache.hadoop.fs.permission.PermissionStatus; 027import org.apache.hadoop.hdfs.DFSUtil; 028import org.apache.hadoop.hdfs.protocol.Block; 029import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfo; 030import org.apache.hadoop.util.StringUtils; 031 032/** 033 * We keep an in-memory representation of the file/block hierarchy. 034 * This is a base INode class containing common fields for file and 035 * directory inodes. 036 */ 037public abstract class INode implements Comparable<byte[]>, FSInodeInfo { 038 /* 039 * The inode name is in java UTF8 encoding; 040 * The name in HdfsFileStatus should keep the same encoding as this. 041 * if this encoding is changed, implicitly getFileInfo and listStatus in 042 * clientProtocol are changed; The decoding at the client 043 * side should change accordingly. 044 */ 045 protected byte[] name; 046 protected INodeDirectory parent; 047 protected long modificationTime; 048 protected long accessTime; 049 050 /** Simple wrapper for two counters : 051 * nsCount (namespace consumed) and dsCount (diskspace consumed). 052 */ 053 static class DirCounts { 054 long nsCount = 0; 055 long dsCount = 0; 056 057 /** returns namespace count */ 058 long getNsCount() { 059 return nsCount; 060 } 061 /** returns diskspace count */ 062 long getDsCount() { 063 return dsCount; 064 } 065 } 066 067 //Only updated by updatePermissionStatus(...). 068 //Other codes should not modify it. 069 private long permission; 070 071 private static enum PermissionStatusFormat { 072 MODE(0, 16), 073 GROUP(MODE.OFFSET + MODE.LENGTH, 25), 074 USER(GROUP.OFFSET + GROUP.LENGTH, 23); 075 076 final int OFFSET; 077 final int LENGTH; //bit length 078 final long MASK; 079 080 PermissionStatusFormat(int offset, int length) { 081 OFFSET = offset; 082 LENGTH = length; 083 MASK = ((-1L) >>> (64 - LENGTH)) << OFFSET; 084 } 085 086 long retrieve(long record) { 087 return (record & MASK) >>> OFFSET; 088 } 089 090 long combine(long bits, long record) { 091 return (record & ~MASK) | (bits << OFFSET); 092 } 093 } 094 095 protected INode() { 096 name = null; 097 parent = null; 098 modificationTime = 0; 099 accessTime = 0; 100 } 101 102 INode(PermissionStatus permissions, long mTime, long atime) { 103 this.name = null; 104 this.parent = null; 105 this.modificationTime = mTime; 106 setAccessTime(atime); 107 setPermissionStatus(permissions); 108 } 109 110 protected INode(String name, PermissionStatus permissions) { 111 this(permissions, 0L, 0L); 112 setLocalName(name); 113 } 114 115 /** copy constructor 116 * 117 * @param other Other node to be copied 118 */ 119 INode(INode other) { 120 setLocalName(other.getLocalName()); 121 this.parent = other.getParent(); 122 setPermissionStatus(other.getPermissionStatus()); 123 setModificationTime(other.getModificationTime()); 124 setAccessTime(other.getAccessTime()); 125 } 126 127 /** 128 * Check whether this is the root inode. 129 */ 130 boolean isRoot() { 131 return name.length == 0; 132 } 133 134 /** Set the {@link PermissionStatus} */ 135 protected void setPermissionStatus(PermissionStatus ps) { 136 setUser(ps.getUserName()); 137 setGroup(ps.getGroupName()); 138 setPermission(ps.getPermission()); 139 } 140 /** Get the {@link PermissionStatus} */ 141 protected PermissionStatus getPermissionStatus() { 142 return new PermissionStatus(getUserName(),getGroupName(),getFsPermission()); 143 } 144 private synchronized void updatePermissionStatus( 145 PermissionStatusFormat f, long n) { 146 permission = f.combine(n, permission); 147 } 148 /** Get user name */ 149 public String getUserName() { 150 int n = (int)PermissionStatusFormat.USER.retrieve(permission); 151 return SerialNumberManager.INSTANCE.getUser(n); 152 } 153 /** Set user */ 154 protected void setUser(String user) { 155 int n = SerialNumberManager.INSTANCE.getUserSerialNumber(user); 156 updatePermissionStatus(PermissionStatusFormat.USER, n); 157 } 158 /** Get group name */ 159 public String getGroupName() { 160 int n = (int)PermissionStatusFormat.GROUP.retrieve(permission); 161 return SerialNumberManager.INSTANCE.getGroup(n); 162 } 163 /** Set group */ 164 protected void setGroup(String group) { 165 int n = SerialNumberManager.INSTANCE.getGroupSerialNumber(group); 166 updatePermissionStatus(PermissionStatusFormat.GROUP, n); 167 } 168 /** Get the {@link FsPermission} */ 169 public FsPermission getFsPermission() { 170 return new FsPermission( 171 (short)PermissionStatusFormat.MODE.retrieve(permission)); 172 } 173 protected short getFsPermissionShort() { 174 return (short)PermissionStatusFormat.MODE.retrieve(permission); 175 } 176 /** Set the {@link FsPermission} of this {@link INode} */ 177 protected void setPermission(FsPermission permission) { 178 updatePermissionStatus(PermissionStatusFormat.MODE, permission.toShort()); 179 } 180 181 /** 182 * Check whether it's a directory 183 */ 184 public abstract boolean isDirectory(); 185 186 /** 187 * Collect all the blocks in all children of this INode. 188 * Count and return the number of files in the sub tree. 189 * Also clears references since this INode is deleted. 190 */ 191 abstract int collectSubtreeBlocksAndClear(List<Block> v); 192 193 /** Compute {@link ContentSummary}. Blocking computation. */ 194 public final ContentSummary computeContentSummary() { 195 return computeAndConvertContentSummary( 196 new ContentSummaryComputationContext()); 197 } 198 199 /** Compute {@link ContentSummary} */ 200 public final ContentSummary computeAndConvertContentSummary( 201 ContentSummaryComputationContext summary) { 202 long[] a = computeContentSummary(summary).getCounts(); 203 return new ContentSummary(a[0], a[1], a[2], getNsQuota(), 204 a[3], getDsQuota()); 205 } 206 /** 207 * @return ContentSummaryComputationContext containing 208 * content counts. 209 * 0: length, 1: file count, 2: directory count 3: disk space 210 */ 211 abstract ContentSummaryComputationContext computeContentSummary( 212 ContentSummaryComputationContext summary); 213 214 /** 215 * Get the quota set for this inode 216 * @return the quota if it is set; -1 otherwise 217 */ 218 long getNsQuota() { 219 return -1; 220 } 221 222 long getDsQuota() { 223 return -1; 224 } 225 226 boolean isQuotaSet() { 227 return getNsQuota() >= 0 || getDsQuota() >= 0; 228 } 229 230 /** 231 * Adds total number of names and total disk space taken under 232 * this tree to counts. 233 * Returns updated counts object. 234 */ 235 abstract DirCounts spaceConsumedInTree(DirCounts counts); 236 237 /** 238 * Get local file name 239 * @return local file name 240 */ 241 String getLocalName() { 242 return DFSUtil.bytes2String(name); 243 } 244 245 246 String getLocalParentDir() { 247 INode inode = isRoot() ? this : getParent(); 248 String parentDir = ""; 249 if (inode != null) { 250 parentDir = inode.getFullPathName(); 251 } 252 return (parentDir != null) ? parentDir : ""; 253 } 254 255 /** 256 * Get local file name 257 * @return local file name 258 */ 259 byte[] getLocalNameBytes() { 260 return name; 261 } 262 263 /** 264 * Set local file name 265 */ 266 void setLocalName(String name) { 267 this.name = DFSUtil.string2Bytes(name); 268 } 269 270 /** 271 * Set local file name 272 */ 273 void setLocalName(byte[] name) { 274 this.name = name; 275 } 276 277 /** {@inheritDoc} */ 278 public String getFullPathName() { 279 // Get the full path name of this inode. 280 return FSDirectory.getFullPathName(this); 281 } 282 283 /** {@inheritDoc} */ 284 public String toString() { 285 return "\"" + getFullPathName() + "\":" 286 + getUserName() + ":" + getGroupName() + ":" 287 + (isDirectory()? "d": "-") + getFsPermission(); 288 } 289 290 /** 291 * Get parent directory 292 * @return parent INode 293 */ 294 INodeDirectory getParent() { 295 return this.parent; 296 } 297 298 /** 299 * Get last modification time of inode. 300 * @return access time 301 */ 302 public long getModificationTime() { 303 return this.modificationTime; 304 } 305 306 /** 307 * Set last modification time of inode. 308 */ 309 void setModificationTime(long modtime) { 310 assert isDirectory(); 311 if (this.modificationTime <= modtime) { 312 this.modificationTime = modtime; 313 } 314 } 315 316 /** 317 * Always set the last modification time of inode. 318 */ 319 void setModificationTimeForce(long modtime) { 320 this.modificationTime = modtime; 321 } 322 323 /** 324 * Get access time of inode. 325 * @return access time 326 */ 327 public long getAccessTime() { 328 return accessTime; 329 } 330 331 /** 332 * Set last access time of inode. 333 */ 334 void setAccessTime(long atime) { 335 accessTime = atime; 336 } 337 338 /** 339 * Is this inode being constructed? 340 */ 341 public boolean isUnderConstruction() { 342 return false; 343 } 344 345 /** 346 * Check whether it's a symlink 347 */ 348 public boolean isLink() { 349 return false; 350 } 351 352 /** 353 * Breaks file path into components. 354 * @param path 355 * @return array of byte arrays each of which represents 356 * a single path component. 357 */ 358 static byte[][] getPathComponents(String path) { 359 return getPathComponents(getPathNames(path)); 360 } 361 362 /** Convert strings to byte arrays for path components. */ 363 static byte[][] getPathComponents(String[] strings) { 364 if (strings.length == 0) { 365 return new byte[][]{null}; 366 } 367 byte[][] bytes = new byte[strings.length][]; 368 for (int i = 0; i < strings.length; i++) 369 bytes[i] = DFSUtil.string2Bytes(strings[i]); 370 return bytes; 371 } 372 373 /** 374 * Splits an absolute path into an array of path components. 375 * @param path 376 * @throws AssertionError if the given path is invalid. 377 * @return array of path components. 378 */ 379 static String[] getPathNames(String path) { 380 if (path == null || !path.startsWith(Path.SEPARATOR)) { 381 throw new AssertionError("Absolute path required"); 382 } 383 return StringUtils.split(path, Path.SEPARATOR_CHAR); 384 } 385 386 /** 387 * Given some components, create a path name. 388 * @param components The path components 389 * @param start index 390 * @param end index 391 * @return concatenated path 392 */ 393 static String constructPath(byte[][] components, int start, int end) { 394 StringBuilder buf = new StringBuilder(); 395 for (int i = start; i < end; i++) { 396 buf.append(DFSUtil.bytes2String(components[i])); 397 if (i < end - 1) { 398 buf.append(Path.SEPARATOR); 399 } 400 } 401 return buf.toString(); 402 } 403 404 boolean removeNode() { 405 if (parent == null) { 406 return false; 407 } else { 408 parent.removeChild(this); 409 parent = null; 410 return true; 411 } 412 } 413 414 // 415 // Comparable interface 416 // 417 public int compareTo(byte[] o) { 418 return compareBytes(name, o); 419 } 420 421 public boolean equals(Object o) { 422 if (!(o instanceof INode)) { 423 return false; 424 } 425 return Arrays.equals(this.name, ((INode)o).name); 426 } 427 428 public int hashCode() { 429 return Arrays.hashCode(this.name); 430 } 431 432 // 433 // static methods 434 // 435 /** 436 * Compare two byte arrays. 437 * 438 * @return a negative integer, zero, or a positive integer 439 * as defined by {@link #compareTo(byte[])}. 440 */ 441 static int compareBytes(byte[] a1, byte[] a2) { 442 if (a1==a2) 443 return 0; 444 int len1 = (a1==null ? 0 : a1.length); 445 int len2 = (a2==null ? 0 : a2.length); 446 int n = Math.min(len1, len2); 447 byte b1, b2; 448 for (int i=0; i<n; i++) { 449 b1 = a1[i]; 450 b2 = a2[i]; 451 if (b1 != b2) 452 return b1 - b2; 453 } 454 return len1 - len2; 455 } 456 457 /** 458 * Create an INode; the inode's name is not set yet 459 * 460 * @param permissions permissions 461 * @param blocks blocks if a file 462 * @param symlink symblic link if a symbolic link 463 * @param replication replication factor 464 * @param modificationTime modification time 465 * @param atime access time 466 * @param nsQuota namespace quota 467 * @param dsQuota disk quota 468 * @param preferredBlockSize block size 469 * @return an inode 470 */ 471 static INode newINode(PermissionStatus permissions, 472 BlockInfo[] blocks, 473 String symlink, 474 short replication, 475 long modificationTime, 476 long atime, 477 long nsQuota, 478 long dsQuota, 479 long preferredBlockSize) { 480 if (symlink.length() != 0) { // check if symbolic link 481 return new INodeSymlink(symlink, modificationTime, atime, permissions); 482 } else if (blocks == null) { //not sym link and blocks null? directory! 483 if (nsQuota >= 0 || dsQuota >= 0) { 484 return new INodeDirectoryWithQuota( 485 permissions, modificationTime, nsQuota, dsQuota); 486 } 487 // regular directory 488 return new INodeDirectory(permissions, modificationTime); 489 } 490 // file 491 return new INodeFile(permissions, blocks, replication, 492 modificationTime, atime, preferredBlockSize); 493 } 494}