1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package org.apache.hadoop.hdfs.server.namenode; 20 21 import com.google.common.base.Preconditions; 22 import org.apache.commons.io.Charsets; 23 import org.apache.hadoop.fs.ContentSummary; 24 import org.apache.hadoop.fs.DirectoryListingStartAfterNotFoundException; 25 import org.apache.hadoop.fs.FileEncryptionInfo; 26 import org.apache.hadoop.fs.InvalidPathException; 27 import org.apache.hadoop.fs.UnresolvedLinkException; 28 import org.apache.hadoop.fs.permission.FsAction; 29 import org.apache.hadoop.fs.permission.FsPermission; 30 import org.apache.hadoop.hdfs.DFSUtil; 31 import org.apache.hadoop.hdfs.protocol.DirectoryListing; 32 import org.apache.hadoop.hdfs.protocol.FsPermissionExtension; 33 import org.apache.hadoop.hdfs.protocol.HdfsConstants; 34 import org.apache.hadoop.hdfs.protocol.HdfsFileStatus; 35 import org.apache.hadoop.hdfs.protocol.HdfsLocatedFileStatus; 36 import org.apache.hadoop.hdfs.protocol.LocatedBlock; 37 import org.apache.hadoop.hdfs.protocol.LocatedBlocks; 38 import org.apache.hadoop.hdfs.protocol.SnapshotException; 39 import org.apache.hadoop.hdfs.server.blockmanagement.BlockStoragePolicySuite; 40 import org.apache.hadoop.hdfs.server.namenode.snapshot.DirectorySnapshottableFeature; 41 import org.apache.hadoop.hdfs.server.namenode.snapshot.Snapshot; 42 import org.apache.hadoop.hdfs.util.ReadOnlyList; 43 44 import java.io.FileNotFoundException; 45 import java.io.IOException; 46 import java.util.Arrays; 47 48 class FSDirStatAndListingOp { getListingInt(FSDirectory fsd, final String srcArg, byte[] startAfter, boolean needLocation)49 static DirectoryListing getListingInt(FSDirectory fsd, final String srcArg, 50 byte[] startAfter, boolean needLocation) throws IOException { 51 FSPermissionChecker pc = fsd.getPermissionChecker(); 52 byte[][] pathComponents = FSDirectory 53 .getPathComponentsForReservedPath(srcArg); 54 final String startAfterString = new String(startAfter, Charsets.UTF_8); 55 final String src = fsd.resolvePath(pc, srcArg, pathComponents); 56 final INodesInPath iip = fsd.getINodesInPath(src, true); 57 58 // Get file name when startAfter is an INodePath 59 if (FSDirectory.isReservedName(startAfterString)) { 60 byte[][] startAfterComponents = FSDirectory 61 .getPathComponentsForReservedPath(startAfterString); 62 try { 63 String tmp = FSDirectory.resolvePath(src, startAfterComponents, fsd); 64 byte[][] regularPath = INode.getPathComponents(tmp); 65 startAfter = regularPath[regularPath.length - 1]; 66 } catch (IOException e) { 67 // Possibly the inode is deleted 68 throw new DirectoryListingStartAfterNotFoundException( 69 "Can't find startAfter " + startAfterString); 70 } 71 } 72 73 boolean isSuperUser = true; 74 if (fsd.isPermissionEnabled()) { 75 if (iip.getLastINode() != null && iip.getLastINode().isDirectory()) { 76 fsd.checkPathAccess(pc, iip, FsAction.READ_EXECUTE); 77 } else { 78 fsd.checkTraverse(pc, iip); 79 } 80 isSuperUser = pc.isSuperUser(); 81 } 82 return getListing(fsd, iip, src, startAfter, needLocation, isSuperUser); 83 } 84 85 /** 86 * Get the file info for a specific file. 87 * 88 * @param srcArg The string representation of the path to the file 89 * @param resolveLink whether to throw UnresolvedLinkException 90 * if src refers to a symlink 91 * 92 * @return object containing information regarding the file 93 * or null if file not found 94 */ getFileInfo( FSDirectory fsd, String srcArg, boolean resolveLink)95 static HdfsFileStatus getFileInfo( 96 FSDirectory fsd, String srcArg, boolean resolveLink) 97 throws IOException { 98 String src = srcArg; 99 if (!DFSUtil.isValidName(src)) { 100 throw new InvalidPathException("Invalid file name: " + src); 101 } 102 FSPermissionChecker pc = fsd.getPermissionChecker(); 103 byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src); 104 src = fsd.resolvePath(pc, src, pathComponents); 105 final INodesInPath iip = fsd.getINodesInPath(src, resolveLink); 106 boolean isSuperUser = true; 107 if (fsd.isPermissionEnabled()) { 108 fsd.checkPermission(pc, iip, false, null, null, null, null, false); 109 isSuperUser = pc.isSuperUser(); 110 } 111 return getFileInfo(fsd, src, resolveLink, 112 FSDirectory.isReservedRawName(srcArg), isSuperUser); 113 } 114 115 /** 116 * Returns true if the file is closed 117 */ isFileClosed(FSDirectory fsd, String src)118 static boolean isFileClosed(FSDirectory fsd, String src) throws IOException { 119 FSPermissionChecker pc = fsd.getPermissionChecker(); 120 byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src); 121 src = fsd.resolvePath(pc, src, pathComponents); 122 final INodesInPath iip = fsd.getINodesInPath(src, true); 123 if (fsd.isPermissionEnabled()) { 124 fsd.checkTraverse(pc, iip); 125 } 126 return !INodeFile.valueOf(iip.getLastINode(), src).isUnderConstruction(); 127 } 128 getContentSummary( FSDirectory fsd, String src)129 static ContentSummary getContentSummary( 130 FSDirectory fsd, String src) throws IOException { 131 byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src); 132 FSPermissionChecker pc = fsd.getPermissionChecker(); 133 src = fsd.resolvePath(pc, src, pathComponents); 134 final INodesInPath iip = fsd.getINodesInPath(src, false); 135 if (fsd.isPermissionEnabled()) { 136 fsd.checkPermission(pc, iip, false, null, null, null, 137 FsAction.READ_EXECUTE); 138 } 139 return getContentSummaryInt(fsd, iip); 140 } 141 getStoragePolicyID(byte inodePolicy, byte parentPolicy)142 private static byte getStoragePolicyID(byte inodePolicy, byte parentPolicy) { 143 return inodePolicy != BlockStoragePolicySuite.ID_UNSPECIFIED ? inodePolicy : 144 parentPolicy; 145 } 146 147 /** 148 * Get a partial listing of the indicated directory 149 * 150 * We will stop when any of the following conditions is met: 151 * 1) this.lsLimit files have been added 152 * 2) needLocation is true AND enough files have been added such 153 * that at least this.lsLimit block locations are in the response 154 * 155 * @param fsd FSDirectory 156 * @param iip the INodesInPath instance containing all the INodes along the 157 * path 158 * @param src the directory name 159 * @param startAfter the name to start listing after 160 * @param needLocation if block locations are returned 161 * @return a partial listing starting after startAfter 162 */ getListing(FSDirectory fsd, INodesInPath iip, String src, byte[] startAfter, boolean needLocation, boolean isSuperUser)163 private static DirectoryListing getListing(FSDirectory fsd, INodesInPath iip, 164 String src, byte[] startAfter, boolean needLocation, boolean isSuperUser) 165 throws IOException { 166 String srcs = FSDirectory.normalizePath(src); 167 final boolean isRawPath = FSDirectory.isReservedRawName(src); 168 169 fsd.readLock(); 170 try { 171 if (srcs.endsWith(HdfsConstants.SEPARATOR_DOT_SNAPSHOT_DIR)) { 172 return getSnapshotsListing(fsd, srcs, startAfter); 173 } 174 final int snapshot = iip.getPathSnapshotId(); 175 final INode targetNode = iip.getLastINode(); 176 if (targetNode == null) 177 return null; 178 byte parentStoragePolicy = isSuperUser ? 179 targetNode.getStoragePolicyID() : BlockStoragePolicySuite 180 .ID_UNSPECIFIED; 181 182 if (!targetNode.isDirectory()) { 183 return new DirectoryListing( 184 new HdfsFileStatus[]{createFileStatus(fsd, src, 185 HdfsFileStatus.EMPTY_NAME, targetNode, needLocation, 186 parentStoragePolicy, snapshot, isRawPath, iip)}, 0); 187 } 188 189 final INodeDirectory dirInode = targetNode.asDirectory(); 190 final ReadOnlyList<INode> contents = dirInode.getChildrenList(snapshot); 191 int startChild = INodeDirectory.nextChild(contents, startAfter); 192 int totalNumChildren = contents.size(); 193 int numOfListing = Math.min(totalNumChildren - startChild, 194 fsd.getLsLimit()); 195 int locationBudget = fsd.getLsLimit(); 196 int listingCnt = 0; 197 HdfsFileStatus listing[] = new HdfsFileStatus[numOfListing]; 198 for (int i=0; i<numOfListing && locationBudget>0; i++) { 199 INode cur = contents.get(startChild+i); 200 byte curPolicy = isSuperUser && !cur.isSymlink()? 201 cur.getLocalStoragePolicyID(): 202 BlockStoragePolicySuite.ID_UNSPECIFIED; 203 listing[i] = createFileStatus(fsd, src, cur.getLocalNameBytes(), cur, 204 needLocation, getStoragePolicyID(curPolicy, 205 parentStoragePolicy), snapshot, isRawPath, iip); 206 listingCnt++; 207 if (needLocation) { 208 // Once we hit lsLimit locations, stop. 209 // This helps to prevent excessively large response payloads. 210 // Approximate #locations with locatedBlockCount() * repl_factor 211 LocatedBlocks blks = 212 ((HdfsLocatedFileStatus)listing[i]).getBlockLocations(); 213 locationBudget -= (blks == null) ? 0 : 214 blks.locatedBlockCount() * listing[i].getReplication(); 215 } 216 } 217 // truncate return array if necessary 218 if (listingCnt < numOfListing) { 219 listing = Arrays.copyOf(listing, listingCnt); 220 } 221 return new DirectoryListing( 222 listing, totalNumChildren-startChild-listingCnt); 223 } finally { 224 fsd.readUnlock(); 225 } 226 } 227 228 /** 229 * Get a listing of all the snapshots of a snapshottable directory 230 */ getSnapshotsListing( FSDirectory fsd, String src, byte[] startAfter)231 private static DirectoryListing getSnapshotsListing( 232 FSDirectory fsd, String src, byte[] startAfter) 233 throws IOException { 234 Preconditions.checkState(fsd.hasReadLock()); 235 Preconditions.checkArgument( 236 src.endsWith(HdfsConstants.SEPARATOR_DOT_SNAPSHOT_DIR), 237 "%s does not end with %s", src, HdfsConstants.SEPARATOR_DOT_SNAPSHOT_DIR); 238 239 final String dirPath = FSDirectory.normalizePath(src.substring(0, 240 src.length() - HdfsConstants.DOT_SNAPSHOT_DIR.length())); 241 242 final INode node = fsd.getINode(dirPath); 243 final INodeDirectory dirNode = INodeDirectory.valueOf(node, dirPath); 244 final DirectorySnapshottableFeature sf = dirNode.getDirectorySnapshottableFeature(); 245 if (sf == null) { 246 throw new SnapshotException( 247 "Directory is not a snapshottable directory: " + dirPath); 248 } 249 final ReadOnlyList<Snapshot> snapshots = sf.getSnapshotList(); 250 int skipSize = ReadOnlyList.Util.binarySearch(snapshots, startAfter); 251 skipSize = skipSize < 0 ? -skipSize - 1 : skipSize + 1; 252 int numOfListing = Math.min(snapshots.size() - skipSize, fsd.getLsLimit()); 253 final HdfsFileStatus listing[] = new HdfsFileStatus[numOfListing]; 254 for (int i = 0; i < numOfListing; i++) { 255 Snapshot.Root sRoot = snapshots.get(i + skipSize).getRoot(); 256 listing[i] = createFileStatus(fsd, src, sRoot.getLocalNameBytes(), sRoot, 257 BlockStoragePolicySuite.ID_UNSPECIFIED, Snapshot.CURRENT_STATE_ID, 258 false, INodesInPath.fromINode(sRoot)); 259 } 260 return new DirectoryListing( 261 listing, snapshots.size() - skipSize - numOfListing); 262 } 263 264 /** Get the file info for a specific file. 265 * @param fsd FSDirectory 266 * @param src The string representation of the path to the file 267 * @param isRawPath true if a /.reserved/raw pathname was passed by the user 268 * @param includeStoragePolicy whether to include storage policy 269 * @return object containing information regarding the file 270 * or null if file not found 271 */ 272 static HdfsFileStatus getFileInfo( 273 FSDirectory fsd, String path, INodesInPath src, boolean isRawPath, 274 boolean includeStoragePolicy) 275 throws IOException { 276 fsd.readLock(); 277 try { 278 final INode i = src.getLastINode(); 279 byte policyId = includeStoragePolicy && i != null && !i.isSymlink() ? 280 i.getStoragePolicyID() : BlockStoragePolicySuite.ID_UNSPECIFIED; 281 return i == null ? null : createFileStatus( 282 fsd, path, HdfsFileStatus.EMPTY_NAME, i, policyId, 283 src.getPathSnapshotId(), isRawPath, src); 284 } finally { 285 fsd.readUnlock(); 286 } 287 } 288 289 static HdfsFileStatus getFileInfo( 290 FSDirectory fsd, String src, boolean resolveLink, boolean isRawPath, 291 boolean includeStoragePolicy) 292 throws IOException { 293 String srcs = FSDirectory.normalizePath(src); 294 if (srcs.endsWith(HdfsConstants.SEPARATOR_DOT_SNAPSHOT_DIR)) { 295 if (fsd.getINode4DotSnapshot(srcs) != null) { 296 return new HdfsFileStatus(0, true, 0, 0, 0, 0, null, null, null, null, 297 HdfsFileStatus.EMPTY_NAME, -1L, 0, null, 298 BlockStoragePolicySuite.ID_UNSPECIFIED); 299 } 300 return null; 301 } 302 303 fsd.readLock(); 304 try { 305 final INodesInPath iip = fsd.getINodesInPath(srcs, resolveLink); 306 return getFileInfo(fsd, src, iip, isRawPath, includeStoragePolicy); 307 } finally { 308 fsd.readUnlock(); 309 } 310 } 311 312 /** 313 * Currently we only support "ls /xxx/.snapshot" which will return all the 314 * snapshots of a directory. The FSCommand Ls will first call getFileInfo to 315 * make sure the file/directory exists (before the real getListing call). 316 * Since we do not have a real INode for ".snapshot", we return an empty 317 * non-null HdfsFileStatus here. 318 */ 319 private static HdfsFileStatus getFileInfo4DotSnapshot( 320 FSDirectory fsd, String src) 321 throws UnresolvedLinkException { 322 if (fsd.getINode4DotSnapshot(src) != null) { 323 return new HdfsFileStatus(0, true, 0, 0, 0, 0, null, null, null, null, 324 HdfsFileStatus.EMPTY_NAME, -1L, 0, null, 325 BlockStoragePolicySuite.ID_UNSPECIFIED); 326 } 327 return null; 328 } 329 330 /** 331 * create an hdfs file status from an inode 332 * 333 * @param fsd FSDirectory 334 * @param path the local name 335 * @param node inode 336 * @param needLocation if block locations need to be included or not 337 * @param isRawPath true if this is being called on behalf of a path in 338 * /.reserved/raw 339 * @return a file status 340 * @throws java.io.IOException if any error occurs 341 */ 342 static HdfsFileStatus createFileStatus( 343 FSDirectory fsd, String fullPath, byte[] path, INode node, 344 boolean needLocation, byte storagePolicy, int snapshot, boolean isRawPath, 345 INodesInPath iip) 346 throws IOException { 347 if (needLocation) { 348 return createLocatedFileStatus(fsd, fullPath, path, node, storagePolicy, 349 snapshot, isRawPath, iip); 350 } else { 351 return createFileStatus(fsd, fullPath, path, node, storagePolicy, snapshot, 352 isRawPath, iip); 353 } 354 } 355 356 /** 357 * Create FileStatus by file INode 358 */ 359 static HdfsFileStatus createFileStatus( 360 FSDirectory fsd, String fullPath, byte[] path, INode node, 361 byte storagePolicy, int snapshot, boolean isRawPath, 362 INodesInPath iip) throws IOException { 363 long size = 0; // length is zero for directories 364 short replication = 0; 365 long blocksize = 0; 366 final boolean isEncrypted; 367 368 final FileEncryptionInfo feInfo = isRawPath ? null : 369 fsd.getFileEncryptionInfo(node, snapshot, iip); 370 371 if (node.isFile()) { 372 final INodeFile fileNode = node.asFile(); 373 size = fileNode.computeFileSize(snapshot); 374 replication = fileNode.getFileReplication(snapshot); 375 blocksize = fileNode.getPreferredBlockSize(); 376 isEncrypted = (feInfo != null) || 377 (isRawPath && fsd.isInAnEZ(INodesInPath.fromINode(node))); 378 } else { 379 isEncrypted = fsd.isInAnEZ(INodesInPath.fromINode(node)); 380 } 381 382 int childrenNum = node.isDirectory() ? 383 node.asDirectory().getChildrenNum(snapshot) : 0; 384 385 INodeAttributes nodeAttrs = 386 fsd.getAttributes(fullPath, path, node, snapshot); 387 return new HdfsFileStatus( 388 size, 389 node.isDirectory(), 390 replication, 391 blocksize, 392 node.getModificationTime(snapshot), 393 node.getAccessTime(snapshot), 394 getPermissionForFileStatus(nodeAttrs, isEncrypted), 395 nodeAttrs.getUserName(), 396 nodeAttrs.getGroupName(), 397 node.isSymlink() ? node.asSymlink().getSymlink() : null, 398 path, 399 node.getId(), 400 childrenNum, 401 feInfo, 402 storagePolicy); 403 } 404 405 /** 406 * Create FileStatus with location info by file INode 407 */ 408 private static HdfsLocatedFileStatus createLocatedFileStatus( 409 FSDirectory fsd, String fullPath, byte[] path, INode node, 410 byte storagePolicy, int snapshot, boolean isRawPath, 411 INodesInPath iip) throws IOException { 412 assert fsd.hasReadLock(); 413 long size = 0; // length is zero for directories 414 short replication = 0; 415 long blocksize = 0; 416 LocatedBlocks loc = null; 417 final boolean isEncrypted; 418 final FileEncryptionInfo feInfo = isRawPath ? null : 419 fsd.getFileEncryptionInfo(node, snapshot, iip); 420 if (node.isFile()) { 421 final INodeFile fileNode = node.asFile(); 422 size = fileNode.computeFileSize(snapshot); 423 replication = fileNode.getFileReplication(snapshot); 424 blocksize = fileNode.getPreferredBlockSize(); 425 426 final boolean inSnapshot = snapshot != Snapshot.CURRENT_STATE_ID; 427 final boolean isUc = !inSnapshot && fileNode.isUnderConstruction(); 428 final long fileSize = !inSnapshot && isUc ? 429 fileNode.computeFileSizeNotIncludingLastUcBlock() : size; 430 431 loc = fsd.getFSNamesystem().getBlockManager().createLocatedBlocks( 432 fileNode.getBlocks(snapshot), fileSize, isUc, 0L, size, false, 433 inSnapshot, feInfo); 434 if (loc == null) { 435 loc = new LocatedBlocks(); 436 } 437 isEncrypted = (feInfo != null) || 438 (isRawPath && fsd.isInAnEZ(INodesInPath.fromINode(node))); 439 } else { 440 isEncrypted = fsd.isInAnEZ(INodesInPath.fromINode(node)); 441 } 442 int childrenNum = node.isDirectory() ? 443 node.asDirectory().getChildrenNum(snapshot) : 0; 444 445 INodeAttributes nodeAttrs = 446 fsd.getAttributes(fullPath, path, node, snapshot); 447 HdfsLocatedFileStatus status = 448 new HdfsLocatedFileStatus(size, node.isDirectory(), replication, 449 blocksize, node.getModificationTime(snapshot), 450 node.getAccessTime(snapshot), 451 getPermissionForFileStatus(nodeAttrs, isEncrypted), 452 nodeAttrs.getUserName(), nodeAttrs.getGroupName(), 453 node.isSymlink() ? node.asSymlink().getSymlink() : null, path, 454 node.getId(), loc, childrenNum, feInfo, storagePolicy); 455 // Set caching information for the located blocks. 456 if (loc != null) { 457 CacheManager cacheManager = fsd.getFSNamesystem().getCacheManager(); 458 for (LocatedBlock lb: loc.getLocatedBlocks()) { 459 cacheManager.setCachedLocations(lb); 460 } 461 } 462 return status; 463 } 464 465 /** 466 * Returns an inode's FsPermission for use in an outbound FileStatus. If the 467 * inode has an ACL or is for an encrypted file/dir, then this method will 468 * return an FsPermissionExtension. 469 * 470 * @param node INode to check 471 * @param snapshot int snapshot ID 472 * @param isEncrypted boolean true if the file/dir is encrypted 473 * @return FsPermission from inode, with ACL bit on if the inode has an ACL 474 * and encrypted bit on if it represents an encrypted file/dir. 475 */ 476 private static FsPermission getPermissionForFileStatus( 477 INodeAttributes node, boolean isEncrypted) { 478 FsPermission perm = node.getFsPermission(); 479 boolean hasAcl = node.getAclFeature() != null; 480 if (hasAcl || isEncrypted) { 481 perm = new FsPermissionExtension(perm, hasAcl, isEncrypted); 482 } 483 return perm; 484 } 485 486 private static ContentSummary getContentSummaryInt(FSDirectory fsd, 487 INodesInPath iip) throws IOException { 488 fsd.readLock(); 489 try { 490 INode targetNode = iip.getLastINode(); 491 if (targetNode == null) { 492 throw new FileNotFoundException("File does not exist: " + iip.getPath()); 493 } 494 else { 495 // Make it relinquish locks everytime contentCountLimit entries are 496 // processed. 0 means disabled. I.e. blocking for the entire duration. 497 ContentSummaryComputationContext cscc = 498 new ContentSummaryComputationContext(fsd, fsd.getFSNamesystem(), 499 fsd.getContentCountLimit(), fsd.getContentSleepMicroSec()); 500 ContentSummary cs = targetNode.computeAndConvertContentSummary(cscc); 501 fsd.addYieldCount(cscc.getYieldCount()); 502 return cs; 503 } 504 } finally { 505 fsd.readUnlock(); 506 } 507 } 508 } 509