HADOOP-6703. Prevent renaming a file, directory or symbolic link to itself. Contributed by Eli Collins.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@938788 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
b212ed99c5
commit
e333072144
@ -361,6 +361,9 @@ Trunk (unreleased changes)
|
|||||||
HADOOP-6690. FilterFileSystem correctly handles setTimes call.
|
HADOOP-6690. FilterFileSystem correctly handles setTimes call.
|
||||||
(Rodrigo Schmidt via dhruba)
|
(Rodrigo Schmidt via dhruba)
|
||||||
|
|
||||||
|
HADOOP-6703. Prevent renaming a file, directory or symbolic link to
|
||||||
|
itself. (Eli Collins via suresh)
|
||||||
|
|
||||||
HADOOP-6710. Symbolic umask for file creation is not conformant with posix.
|
HADOOP-6710. Symbolic umask for file creation is not conformant with posix.
|
||||||
(suresh)
|
(suresh)
|
||||||
|
|
||||||
|
@ -603,6 +603,14 @@ public abstract class AbstractFileSystem {
|
|||||||
dstStatus = null;
|
dstStatus = null;
|
||||||
}
|
}
|
||||||
if (dstStatus != null) {
|
if (dstStatus != null) {
|
||||||
|
if (dst.equals(src)) {
|
||||||
|
throw new FileAlreadyExistsException(
|
||||||
|
"The source "+src+" and destination "+dst+" are the same");
|
||||||
|
}
|
||||||
|
if (srcStatus.isSymlink() && dst.equals(srcStatus.getSymlink())) {
|
||||||
|
throw new FileAlreadyExistsException(
|
||||||
|
"Cannot rename symlink "+src+" to its target "+dst);
|
||||||
|
}
|
||||||
if (srcStatus.isDir() != dstStatus.isDir()) {
|
if (srcStatus.isDir() != dstStatus.isDir()) {
|
||||||
throw new IOException("Source " + src + " Destination " + dst
|
throw new IOException("Source " + src + " Destination " + dst
|
||||||
+ " both should be either file or directory");
|
+ " both should be either file or directory");
|
||||||
|
@ -824,6 +824,26 @@ public abstract class FileContextMainOperationsBaseTest {
|
|||||||
rename(src, dst, true, false, true, Rename.OVERWRITE);
|
rename(src, dst, true, false, true, Rename.OVERWRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRenameFileToItself() throws Exception {
|
||||||
|
if (!renameSupported()) return;
|
||||||
|
Path src = getTestRootPath(fc, "test/hadoop/file");
|
||||||
|
createFile(src);
|
||||||
|
try {
|
||||||
|
rename(src, src, false, true, false, Rename.NONE);
|
||||||
|
Assert.fail("Renamed file to itself");
|
||||||
|
} catch (IOException e) {
|
||||||
|
Assert.assertTrue(unwrapException(e) instanceof FileAlreadyExistsException);
|
||||||
|
}
|
||||||
|
// Also fails with overwrite
|
||||||
|
try {
|
||||||
|
rename(src, src, false, true, false, Rename.OVERWRITE);
|
||||||
|
Assert.fail("Renamed file to itself");
|
||||||
|
} catch (IOException e) {
|
||||||
|
Assert.assertTrue(unwrapException(e) instanceof FileAlreadyExistsException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRenameFileAsExistingFile() throws Exception {
|
public void testRenameFileAsExistingFile() throws Exception {
|
||||||
if (!renameSupported()) return;
|
if (!renameSupported()) return;
|
||||||
@ -869,6 +889,26 @@ public abstract class FileContextMainOperationsBaseTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRenameDirectoryToItself() throws Exception {
|
||||||
|
if (!renameSupported()) return;
|
||||||
|
Path src = getTestRootPath(fc, "test/hadoop/dir");
|
||||||
|
fc.mkdir(src, FileContext.DEFAULT_PERM, true);
|
||||||
|
try {
|
||||||
|
rename(src, src, false, true, false, Rename.NONE);
|
||||||
|
Assert.fail("Renamed directory to itself");
|
||||||
|
} catch (IOException e) {
|
||||||
|
Assert.assertTrue(unwrapException(e) instanceof FileAlreadyExistsException);
|
||||||
|
}
|
||||||
|
// Also fails with overwrite
|
||||||
|
try {
|
||||||
|
rename(src, src, false, true, false, Rename.OVERWRITE);
|
||||||
|
Assert.fail("Renamed directory to itself");
|
||||||
|
} catch (IOException e) {
|
||||||
|
Assert.assertTrue(unwrapException(e) instanceof FileAlreadyExistsException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRenameDirectoryToNonExistentParent() throws Exception {
|
public void testRenameDirectoryToNonExistentParent() throws Exception {
|
||||||
if (!renameSupported()) return;
|
if (!renameSupported()) return;
|
||||||
|
@ -51,6 +51,10 @@ public abstract class FileContextSymlinkBaseTest {
|
|||||||
abstract protected String testBaseDir2();
|
abstract protected String testBaseDir2();
|
||||||
abstract protected URI testURI();
|
abstract protected URI testURI();
|
||||||
|
|
||||||
|
protected IOException unwrapException(IOException e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
protected static void createAndWriteFile(FileContext fc, Path p)
|
protected static void createAndWriteFile(FileContext fc, Path p)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
FSDataOutputStream out;
|
FSDataOutputStream out;
|
||||||
@ -766,7 +770,25 @@ public abstract class FileContextSymlinkBaseTest {
|
|||||||
assertFalse(fc.exists(file));
|
assertFalse(fc.exists(file));
|
||||||
assertTrue(fc.exists(fileNewViaLink));
|
assertTrue(fc.exists(fileNewViaLink));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
/** Rename a symlink to itself */
|
||||||
|
public void testRenameSymlinkToItself() throws IOException {
|
||||||
|
Path link = new Path(testBaseDir1(), "linkToFile1");
|
||||||
|
fc.createSymlink(new Path("/doestNotExist"), link, false);
|
||||||
|
try {
|
||||||
|
fc.rename(link, link);
|
||||||
|
} catch (IOException e) {
|
||||||
|
assertTrue(unwrapException(e) instanceof FileAlreadyExistsException);
|
||||||
|
}
|
||||||
|
// Fails with overwrite as well
|
||||||
|
try {
|
||||||
|
fc.rename(link, link, Rename.OVERWRITE);
|
||||||
|
} catch (IOException e) {
|
||||||
|
assertTrue(unwrapException(e) instanceof FileAlreadyExistsException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
/** Rename a symlink */
|
/** Rename a symlink */
|
||||||
public void testRenameSymlink() throws IOException {
|
public void testRenameSymlink() throws IOException {
|
||||||
@ -786,8 +808,84 @@ public abstract class FileContextSymlinkBaseTest {
|
|||||||
} catch (IOException x) {
|
} catch (IOException x) {
|
||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
/** Rename a symlink to the file it links to */
|
||||||
|
public void testRenameSymlinkToFileItLinksTo() throws IOException {
|
||||||
|
/* NB: The rename is not atomic, so file is deleted before renaming
|
||||||
|
* linkToFile. In this interval linkToFile is dangling and local file
|
||||||
|
* system does not handle dangling links because File.exists returns
|
||||||
|
* false for dangling links. */
|
||||||
|
if ("file".equals(getScheme())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Path file = new Path(testBaseDir1(), "file");
|
||||||
|
Path link = new Path(testBaseDir1(), "linkToFile");
|
||||||
|
createAndWriteFile(file);
|
||||||
|
fc.createSymlink(file, link, false);
|
||||||
|
try {
|
||||||
|
fc.rename(link, file);
|
||||||
|
fail("Renamed symlink to its target");
|
||||||
|
} catch (IOException e) {
|
||||||
|
assertTrue(unwrapException(e) instanceof FileAlreadyExistsException);
|
||||||
|
}
|
||||||
|
// Check the rename didn't happen
|
||||||
|
assertTrue(fc.isFile(file));
|
||||||
|
assertTrue(fc.exists(link));
|
||||||
|
assertTrue(fc.getFileLinkStatus(link).isSymlink());
|
||||||
|
assertEquals(file, fc.getLinkTarget(link));
|
||||||
|
try {
|
||||||
|
fc.rename(link, file, Rename.OVERWRITE);
|
||||||
|
fail("Renamed symlink to its target");
|
||||||
|
} catch (IOException e) {
|
||||||
|
assertTrue(unwrapException(e) instanceof FileAlreadyExistsException);
|
||||||
|
}
|
||||||
|
// Check the rename didn't happen
|
||||||
|
assertTrue(fc.isFile(file));
|
||||||
|
assertTrue(fc.exists(link));
|
||||||
|
assertTrue(fc.getFileLinkStatus(link).isSymlink());
|
||||||
|
assertEquals(file, fc.getLinkTarget(link));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
/** Rename a symlink to the directory it links to */
|
||||||
|
public void testRenameSymlinkToDirItLinksTo() throws IOException {
|
||||||
|
/* NB: The rename is not atomic, so dir is deleted before renaming
|
||||||
|
* linkToFile. In this interval linkToFile is dangling and local file
|
||||||
|
* system does not handle dangling links because File.exists returns
|
||||||
|
* false for dangling links. */
|
||||||
|
if ("file".equals(getScheme())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Path dir = new Path(testBaseDir1(), "dir");
|
||||||
|
Path link = new Path(testBaseDir1(), "linkToDir");
|
||||||
|
fc.mkdir(dir, FileContext.DEFAULT_PERM, false);
|
||||||
|
fc.createSymlink(dir, link, false);
|
||||||
|
try {
|
||||||
|
fc.rename(link, dir);
|
||||||
|
fail("Renamed symlink to its target");
|
||||||
|
} catch (IOException e) {
|
||||||
|
assertTrue(unwrapException(e) instanceof FileAlreadyExistsException);
|
||||||
|
}
|
||||||
|
// Check the rename didn't happen
|
||||||
|
assertTrue(fc.isDirectory(dir));
|
||||||
|
assertTrue(fc.exists(link));
|
||||||
|
assertTrue(fc.getFileLinkStatus(link).isSymlink());
|
||||||
|
assertEquals(dir, fc.getLinkTarget(link));
|
||||||
|
try {
|
||||||
|
fc.rename(link, dir, Rename.OVERWRITE);
|
||||||
|
fail("Renamed symlink to its target");
|
||||||
|
} catch (IOException e) {
|
||||||
|
assertTrue(unwrapException(e) instanceof FileAlreadyExistsException);
|
||||||
|
}
|
||||||
|
// Check the rename didn't happen
|
||||||
|
assertTrue(fc.isDirectory(dir));
|
||||||
|
assertTrue(fc.exists(link));
|
||||||
|
assertTrue(fc.getFileLinkStatus(link).isSymlink());
|
||||||
|
assertEquals(dir, fc.getLinkTarget(link));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
/** Test renaming symlink target */
|
/** Test renaming symlink target */
|
||||||
public void testMoveLinkTarget() throws IOException {
|
public void testMoveLinkTarget() throws IOException {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user