blob: 326b2968739875aebd4d5758a623874e0c61ab2c [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hdfs.server.namenode;
import com.google.common.base.Preconditions;
import org.apache.commons.io.Charsets;
import org.apache.hadoop.fs.ContentSummary;
import org.apache.hadoop.fs.FileEncryptionInfo;
import org.apache.hadoop.fs.InvalidPathException;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.fs.permission.PermissionStatus;
import org.apache.hadoop.hdfs.DFSUtil;
import org.apache.hadoop.hdfs.protocol.ClientProtocol;
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
import org.apache.hadoop.hdfs.protocol.FsPermissionExtension;
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
import org.apache.hadoop.hdfs.protocol.HdfsFileStatus;
import org.apache.hadoop.hdfs.protocol.HdfsLocatedFileStatus;
import org.apache.hadoop.hdfs.protocol.LocatedBlock;
import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
import org.apache.hadoop.hdfs.server.blockmanagement.BlockManager;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Map;
import static org.apache.hadoop.util.Time.now;
class FSDirStatAndListingOp {
static DirectoryListing getListingInt(FSDirectory fsd, final String srcArg,
byte[] startAfter, boolean needLocation) throws IOException {
FSPermissionChecker pc = fsd.getPermissionChecker();
String startAfterComponent = startAfter.length == 0 ? "" : new String(startAfter, Charsets.UTF_8);
try (ROTransaction tx = fsd.newROTransaction().begin()) {
Resolver.Result paths = Resolver.resolve(tx, srcArg);
if (paths.invalidPath()) {
throw new InvalidPathException(srcArg);
} else if (paths.notFound()) {
return null;
}
final FlatINodesInPath iip = paths.inodesInPath();
boolean isSuperUser = true;
if (fsd.isPermissionEnabled()) {
if (iip.getLastINode() != null && iip.getLastINode().isDirectory()) {
fsd.checkPathAccess(pc, iip, FsAction.READ_EXECUTE);
} else {
fsd.checkTraverse(pc, paths);
}
isSuperUser = pc.isSuperUser();
}
return getListing(tx, fsd, iip, srcArg, startAfterComponent, needLocation,
isSuperUser);
}
}
/**
* Get the file info for a specific file.
*
* @param srcArg The string representation of the path to the file
* @param resolveLink whether to throw UnresolvedLinkException
* if src refers to a symlink
*
* @return object containing information regarding the file
* or null if file not found
*/
static HdfsFileStatus getFileInfo(
FSDirectory fsd, String srcArg, boolean resolveLink)
throws IOException {
String src = srcArg;
if (!DFSUtil.isValidName(src)) {
throw new InvalidPathException("Invalid file name: " + src);
}
FSPermissionChecker pc = fsd.getPermissionChecker();
try (ROTransaction tx = fsd.newROTransaction().begin()) {
Resolver.Result paths = Resolver.resolve(tx, src);
if (!paths.ok()) {
return null;
}
// TODO: Handle storage policy ID
if (fsd.isPermissionEnabled()) {
fsd.checkTraverse(pc, paths);
}
// byte policyId = includeStoragePolicy && i != null && !i.isSymlink() ?
// i.getStoragePolicyID() : BlockStoragePolicySuite.ID_UNSPECIFIED;
byte policyId = HdfsConstants.BLOCK_STORAGE_POLICY_ID_UNSPECIFIED;
return createFileStatus(tx, fsd, paths.inodesInPath().getLastINode(),
HdfsFileStatus.EMPTY_NAME,
policyId);
}
}
/**
* Returns true if the file is closed
*/
static boolean isFileClosed(FSDirectory fsd, String src) throws IOException {
FSPermissionChecker pc = fsd.getPermissionChecker();
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src);
src = fsd.resolvePath(pc, src, pathComponents);
final INodesInPath iip = fsd.getINodesInPath(src, true);
if (fsd.isPermissionEnabled()) {
fsd.checkTraverse(pc, iip);
}
return !INodeFile.valueOf(iip.getLastINode(), src).isUnderConstruction();
}
static ContentSummary getContentSummary(
FSDirectory fsd, String src) throws IOException {
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src);
FSPermissionChecker pc = fsd.getPermissionChecker();
src = fsd.resolvePath(pc, src, pathComponents);
final INodesInPath iip = fsd.getINodesInPath(src, false);
if (fsd.isPermissionEnabled()) {
fsd.checkPermission(pc, iip, false, null, null, null,
FsAction.READ_EXECUTE);
}
return getContentSummaryInt(fsd, iip);
}
/**
* Get block locations within the specified range.
* @see ClientProtocol#getBlockLocations(String, long, long)
* @throws IOException
*/
static GetBlockLocationsResult getBlockLocations(
FSDirectory fsd, FSPermissionChecker pc, String src, long offset,
long length, boolean needBlockToken) throws IOException {
Preconditions.checkArgument(offset >= 0,
"Negative offset is not supported.");
Preconditions.checkArgument(length >= 0,
"Negative length is not supported.");
CacheManager cm = fsd.getFSNamesystem().getCacheManager();
BlockManager bm = fsd.getBlockManager();
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src);
src = fsd.resolvePath(pc, src, pathComponents);
try (ROTransaction tx = fsd.newROTransaction().begin()){
Resolver.Result paths = Resolver.resolve(tx, src);
if (paths.invalidPath()) {
throw new InvalidPathException(src);
} else if (paths.notFound()) {
throw new FileNotFoundException(src);
}
if (fsd.isPermissionEnabled()) {
fsd.checkPathAccess(pc, paths.inodesInPath(), FsAction.READ);
fsd.checkUnreadableBySuperuser(pc, paths.inodesInPath());
}
FlatINode inode = paths.inodesInPath().getLastINode();
FlatINodeFileFeature file = inode.feature(FlatINodeFileFeature.class);
if (file == null) {
throw new FileNotFoundException(src);
}
final long fileSize = file.fileSize();
final FileEncryptionInfo feInfo = null;
// final FileEncryptionInfo feInfo = FSDirectory.isReservedRawName(src)
// ? null
// : fsd.getFileEncryptionInfo(inode, iip.getPathSnapshotId(), iip);
LocatedBlocks blocks = bm.createLocatedBlocks(
file.blocks(), offset, length, fileSize, needBlockToken);
// TODO: Fix snapshot
blocks = FSDirStatAndListingOp.attachFileInfo(
blocks, fileSize, file.inConstruction(), false, feInfo);
// Set caching information for the located blocks.
for (LocatedBlock lb : blocks.getLocatedBlocks()) {
cm.setCachedLocations(lb);
}
final long now = now();
boolean updateAccessTime = fsd.isAccessTimeSupported()
&& now > inode.atime() + fsd.getAccessTimePrecision();
return new GetBlockLocationsResult(updateAccessTime, blocks);
}
}
private static byte getStoragePolicyID(byte inodePolicy, byte parentPolicy) {
return inodePolicy != HdfsConstants.BLOCK_STORAGE_POLICY_ID_UNSPECIFIED
? inodePolicy : parentPolicy;
}
/**
* Get a partial listing of the indicated directory
*
* We will stop when any of the following conditions is met:
* 1) this.lsLimit files have been added
* 2) needLocation is true AND enough files have been added such
* that at least this.lsLimit block locations are in the response
*
* @param fsd FSDirectory
* @param iip the INodesInPath instance containing all the INodes along the
* path
* @param src the directory name
* @param startAfter the name to start listing after
* @param needLocation if block locations are returned
* @return a partial listing starting after startAfter
*/
private static DirectoryListing getListing(
ROTransaction tx,
FSDirectory fsd, FlatINodesInPath iip,
String src, String startAfter, boolean needLocation, boolean isSuperUser)
throws IOException {
String srcs = FSDirectory.normalizePath(src);
final boolean isRawPath = FSDirectory.isReservedRawName(src);
final FlatINode targetNode = iip.getLastINode();
// TODO: Handle storage policy
// byte parentStoragePolicy = isSuperUser ?
// targetNode.getStoragePolicyID() : HdfsConstants
// .BLOCK_STORAGE_POLICY_ID_UNSPECIFIED;
byte parentStoragePolicy = HdfsConstants
.BLOCK_STORAGE_POLICY_ID_UNSPECIFIED;
if (!targetNode.isDirectory()) {
return new DirectoryListing(
new HdfsFileStatus[]{ createFileStatus(
tx, fsd, targetNode, HdfsFileStatus.EMPTY_NAME,
parentStoragePolicy)
}, 0);
}
try (DBChildrenView children = tx.childrenView(targetNode.id())) {
children.seekTo(ByteBuffer.wrap(startAfter.getBytes(Charsets.UTF_8)));
int numOfListing = fsd.getLsLimit();
int locationBudget = fsd.getLsLimit();
int listingCnt = 0;
int i = 0;
HdfsFileStatus listing[] = new HdfsFileStatus[numOfListing];
for (Map.Entry<ByteBuffer, Long> e : children) {
if (locationBudget < 0 && i >= listing.length) {
break;
}
FlatINode cur = tx.getINode(e.getValue());
// TODO: Handle Storage policy
// byte curPolicy = isSuperUser && !cur.isSymlink()?
// cur.getLocalStoragePolicyID():
// HdfsConstantsClient.BLOCK_STORAGE_POLICY_ID_UNSPECIFIED;
byte curPolicy = HdfsConstants.BLOCK_STORAGE_POLICY_ID_UNSPECIFIED;
ByteBuffer b = e.getKey().duplicate();
byte[] localName = new byte[b.remaining()];
b.get(localName);
listing[i] = createFileStatus(tx, fsd, cur, localName, needLocation,
getStoragePolicyID(curPolicy,
parentStoragePolicy));
if (needLocation) {
// Once we hit lsLimit locations, stop.
// This helps to prevent excessively large response payloads.
// Approximate #locations with locatedBlockCount() * repl_factor
LocatedBlocks blks = ((HdfsLocatedFileStatus) listing[i]).getBlockLocations();
locationBudget -= (blks == null) ? 0 : blks.locatedBlockCount() * listing[i].getReplication();
}
++i;
++listingCnt;
}
// truncate return array if necessary
if (listingCnt < numOfListing) {
listing = Arrays.copyOf(listing, listingCnt);
}
return new DirectoryListing(
listing,
listingCnt < numOfListing ? 0 : children.size() - listingCnt);
}
}
/** Get the file info for a specific file.
* @param fsd FSDirectory
* @param src The string representation of the path to the file
* @param isRawPath true if a /.reserved/raw pathname was passed by the user
* @param includeStoragePolicy whether to include storage policy
* @return object containing information regarding the file
* or null if file not found
*/
static HdfsFileStatus getFileInfo(
FSDirectory fsd, String path, INodesInPath src, boolean isRawPath,
boolean includeStoragePolicy)
throws IOException {
throw new IllegalArgumentException("Unimplemented");
}
static HdfsFileStatus getFileInfo(
FSDirectory fsd, String src, boolean resolveLink, boolean isRawPath,
boolean includeStoragePolicy)
throws IOException {
throw new IllegalArgumentException("Unimplemented");
}
/**
* create an hdfs file status from an inode
*
* @param fsd FSDirectory
* @param node inode
* @param needLocation if block locations need to be included or not
* @return a file status
* @throws java.io.IOException if any error occurs
*/
private static HdfsFileStatus createFileStatus(
ROTransaction tx, FSDirectory fsd, FlatINode node,
byte[] localName, boolean needLocation, byte storagePolicy)
throws IOException {
if (needLocation) {
return createLocatedFileStatus(tx, fsd, node, localName, storagePolicy);
} else {
return createFileStatus(tx, fsd, node, localName, storagePolicy);
}
}
/**
* Create FileStatus by file INode
*/
static HdfsFileStatus createFileStatusForEditLog(
FSDirectory fsd, String fullPath, byte[] path, INode node,
byte storagePolicy, int snapshot, boolean isRawPath,
INodesInPath iip) throws IOException {
INodeAttributes nodeAttrs = getINodeAttributes(fsd, fullPath, path, node,
snapshot);
return createFileStatus(fsd, path, node, nodeAttrs,
storagePolicy, snapshot, isRawPath, iip);
}
/**
* Create FileStatus by file INode
*/
static HdfsFileStatus createFileStatus(
FSDirectory fsd, byte[] path, INode node,
INodeAttributes nodeAttrs, byte storagePolicy, int snapshot,
boolean isRawPath, INodesInPath iip) throws IOException {
long size = 0; // length is zero for directories
short replication = 0;
long blocksize = 0;
final boolean isEncrypted;
final FileEncryptionInfo feInfo = isRawPath ? null :
fsd.getFileEncryptionInfo(node, snapshot, iip);
if (node.isFile()) {
final INodeFile fileNode = node.asFile();
size = fileNode.computeFileSize(snapshot);
replication = fileNode.getFileReplication(snapshot);
blocksize = fileNode.getPreferredBlockSize();
isEncrypted = (feInfo != null) ||
(isRawPath && fsd.isInAnEZ(INodesInPath.fromINode(node)));
} else {
isEncrypted = fsd.isInAnEZ(INodesInPath.fromINode(node));
}
int childrenNum = node.isDirectory() ?
node.asDirectory().getChildrenNum(snapshot) : 0;
return new HdfsFileStatus(
size,
node.isDirectory(),
replication,
blocksize,
node.getModificationTime(snapshot),
node.getAccessTime(snapshot),
getPermissionForFileStatus(nodeAttrs, isEncrypted),
nodeAttrs.getUserName(),
nodeAttrs.getGroupName(),
node.isSymlink() ? node.asSymlink().getSymlink() : null,
path,
node.getId(),
childrenNum,
feInfo,
storagePolicy);
}
private static INodeAttributes getINodeAttributes(
FSDirectory fsd, String fullPath, byte[] path, INode node, int snapshot) {
return fsd.getAttributes(fullPath, path, node, snapshot);
}
/**
* Create FileStatus by file INode
*/
static HdfsFileStatus createFileStatus(
Transaction tx, FSDirectory fsd, FlatINode node,
byte[] localName, byte storagePolicy)
throws IOException {
long size = 0; // length is zero for directories
short replication = 0;
long blocksize = 0;
final boolean isEncrypted;
// TODO: Handle FileEncryptionInfo
final FileEncryptionInfo feInfo = null;
// final FileEncryptionInfo feInfo = isRawPath ? null :
// fsd.getFileEncryptionInfo(node, snapshot, iip);
if (node.isFile()) {
FlatINodeFileFeature f = node.feature(FlatINodeFileFeature.class);
size =
f.fileSize();
replication = f.replication();
blocksize = f.blockSize();
isEncrypted = false;
} else {
isEncrypted = false;
}
int childrenNum = 0;
if (node.isDirectory()) {
try(DBChildrenView children = tx.childrenView(node.id())) {
childrenNum = children.size();
}
}
PermissionStatus perm = node.permissionStatus(fsd.ugid());
// TODO:
// <ul>
// <li>If the INode has ACL / encryption, the function returns
// FsPermissionExtension instead of FsPermisson</li>
// <li>Handle symlink</li>
// </ul>
//
return new HdfsFileStatus(size,
node.isDirectory(), replication, blocksize,
node.mtime(),
node.atime(),
perm.getPermission(),
perm.getUserName(),
perm.getGroupName(),
null,
localName,
node.id(),
childrenNum,
feInfo,
storagePolicy);
}
/**
* Create FileStatus with location info by file INode
*/
private static HdfsLocatedFileStatus createLocatedFileStatus(
ROTransaction tx, FSDirectory fsd, FlatINode node, byte[] path,
byte storagePolicy) throws IOException {
assert fsd.hasReadLock();
long size = 0; // length is zero for directories
short replication = 0;
long blocksize = 0;
LocatedBlocks loc = null;
// TODO
final FileEncryptionInfo feInfo = null;
// final FileEncryptionInfo feInfo = isRawPath ? null :
// fsd.getFileEncryptionInfo(node, snapshot, iip);
if (node.isFile()) {
FlatINodeFileFeature file = node.feature(FlatINodeFileFeature.class);
size = file.fileSize();
replication = file.replication();
blocksize = file.blockSize();
final boolean isUc = file.inConstruction();
final long fileSize = file.inConstruction()
? size - file.lastBlock().getNumBytes()
: size;
loc = fsd.getBlockManager().createLocatedBlocks(
file.blocks(), 0L, size, fileSize, false);
// TODO: Snapsho
loc = attachFileInfo(loc, fileSize, isUc, false, feInfo);
}
int childrenNum = 0;
if (node.isDirectory()) {
try(DBChildrenView children = tx.childrenView(node.id())) {
childrenNum = children.size();
}
}
PermissionStatus perm = node.permissionStatus(fsd.ugid());
HdfsLocatedFileStatus status = new HdfsLocatedFileStatus(
size, node.isDirectory(), replication, blocksize, node.mtime(), node.atime(),
perm.getPermission(), perm.getUserName(), perm.getGroupName(),
null, path, node.id(), loc, childrenNum, feInfo, storagePolicy);
// Set caching information for the located blocks.
if (loc != null) {
CacheManager cacheManager = fsd.getFSNamesystem().getCacheManager();
for (LocatedBlock lb: loc.getLocatedBlocks()) {
cacheManager.setCachedLocations(lb);
}
}
return status;
}
/**
* Returns an inode's FsPermission for use in an outbound FileStatus. If the
* inode has an ACL or is for an encrypted file/dir, then this method will
* return an FsPermissionExtension.
*
* @param node INode to check
* @param isEncrypted boolean true if the file/dir is encrypted
* @return FsPermission from inode, with ACL bit on if the inode has an ACL
* and encrypted bit on if it represents an encrypted file/dir.
*/
private static FsPermission getPermissionForFileStatus(
INodeAttributes node, boolean isEncrypted) {
FsPermission perm = node.getFsPermission();
boolean hasAcl = node.getAclFeature() != null;
if (hasAcl || isEncrypted) {
perm = new FsPermissionExtension(perm, hasAcl, isEncrypted);
}
return perm;
}
private static ContentSummary getContentSummaryInt(FSDirectory fsd,
INodesInPath iip) throws IOException {
fsd.readLock();
try {
INode targetNode = iip.getLastINode();
if (targetNode == null) {
throw new FileNotFoundException("File does not exist: " + iip.getPath());
}
else {
// Make it relinquish locks everytime contentCountLimit entries are
// processed. 0 means disabled. I.e. blocking for the entire duration.
ContentSummaryComputationContext cscc =
new ContentSummaryComputationContext(fsd, fsd.getFSNamesystem(),
fsd.getContentCountLimit(), fsd.getContentSleepMicroSec());
ContentSummary cs = targetNode.computeAndConvertContentSummary(cscc);
fsd.addYieldCount(cscc.getYieldCount());
return cs;
}
} finally {
fsd.readUnlock();
}
}
static class GetBlockLocationsResult {
final boolean updateAccessTime;
final LocatedBlocks blocks;
boolean updateAccessTime() {
return updateAccessTime;
}
private GetBlockLocationsResult(
boolean updateAccessTime, LocatedBlocks blocks) {
this.updateAccessTime = updateAccessTime;
this.blocks = blocks;
}
}
static LocatedBlocks attachFileInfo(
LocatedBlocks from, long flength, boolean isUnderConstruction,
boolean inSnapshot, FileEncryptionInfo feInfo) {
return new LocatedBlocks(flength, isUnderConstruction,
from.getLocatedBlocks(),
from.getLastLocatedBlock(),
from.isLastBlockComplete() || inSnapshot,
feInfo);
}
}