HDFS-4148. Disallow write/modify operations on files and directories in a snapshot. Contributed by Brandon Li.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/HDFS-2802@1409023 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Suresh Srinivas 2012-11-13 23:26:40 +00:00
parent 2a9f450511
commit 2116d0520e
9 changed files with 399 additions and 70 deletions

View File

@ -67,3 +67,6 @@ Branch-2802 Snapshot (Unreleased)
HDFS-4177. Add a snapshot parameter to INodeDirectory.getChildrenList() for
selecting particular snapshot children list views. (szetszwo)
HDFS-4148. Disallow write/modify operations on files and directories in a
snapshot. (Brandon Li via suresh)

View File

@ -134,6 +134,7 @@ import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifie
import org.apache.hadoop.hdfs.server.common.HdfsServerConstants;
import org.apache.hadoop.hdfs.server.namenode.NameNode;
import org.apache.hadoop.hdfs.server.namenode.SafeModeException;
import org.apache.hadoop.hdfs.server.namenode.snapshot.SnapshotAccessControlException;
import org.apache.hadoop.io.DataOutputBuffer;
import org.apache.hadoop.io.EnumSetWritable;
import org.apache.hadoop.io.IOUtils;
@ -1311,7 +1312,8 @@ public class DFSClient implements java.io.Closeable {
ParentNotDirectoryException.class,
NSQuotaExceededException.class,
DSQuotaExceededException.class,
UnresolvedPathException.class);
UnresolvedPathException.class,
SnapshotAccessControlException.class);
}
}
@ -1342,7 +1344,8 @@ public class DFSClient implements java.io.Closeable {
SafeModeException.class,
DSQuotaExceededException.class,
UnsupportedOperationException.class,
UnresolvedPathException.class);
UnresolvedPathException.class,
SnapshotAccessControlException.class);
}
return DFSOutputStream.newStreamForAppend(this, src, buffersize, progress,
lastBlock, stat, dfsClientConf.createChecksum());
@ -1395,7 +1398,8 @@ public class DFSClient implements java.io.Closeable {
FileNotFoundException.class,
SafeModeException.class,
DSQuotaExceededException.class,
UnresolvedPathException.class);
UnresolvedPathException.class,
SnapshotAccessControlException.class);
}
}
@ -1413,7 +1417,8 @@ public class DFSClient implements java.io.Closeable {
throw re.unwrapRemoteException(AccessControlException.class,
NSQuotaExceededException.class,
DSQuotaExceededException.class,
UnresolvedPathException.class);
UnresolvedPathException.class,
SnapshotAccessControlException.class);
}
}
@ -1427,7 +1432,8 @@ public class DFSClient implements java.io.Closeable {
namenode.concat(trg, srcs);
} catch(RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
UnresolvedPathException.class);
UnresolvedPathException.class,
SnapshotAccessControlException.class);
}
}
/**
@ -1447,7 +1453,8 @@ public class DFSClient implements java.io.Closeable {
ParentNotDirectoryException.class,
SafeModeException.class,
NSQuotaExceededException.class,
UnresolvedPathException.class);
UnresolvedPathException.class,
SnapshotAccessControlException.class);
}
}
/**
@ -1475,7 +1482,8 @@ public class DFSClient implements java.io.Closeable {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class,
SafeModeException.class,
UnresolvedPathException.class);
UnresolvedPathException.class,
SnapshotAccessControlException.class);
}
}
@ -1797,7 +1805,8 @@ public class DFSClient implements java.io.Closeable {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class,
SafeModeException.class,
UnresolvedPathException.class);
UnresolvedPathException.class,
SnapshotAccessControlException.class);
}
}
@ -1818,7 +1827,8 @@ public class DFSClient implements java.io.Closeable {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class,
SafeModeException.class,
UnresolvedPathException.class);
UnresolvedPathException.class,
SnapshotAccessControlException.class);
}
}
@ -2049,7 +2059,8 @@ public class DFSClient implements java.io.Closeable {
SafeModeException.class,
NSQuotaExceededException.class,
DSQuotaExceededException.class,
UnresolvedPathException.class);
UnresolvedPathException.class,
SnapshotAccessControlException.class);
}
}
@ -2092,7 +2103,8 @@ public class DFSClient implements java.io.Closeable {
FileNotFoundException.class,
NSQuotaExceededException.class,
DSQuotaExceededException.class,
UnresolvedPathException.class);
UnresolvedPathException.class,
SnapshotAccessControlException.class);
}
}
@ -2108,7 +2120,8 @@ public class DFSClient implements java.io.Closeable {
} catch(RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class,
UnresolvedPathException.class);
UnresolvedPathException.class,
SnapshotAccessControlException.class);
}
}

View File

@ -69,6 +69,7 @@ import org.apache.hadoop.hdfs.security.token.block.BlockTokenIdentifier;
import org.apache.hadoop.hdfs.security.token.block.InvalidBlockTokenException;
import org.apache.hadoop.hdfs.server.namenode.NotReplicatedYetException;
import org.apache.hadoop.hdfs.server.namenode.SafeModeException;
import org.apache.hadoop.hdfs.server.namenode.snapshot.SnapshotAccessControlException;
import org.apache.hadoop.io.EnumSetWritable;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.ipc.RemoteException;
@ -1301,7 +1302,8 @@ public class DFSOutputStream extends FSOutputSummer implements Syncable {
ParentNotDirectoryException.class,
NSQuotaExceededException.class,
SafeModeException.class,
UnresolvedPathException.class);
UnresolvedPathException.class,
SnapshotAccessControlException.class);
}
streamer = new DataStreamer();
}

View File

@ -35,6 +35,7 @@ import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.server.namenode.NotReplicatedYetException;
import org.apache.hadoop.hdfs.server.namenode.SafeModeException;
import org.apache.hadoop.hdfs.server.namenode.snapshot.SnapshotAccessControlException;
import org.apache.hadoop.io.EnumSetWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.retry.Idempotent;
@ -163,6 +164,7 @@ public interface ClientProtocol {
* quota restriction
* @throws SafeModeException create not allowed in safemode
* @throws UnresolvedLinkException If <code>src</code> contains a symlink
* @throws SnapshotAccessControlException if path is in RO snapshot
* @throws IOException If an I/O error occurred
*
* RuntimeExceptions:
@ -174,7 +176,7 @@ public interface ClientProtocol {
AlreadyBeingCreatedException, DSQuotaExceededException,
FileAlreadyExistsException, FileNotFoundException,
NSQuotaExceededException, ParentNotDirectoryException, SafeModeException,
UnresolvedLinkException, IOException;
UnresolvedLinkException, SnapshotAccessControlException, IOException;
/**
* Append to the end of the file.
@ -194,6 +196,7 @@ public interface ClientProtocol {
* restriction
* @throws SafeModeException append not allowed in safemode
* @throws UnresolvedLinkException If <code>src</code> contains a symlink
* @throws SnapshotAccessControlException if path is in RO snapshot
* @throws IOException If an I/O error occurred.
*
* RuntimeExceptions:
@ -202,7 +205,7 @@ public interface ClientProtocol {
public LocatedBlock append(String src, String clientName)
throws AccessControlException, DSQuotaExceededException,
FileNotFoundException, SafeModeException, UnresolvedLinkException,
IOException;
SnapshotAccessControlException, IOException;
/**
* Set replication for an existing file.
@ -224,13 +227,14 @@ public interface ClientProtocol {
* @throws FileNotFoundException If file <code>src</code> is not found
* @throws SafeModeException not allowed in safemode
* @throws UnresolvedLinkException if <code>src</code> contains a symlink
* @throws SnapshotAccessControlException if path is in RO snapshot
* @throws IOException If an I/O error occurred
*/
@Idempotent
public boolean setReplication(String src, short replication)
throws AccessControlException, DSQuotaExceededException,
FileNotFoundException, SafeModeException, UnresolvedLinkException,
IOException;
SnapshotAccessControlException, IOException;
/**
* Set permissions for an existing file/directory.
@ -239,12 +243,13 @@ public interface ClientProtocol {
* @throws FileNotFoundException If file <code>src</code> is not found
* @throws SafeModeException not allowed in safemode
* @throws UnresolvedLinkException If <code>src</code> contains a symlink
* @throws SnapshotAccessControlException if path is in RO snapshot
* @throws IOException If an I/O error occurred
*/
@Idempotent
public void setPermission(String src, FsPermission permission)
throws AccessControlException, FileNotFoundException, SafeModeException,
UnresolvedLinkException, IOException;
UnresolvedLinkException, SnapshotAccessControlException, IOException;
/**
* Set Owner of a path (i.e. a file or a directory).
@ -257,12 +262,13 @@ public interface ClientProtocol {
* @throws FileNotFoundException If file <code>src</code> is not found
* @throws SafeModeException not allowed in safemode
* @throws UnresolvedLinkException If <code>src</code> contains a symlink
* @throws SnapshotAccessControlException if path is in RO snapshot
* @throws IOException If an I/O error occurred
*/
@Idempotent
public void setOwner(String src, String username, String groupname)
throws AccessControlException, FileNotFoundException, SafeModeException,
UnresolvedLinkException, IOException;
UnresolvedLinkException, SnapshotAccessControlException, IOException;
/**
* The client can give up on a block by calling abandonBlock().
@ -385,10 +391,11 @@ public interface ClientProtocol {
* @return true if successful, or false if the old name does not exist
* or if the new name already belongs to the namespace.
*
* @throws SnapshotAccessControlException if path is in RO snapshot
* @throws IOException an I/O error occurred
*/
public boolean rename(String src, String dst)
throws UnresolvedLinkException, IOException;
throws UnresolvedLinkException, SnapshotAccessControlException, IOException;
/**
* Moves blocks from srcs to trg and delete srcs
@ -398,9 +405,10 @@ public interface ClientProtocol {
* @throws IOException if some arguments are invalid
* @throws UnresolvedLinkException if <code>trg</code> or <code>srcs</code>
* contains a symlink
* @throws SnapshotAccessControlException if path is in RO snapshot
*/
public void concat(String trg, String[] srcs)
throws IOException, UnresolvedLinkException;
throws IOException, UnresolvedLinkException, SnapshotAccessControlException;
/**
* Rename src to dst.
@ -434,13 +442,14 @@ public interface ClientProtocol {
* @throws SafeModeException rename not allowed in safemode
* @throws UnresolvedLinkException If <code>src</code> or
* <code>dst</code> contains a symlink
* @throws SnapshotAccessControlException if path is in RO snapshot
* @throws IOException If an I/O error occurred
*/
public void rename2(String src, String dst, Options.Rename... options)
throws AccessControlException, DSQuotaExceededException,
FileAlreadyExistsException, FileNotFoundException,
NSQuotaExceededException, ParentNotDirectoryException, SafeModeException,
UnresolvedLinkException, IOException;
UnresolvedLinkException, SnapshotAccessControlException, IOException;
/**
* Delete the given file or directory from the file system.
@ -457,11 +466,12 @@ public interface ClientProtocol {
* @throws FileNotFoundException If file <code>src</code> is not found
* @throws SafeModeException create not allowed in safemode
* @throws UnresolvedLinkException If <code>src</code> contains a symlink
* @throws SnapshotAccessControlException if path is in RO snapshot
* @throws IOException If an I/O error occurred
*/
public boolean delete(String src, boolean recursive)
throws AccessControlException, FileNotFoundException, SafeModeException,
UnresolvedLinkException, IOException;
UnresolvedLinkException, SnapshotAccessControlException, IOException;
/**
* Create a directory (or hierarchy of directories) with the given
@ -482,6 +492,7 @@ public interface ClientProtocol {
* is not a directory
* @throws SafeModeException create not allowed in safemode
* @throws UnresolvedLinkException If <code>src</code> contains a symlink
* @throws SnapshotAccessControlException if path is in RO snapshot
* @throws IOException If an I/O error occurred.
*
* RunTimeExceptions:
@ -492,7 +503,7 @@ public interface ClientProtocol {
throws AccessControlException, FileAlreadyExistsException,
FileNotFoundException, NSQuotaExceededException,
ParentNotDirectoryException, SafeModeException, UnresolvedLinkException,
IOException;
SnapshotAccessControlException, IOException;
/**
* Get a partial listing of the indicated directory
@ -800,12 +811,13 @@ public interface ClientProtocol {
* @throws QuotaExceededException if the directory size
* is greater than the given quota
* @throws UnresolvedLinkException if the <code>path</code> contains a symlink.
* @throws SnapshotAccessControlException if path is in RO snapshot
* @throws IOException If an I/O error occurred
*/
@Idempotent
public void setQuota(String path, long namespaceQuota, long diskspaceQuota)
throws AccessControlException, FileNotFoundException,
UnresolvedLinkException, IOException;
UnresolvedLinkException, SnapshotAccessControlException, IOException;
/**
* Write all metadata for this file into persistent storage.
@ -836,12 +848,13 @@ public interface ClientProtocol {
* @throws AccessControlException permission denied
* @throws FileNotFoundException file <code>src</code> is not found
* @throws UnresolvedLinkException if <code>src</code> contains a symlink.
* @throws SnapshotAccessControlException if path is in RO snapshot
* @throws IOException If an I/O error occurred
*/
@Idempotent
public void setTimes(String src, long mtime, long atime)
throws AccessControlException, FileNotFoundException,
UnresolvedLinkException, IOException;
UnresolvedLinkException, SnapshotAccessControlException, IOException;
/**
* Create symlink to a file or directory.
@ -859,13 +872,14 @@ public interface ClientProtocol {
* @throws ParentNotDirectoryException If parent of <code>link</code> is not a
* directory.
* @throws UnresolvedLinkException if <code>link</target> contains a symlink.
* @throws SnapshotAccessControlException if path is in RO snapshot
* @throws IOException If an I/O error occurred
*/
public void createSymlink(String target, String link, FsPermission dirPerm,
boolean createParent) throws AccessControlException,
FileAlreadyExistsException, FileNotFoundException,
ParentNotDirectoryException, SafeModeException, UnresolvedLinkException,
IOException;
SnapshotAccessControlException, IOException;
/**
* Return the target of the given symlink. If there is an intermediate

View File

@ -59,6 +59,7 @@ import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.BlockUCState;
import org.apache.hadoop.hdfs.server.namenode.INode.BlocksMapUpdateInfo;
import org.apache.hadoop.hdfs.server.namenode.INodeDirectory.INodesInPath;
import org.apache.hadoop.hdfs.server.namenode.snapshot.INodeDirectorySnapshottable;
import org.apache.hadoop.hdfs.server.namenode.snapshot.SnapshotAccessControlException;
import org.apache.hadoop.hdfs.server.namenode.snapshot.Snapshot;
import org.apache.hadoop.hdfs.util.ByteArray;
import org.apache.hadoop.hdfs.util.ReadOnlyList;
@ -220,6 +221,7 @@ public class FSDirectory implements Closeable {
* @throws FileAlreadyExistsException
* @throws QuotaExceededException
* @throws UnresolvedLinkException
* @throws SnapshotAccessControlException
*/
INodeFileUnderConstruction addFile(String path,
PermissionStatus permissions,
@ -230,7 +232,7 @@ public class FSDirectory implements Closeable {
DatanodeDescriptor clientNode,
long generationStamp)
throws FileAlreadyExistsException, QuotaExceededException,
UnresolvedLinkException {
UnresolvedLinkException, SnapshotAccessControlException {
waitForReady();
// Always do an implicit mkdirs for parent directory tree.
@ -447,13 +449,14 @@ public class FSDirectory implements Closeable {
}
/**
* @throws SnapshotAccessControlException
* @see #unprotectedRenameTo(String, String, long)
* @deprecated Use {@link #renameTo(String, String, Rename...)} instead.
*/
@Deprecated
boolean renameTo(String src, String dst)
throws QuotaExceededException, UnresolvedLinkException,
FileAlreadyExistsException {
FileAlreadyExistsException, SnapshotAccessControlException {
if (NameNode.stateChangeLog.isDebugEnabled()) {
NameNode.stateChangeLog.debug("DIR* FSDirectory.renameTo: "
+src+" to "+dst);
@ -503,14 +506,15 @@ public class FSDirectory implements Closeable {
* @return true if rename succeeds; false otherwise
* @throws QuotaExceededException if the operation violates any quota limit
* @throws FileAlreadyExistsException if the src is a symlink that points to dst
* @throws SnapshotAccessControlException if path is in RO snapshot
* @deprecated See {@link #renameTo(String, String)}
*/
@Deprecated
boolean unprotectedRenameTo(String src, String dst, long timestamp)
throws QuotaExceededException, UnresolvedLinkException,
FileAlreadyExistsException {
FileAlreadyExistsException, SnapshotAccessControlException {
assert hasWriteLock();
INodesInPath srcInodesInPath = rootDir.getExistingPathINodes(src, false);
INodesInPath srcInodesInPath = rootDir.getMutableINodesInPath(src, false);
INode[] srcInodes = srcInodesInPath.getINodes();
INode srcInode = srcInodes[srcInodes.length-1];
@ -552,6 +556,10 @@ public class FSDirectory implements Closeable {
byte[][] dstComponents = INode.getPathComponents(dst);
INodesInPath dstInodesInPath = rootDir.getExistingPathINodes(dstComponents,
dstComponents.length, false);
if (dstInodesInPath.isSnapshot()) {
throw new SnapshotAccessControlException(
"Modification on RO snapshot is disallowed");
}
INode[] dstInodes = dstInodesInPath.getINodes();
if (dstInodes[dstInodes.length-1] != null) {
NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: "
@ -635,7 +643,8 @@ public class FSDirectory implements Closeable {
}
}
String error = null;
final INodesInPath srcInodesInPath = rootDir.getExistingPathINodes(src, false);
final INodesInPath srcInodesInPath = rootDir.getMutableINodesInPath(src,
false);
final INode[] srcInodes = srcInodesInPath.getINodes();
final INode srcInode = srcInodes[srcInodes.length - 1];
// validate source
@ -672,8 +681,8 @@ public class FSDirectory implements Closeable {
throw new IOException(error);
}
final byte[][] dstComponents = INode.getPathComponents(dst);
INodesInPath dstInodesInPath = rootDir.getExistingPathINodes(dstComponents,
dstComponents.length, false);
final INodesInPath dstInodesInPath = rootDir.getMutableINodesInPath(
dstComponents, false);
final INode[] dstInodes = dstInodesInPath.getINodes();
INode dstInode = dstInodes[dstInodes.length - 1];
if (dstInodes.length == 1) {
@ -804,9 +813,11 @@ public class FSDirectory implements Closeable {
* @param oldReplication old replication - output parameter
* @return array of file blocks
* @throws QuotaExceededException
* @throws SnapshotAccessControlException
*/
Block[] setReplication(String src, short replication, short[] oldReplication)
throws QuotaExceededException, UnresolvedLinkException {
throws QuotaExceededException, UnresolvedLinkException,
SnapshotAccessControlException {
waitForReady();
Block[] fileBlocks = null;
writeLock();
@ -820,14 +831,12 @@ public class FSDirectory implements Closeable {
}
}
Block[] unprotectedSetReplication(String src,
short replication,
short[] oldReplication
) throws QuotaExceededException,
UnresolvedLinkException {
Block[] unprotectedSetReplication(String src, short replication,
short[] oldReplication) throws QuotaExceededException,
UnresolvedLinkException, SnapshotAccessControlException {
assert hasWriteLock();
final INodesInPath inodesInPath = rootDir.getExistingPathINodes(src, true);
final INodesInPath inodesInPath = rootDir.getMutableINodesInPath(src, true);
final INode[] inodes = inodesInPath.getINodes();
INode inode = inodes[inodes.length - 1];
if (inode == null) {
@ -890,8 +899,26 @@ public class FSDirectory implements Closeable {
}
}
boolean existsMutable(String src) throws UnresolvedLinkException,
SnapshotAccessControlException {
src = normalizePath(src);
readLock();
try {
INode inode = rootDir.getMutableNode(src, false);
if (inode == null) {
return false;
}
return inode.isDirectory() || inode.isSymlink()
? true
: ((INodeFile)inode).getBlocks() != null;
} finally {
readUnlock();
}
}
void setPermission(String src, FsPermission permission)
throws FileNotFoundException, UnresolvedLinkException {
throws FileNotFoundException, UnresolvedLinkException,
SnapshotAccessControlException {
writeLock();
try {
unprotectedSetPermission(src, permission);
@ -900,11 +927,12 @@ public class FSDirectory implements Closeable {
}
fsImage.getEditLog().logSetPermissions(src, permission);
}
void unprotectedSetPermission(String src, FsPermission permissions)
throws FileNotFoundException, UnresolvedLinkException {
void unprotectedSetPermission(String src, FsPermission permissions)
throws FileNotFoundException, UnresolvedLinkException,
SnapshotAccessControlException {
assert hasWriteLock();
INode inode = rootDir.getNode(src, true);
INode inode = rootDir.getMutableNode(src, true);
if (inode == null) {
throw new FileNotFoundException("File does not exist: " + src);
}
@ -912,7 +940,8 @@ public class FSDirectory implements Closeable {
}
void setOwner(String src, String username, String groupname)
throws FileNotFoundException, UnresolvedLinkException {
throws FileNotFoundException, UnresolvedLinkException,
SnapshotAccessControlException {
writeLock();
try {
unprotectedSetOwner(src, username, groupname);
@ -922,10 +951,11 @@ public class FSDirectory implements Closeable {
fsImage.getEditLog().logSetOwner(src, username, groupname);
}
void unprotectedSetOwner(String src, String username, String groupname)
throws FileNotFoundException, UnresolvedLinkException {
void unprotectedSetOwner(String src, String username, String groupname)
throws FileNotFoundException, UnresolvedLinkException,
SnapshotAccessControlException {
assert hasWriteLock();
INode inode = rootDir.getNode(src, true);
INode inode = rootDir.getMutableNode(src, true);
if (inode == null) {
throw new FileNotFoundException("File does not exist: " + src);
}
@ -1020,7 +1050,7 @@ public class FSDirectory implements Closeable {
int filesRemoved;
writeLock();
try {
final INodesInPath inodesInPath = rootDir.getExistingPathINodes(
final INodesInPath inodesInPath = rootDir.getMutableINodesInPath(
normalizePath(src), false);
final INode[] inodes = inodesInPath.getINodes();
if (checkPathINodes(inodes, src) == 0) {
@ -1095,14 +1125,15 @@ public class FSDirectory implements Closeable {
* <br>
* @param src a string representation of a path to an inode
* @param mtime the time the inode is removed
* @throws SnapshotAccessControlException if path is in RO snapshot
*/
void unprotectedDelete(String src, long mtime)
throws UnresolvedLinkException {
throws UnresolvedLinkException, SnapshotAccessControlException {
assert hasWriteLock();
BlocksMapUpdateInfo collectedBlocks = new BlocksMapUpdateInfo();
int filesRemoved = 0;
final INodesInPath inodesInPath = rootDir.getExistingPathINodes(
final INodesInPath inodesInPath = rootDir.getMutableINodesInPath(
normalizePath(src), false);
final INode[] inodes = inodesInPath.getINodes();
if (checkPathINodes(inodes, src) == 0) {
@ -1324,6 +1355,20 @@ public class FSDirectory implements Closeable {
}
}
/**
* Get {@link INode} associated with the file / directory.
* @throws SnapshotAccessControlException if path is in RO snapshot
*/
public INode getMutableINode(String src) throws UnresolvedLinkException,
SnapshotAccessControlException {
readLock();
try {
return rootDir.getMutableNode(src, true);
} finally {
readUnlock();
}
}
/**
* Get the parent node of path.
*
@ -1342,14 +1387,15 @@ public class FSDirectory implements Closeable {
/**
* Check whether the filepath could be created
* @throws SnapshotAccessControlException if path is in RO snapshot
*/
boolean isValidToCreate(String src) throws UnresolvedLinkException {
boolean isValidToCreate(String src) throws UnresolvedLinkException,
SnapshotAccessControlException {
String srcs = normalizePath(src);
readLock();
try {
if (srcs.startsWith("/") &&
!srcs.endsWith("/") &&
rootDir.getNode(srcs, false) == null) {
if (srcs.startsWith("/") && !srcs.endsWith("/")
&& rootDir.getMutableNode(srcs, false) == null) {
return true;
} else {
return false;
@ -1372,6 +1418,22 @@ public class FSDirectory implements Closeable {
readUnlock();
}
}
/**
* Check whether the path specifies a directory
* @throws SnapshotAccessControlException if path is in RO snapshot
*/
boolean isDirMutable(String src) throws UnresolvedLinkException,
SnapshotAccessControlException {
src = normalizePath(src);
readLock();
try {
INode node = rootDir.getMutableNode(src, false);
return node != null && node.isDirectory();
} finally {
readUnlock();
}
}
/** Updates namespace and diskspace consumed for all
* directories until the parent directory of file represented by path.
@ -1510,11 +1572,12 @@ public class FSDirectory implements Closeable {
* @throws QuotaExceededException if directory creation violates
* any quota limit
* @throws UnresolvedLinkException if a symlink is encountered in src.
* @throws SnapshotAccessControlException if path is in RO snapshot
*/
boolean mkdirs(String src, PermissionStatus permissions,
boolean inheritPermission, long now)
throws FileAlreadyExistsException, QuotaExceededException,
UnresolvedLinkException {
UnresolvedLinkException, SnapshotAccessControlException {
src = normalizePath(src);
String[] names = INode.getPathNames(src);
byte[][] components = INode.getPathComponents(names);
@ -1524,6 +1587,10 @@ public class FSDirectory implements Closeable {
try {
INodesInPath inodesInPath = rootDir.getExistingPathINodes(components,
components.length, false);
if (inodesInPath.isSnapshot()) {
throw new SnapshotAccessControlException(
"Modification on RO snapshot is disallowed");
}
INode[] inodes = inodesInPath.getINodes();
// find the index of the first null in inodes[]
@ -1935,10 +2002,11 @@ public class FSDirectory implements Closeable {
* @throws QuotaExceededException if the directory tree size is
* greater than the given quota
* @throws UnresolvedLinkException if a symlink is encountered in src.
* @throws SnapshotAccessControlException if path is in RO snapshot
*/
INodeDirectory unprotectedSetQuota(String src, long nsQuota, long dsQuota)
throws FileNotFoundException, QuotaExceededException,
UnresolvedLinkException {
throws FileNotFoundException, QuotaExceededException,
UnresolvedLinkException, SnapshotAccessControlException {
assert hasWriteLock();
// sanity check
if ((nsQuota < 0 && nsQuota != HdfsConstants.QUOTA_DONT_SET &&
@ -1951,9 +2019,8 @@ public class FSDirectory implements Closeable {
}
String srcs = normalizePath(src);
final INodesInPath inodesInPath = rootDir.getExistingPathINodes(src, true);
final INode[] inodes = inodesInPath.getINodes();
final INode[] inodes = rootDir.getMutableINodesInPath(srcs, true)
.getINodes();
INode targetNode = inodes[inodes.length-1];
if (targetNode == null) {
throw new FileNotFoundException("Directory does not exist: " + srcs);
@ -1998,11 +2065,12 @@ public class FSDirectory implements Closeable {
/**
* See {@link ClientProtocol#setQuota(String, long, long)} for the
* contract.
* @throws SnapshotAccessControlException if path is in RO snapshot
* @see #unprotectedSetQuota(String, long, long)
*/
void setQuota(String src, long nsQuota, long dsQuota)
throws FileNotFoundException, QuotaExceededException,
UnresolvedLinkException {
void setQuota(String src, long nsQuota, long dsQuota)
throws FileNotFoundException, QuotaExceededException,
UnresolvedLinkException, SnapshotAccessControlException {
writeLock();
try {
INodeDirectory dir = unprotectedSetQuota(src, nsQuota, dsQuota);

View File

@ -1396,7 +1396,8 @@ public class FSNamesystem implements Namesystem, FSClusterStats,
// replication and blocks sizes should be the same for ALL the blocks
// check the target
final INodeFile trgInode = INodeFile.valueOf(dir.getINode(target), target);
final INodeFile trgInode = INodeFile.valueOf(dir.getMutableINode(target),
target);
if(trgInode.isUnderConstruction()) {
throw new HadoopIllegalArgumentException("concat: target file "
+ target + " is under construction");
@ -1431,7 +1432,7 @@ public class FSNamesystem implements Namesystem, FSClusterStats,
if(i==srcs.length-1)
endSrc=true;
final INodeFile srcInode = INodeFile.valueOf(dir.getINode(src), src);
final INodeFile srcInode = INodeFile.valueOf(dir.getMutableINode(src), src);
if(src.isEmpty()
|| srcInode.isUnderConstruction()
|| srcInode.numBlocks() == 0) {
@ -1514,7 +1515,7 @@ public class FSNamesystem implements Namesystem, FSClusterStats,
if (isPermissionEnabled) {
checkPathAccess(src, FsAction.WRITE);
}
INode inode = dir.getINode(src);
INode inode = dir.getMutableINode(src);
if (inode != null) {
dir.setTimes(src, inode, mtime, atime, true);
if (auditLog.isInfoEnabled() && isExternalInvocation()) {
@ -1787,7 +1788,7 @@ public class FSNamesystem implements Namesystem, FSClusterStats,
}
// Verify that the destination does not exist as a directory already.
boolean pathExists = dir.exists(src);
boolean pathExists = dir.existsMutable(src);
if (pathExists && dir.isDir(src)) {
throw new FileAlreadyExistsException("Cannot create file " + src
+ "; already exists as a directory.");
@ -2929,7 +2930,7 @@ public class FSNamesystem implements Namesystem, FSClusterStats,
if (isPermissionEnabled) {
checkTraverse(src);
}
if (dir.isDir(src)) {
if (dir.isDirMutable(src)) {
// all the users of mkdirs() are used to expect 'true' even if
// a new directory is not created.
return true;

View File

@ -33,6 +33,7 @@ import org.apache.hadoop.hdfs.protocol.UnresolvedPathException;
import org.apache.hadoop.hdfs.server.namenode.snapshot.INodeDirectorySnapshottable;
import org.apache.hadoop.hdfs.server.namenode.snapshot.INodeDirectoryWithSnapshot;
import org.apache.hadoop.hdfs.server.namenode.snapshot.Snapshot;
import org.apache.hadoop.hdfs.server.namenode.snapshot.SnapshotAccessControlException;
import org.apache.hadoop.hdfs.util.ReadOnlyList;
import com.google.common.annotations.VisibleForTesting;
@ -136,6 +137,44 @@ public class INodeDirectory extends INode {
return getINodesInPath(path, resolveLink).getINode(0);
}
/**
* @return the INode of the last component in src, or null if the last
* component does not exist.
* @throws UnresolvedLinkException if symlink can't be resolved
* @throws SnapshotAccessControlException if path is in RO snapshot
*/
INode getMutableNode(String src, boolean resolveLink)
throws UnresolvedLinkException, SnapshotAccessControlException {
INode[] inodes = getMutableINodesInPath(src, resolveLink).getINodes();
return inodes[inodes.length - 1];
}
/**
* @return the INodesInPath of the components in src
* @throws UnresolvedLinkException if symlink can't be resolved
* @throws SnapshotAccessControlException if path is in RO snapshot
*/
INodesInPath getMutableINodesInPath(String src, boolean resolveLink)
throws UnresolvedLinkException, SnapshotAccessControlException {
return getMutableINodesInPath(INode.getPathComponents(src), resolveLink);
}
/**
* @return the INodesInPath of the components in src
* @throws UnresolvedLinkException if symlink can't be resolved
* @throws SnapshotAccessControlException if path is in RO snapshot
*/
INodesInPath getMutableINodesInPath(byte[][] components, boolean resolveLink)
throws UnresolvedLinkException, SnapshotAccessControlException {
INodesInPath inodesInPath = getExistingPathINodes(components,
components.length, resolveLink);
if (inodesInPath.isSnapshot()) {
throw new SnapshotAccessControlException(
"Modification on RO snapshot is disallowed");
}
return inodesInPath;
}
/**
* Retrieve existing INodes from a path. If existing is big enough to store
* all path components (existing and non-existing), then existing INodes

View File

@ -0,0 +1,33 @@
/**
* 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.snapshot;
import org.apache.hadoop.security.AccessControlException;
/** Snapshot access related exception. */
public class SnapshotAccessControlException extends AccessControlException {
private static final long serialVersionUID = 1L;
public SnapshotAccessControlException(final String message) {
super(message);
}
public SnapshotAccessControlException(final Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,156 @@
/**
* 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.snapshot;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Options;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hdfs.DFSClient;
import org.apache.hadoop.hdfs.DistributedFileSystem;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.server.namenode.FSNamesystem;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* This class tests snapshot functionality. One or multiple snapshots are
* created. The snapshotted directory is changed and verification is done to
* ensure snapshots remain unchanges.
*/
public class TestDisallowModifyROSnapshot {
private final static Path dir = new Path("/TestSnapshot");
private final static Path sub1 = new Path(dir, "sub1");
private final static Path sub2 = new Path(dir, "sub2");
protected static Configuration conf;
protected static MiniDFSCluster cluster;
protected static FSNamesystem fsn;
protected static DistributedFileSystem fs;
/**
* The list recording all previous snapshots. Each element in the array
* records a snapshot root.
*/
protected static ArrayList<Path> snapshotList = new ArrayList<Path>();
static Path objInSnapshot = null;
@BeforeClass
public static void setUp() throws Exception {
conf = new Configuration();
cluster = new MiniDFSCluster.Builder(conf).numDataNodes(1).build();
cluster.waitActive();
fsn = cluster.getNamesystem();
fs = cluster.getFileSystem();
Path path1 = new Path(sub1, "dir1");
assertTrue(fs.mkdirs(path1));
Path path2 = new Path(sub2, "dir2");
assertTrue(fs.mkdirs(path2));
SnapshotTestHelper.createSnapshot(fs, sub1, "testSnapshot");
objInSnapshot = SnapshotTestHelper.getSnapshotPath(sub1, "testSnapshot",
"dir1");
}
@AfterClass
public static void tearDown() throws Exception {
if (cluster != null) {
cluster.shutdown();
}
}
@Test(expected = SnapshotAccessControlException.class)
public void testSetReplication() throws Exception {
fs.setReplication(objInSnapshot, (short) 1);
}
@Test(expected = SnapshotAccessControlException.class)
public void testSetPermission() throws Exception {
fs.setPermission(objInSnapshot, new FsPermission("777"));
}
@Test(expected = SnapshotAccessControlException.class)
public void testSetOwner() throws Exception {
fs.setOwner(objInSnapshot, "username", "groupname");
}
@Test
public void testRename() throws Exception {
try {
fs.rename(objInSnapshot, new Path("/invalid/path"));
fail("Didn't throw SnapshotAccessControlException");
} catch (SnapshotAccessControlException e) { /* Ignored */ }
try {
fs.rename(sub2, objInSnapshot);
fail("Didn't throw SnapshotAccessControlException");
} catch (SnapshotAccessControlException e) { /* Ignored */ }
try {
fs.rename(sub2, objInSnapshot, (Options.Rename) null);
fail("Didn't throw SnapshotAccessControlException");
} catch (SnapshotAccessControlException e) { /* Ignored */ }
}
@Test(expected = SnapshotAccessControlException.class)
public void testDelete() throws Exception {
fs.delete(objInSnapshot, true);
}
@Test(expected = SnapshotAccessControlException.class)
public void testQuota() throws Exception {
fs.setQuota(objInSnapshot, 100, 100);
}
@Test(expected = SnapshotAccessControlException.class)
public void testSetTime() throws Exception {
fs.setTimes(objInSnapshot, 100, 100);
}
@Test(expected = SnapshotAccessControlException.class)
public void testCreate() throws Exception {
@SuppressWarnings("deprecation")
DFSClient dfsclient = new DFSClient(conf);
dfsclient.create(objInSnapshot.toString(), true);
}
@Test(expected = SnapshotAccessControlException.class)
public void testAppend() throws Exception {
fs.append(objInSnapshot, 65535, null);
}
@Test(expected = SnapshotAccessControlException.class)
public void testMkdir() throws Exception {
fs.mkdirs(objInSnapshot, new FsPermission("777"));
}
@Test(expected = SnapshotAccessControlException.class)
public void testCreateSymlink() throws Exception {
@SuppressWarnings("deprecation")
DFSClient dfsclient = new DFSClient(conf);
// TODO: if link is objInSnapshot, ParentNotDirectoryException got thrown
// first by verifyParentDir()
dfsclient.createSymlink(sub2.toString(), "/TestSnapshot/sub1/.snapshot",
false);
}
}