blob: da9b895b57eaf12eb2743d106f7f81e6b90a7aee [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.fs;
import java.io.*;
import java.net.URI;
import java.util.EnumSet;
import org.apache.hadoop.fs.FileContext;
import org.apache.hadoop.fs.Options.CreateOpts;
import org.apache.hadoop.fs.Options.Rename;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.fs.CreateFlag;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.FSDataOutputStream;
import static org.apache.hadoop.fs.FileContextTestHelper.*;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeTrue;
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
/**
* Test symbolic links using FileContext.
*/
public abstract class FileContextSymlinkBaseTest {
static final long seed = 0xDEADBEEFL;
static final int blockSize = 8192;
static final int fileSize = 16384;
protected static FileContext fc;
abstract protected String getScheme();
abstract protected String testBaseDir1() throws IOException;
abstract protected String testBaseDir2() throws IOException;
abstract protected URI testURI();
protected IOException unwrapException(IOException e) {
return e;
}
protected static void createAndWriteFile(FileContext fc, Path p)
throws IOException {
createFile(fc, p, fileSize / blockSize,
CreateOpts.createParent(),
CreateOpts.repFac((short) 1),
CreateOpts.blockSize(blockSize));
}
protected static void createAndWriteFile(Path p) throws IOException {
createAndWriteFile(fc, p);
}
protected static void readFile(Path p) throws IOException {
FileContextTestHelper.readFile(fc, p, fileSize);
}
protected static void readFile(FileContext fc, Path p) throws IOException {
FileContextTestHelper.readFile(fc, p, fileSize);
}
protected static void appendToFile(Path p) throws IOException {
FileContextTestHelper.appendToFile(fc, p, fileSize / blockSize,
CreateOpts.blockSize(blockSize));
}
@Before
public void setUp() throws Exception {
fc.mkdir(new Path(testBaseDir1()), FileContext.DEFAULT_PERM, true);
fc.mkdir(new Path(testBaseDir2()), FileContext.DEFAULT_PERM, true);
}
@After
public void tearDown() throws Exception {
fc.delete(new Path(testBaseDir1()), true);
fc.delete(new Path(testBaseDir2()), true);
}
@Test
/** The root is not a symlink */
public void testStatRoot() throws IOException {
assertFalse(fc.getFileLinkStatus(new Path("/")).isSymlink());
}
@Test
/** Test setWorkingDirectory not resolves symlinks */
public void testSetWDNotResolvesLinks() throws IOException {
Path dir = new Path(testBaseDir1());
Path linkToDir = new Path(testBaseDir1()+"/link");
fc.createSymlink(dir, linkToDir, false);
fc.setWorkingDirectory(linkToDir);
assertEquals(linkToDir.getName(), fc.getWorkingDirectory().getName());
}
@Test
/** Test create a dangling link */
public void testCreateDanglingLink() throws IOException {
Path file = new Path("/noSuchFile");
Path link = new Path(testBaseDir1()+"/link");
fc.createSymlink(file, link, false);
try {
fc.getFileStatus(link);
fail("Got file status of non-existant file");
} catch (FileNotFoundException f) {
// Expected
}
fc.delete(link, false);
}
@Test
/** Test create a link to null and empty path */
public void testCreateLinkToNullEmpty() throws IOException {
Path link = new Path(testBaseDir1()+"/link");
try {
fc.createSymlink(null, link, false);
fail("Can't create symlink to null");
} catch (java.lang.NullPointerException e) {
// Expected, create* with null yields NPEs
}
try {
fc.createSymlink(new Path(""), link, false);
fail("Can't create symlink to empty string");
} catch (java.lang.IllegalArgumentException e) {
// Expected, Path("") is invalid
}
}
@Test
/** Create a link with createParent set */
public void testCreateLinkCanCreateParent() throws IOException {
Path file = new Path(testBaseDir1()+"/file");
Path link = new Path(testBaseDir2()+"/linkToFile");
createAndWriteFile(file);
fc.delete(new Path(testBaseDir2()), true);
try {
fc.createSymlink(file, link, false);
fail("Created link without first creating parent dir");
} catch (IOException x) {
// Expected. Need to create testBaseDir2() first.
}
assertFalse(exists(fc, new Path(testBaseDir2())));
fc.createSymlink(file, link, true);
readFile(link);
}
@Test
/** Try to create a directory given a path that refers to a symlink */
public void testMkdirExistingLink() throws IOException {
Path dir = new Path(testBaseDir1()+"/link");
fc.createSymlink(new Path("/doesNotExist"), dir, false);
try {
fc.mkdir(dir, FileContext.DEFAULT_PERM, false);
fail("Created a dir where a symlink exists");
} catch (FileAlreadyExistsException e) {
// Expected. The symlink already exists.
} catch (IOException e) {
// LocalFs just throws an IOException
assertEquals("file", getScheme());
}
}
@Test
/** Try to create a file with parent that is a dangling link */
public void testCreateFileViaDanglingLinkParent() throws IOException {
Path dir = new Path(testBaseDir1()+"/dangling");
Path file = new Path(testBaseDir1()+"/dangling/file");
fc.createSymlink(new Path("/doesNotExist"), dir, false);
FSDataOutputStream out;
try {
out = fc.create(file, EnumSet.of(CreateFlag.CREATE),
CreateOpts.repFac((short) 1),
CreateOpts.blockSize(blockSize));
out.close();
fail("Created a link with dangling link parent");
} catch (FileNotFoundException e) {
// Expected. The parent is dangling.
}
}
@Test
/** Delete a link */
public void testDeleteLink() throws IOException {
Path file = new Path(testBaseDir1()+"/file");
Path link = new Path(testBaseDir1()+"/linkToFile");
createAndWriteFile(file);
fc.createSymlink(file, link, false);
readFile(link);
fc.delete(link, false);
try {
readFile(link);
fail("Symlink should have been deleted");
} catch (IOException x) {
// Expected
}
// If we deleted the link we can put it back
fc.createSymlink(file, link, false);
}
@Test
/** Ensure open resolves symlinks */
public void testOpenResolvesLinks() throws IOException {
Path file = new Path(testBaseDir1()+"/noSuchFile");
Path link = new Path(testBaseDir1()+"/link");
fc.createSymlink(file, link, false);
try {
fc.open(link);
fail("link target does not exist");
} catch (FileNotFoundException x) {
// Expected
}
fc.delete(link, false);
}
@Test
/** Stat a link to a file */
public void testStatLinkToFile() throws IOException {
Path file = new Path(testBaseDir1()+"/file");
Path linkToFile = new Path(testBaseDir1()+"/linkToFile");
createAndWriteFile(file);
fc.createSymlink(file, linkToFile, false);
assertFalse(fc.getFileLinkStatus(linkToFile).isDirectory());
assertTrue(isSymlink(fc, linkToFile));
assertTrue(isFile(fc, linkToFile));
assertFalse(isDir(fc, linkToFile));
assertEquals(file.toUri().getPath(),
fc.getLinkTarget(linkToFile).toString());
// The local file system does not fully resolve the link
// when obtaining the file status
if (!"file".equals(getScheme())) {
assertEquals(fc.getFileStatus(file), fc.getFileStatus(linkToFile));
assertEquals(fc.makeQualified(file),
fc.getFileStatus(linkToFile).getPath());
assertEquals(fc.makeQualified(linkToFile),
fc.getFileLinkStatus(linkToFile).getPath());
}
}
@Test
/** Stat a relative link to a file */
public void testStatRelLinkToFile() throws IOException {
assumeTrue(!"file".equals(getScheme()));
Path baseDir = new Path(testBaseDir1());
Path file = new Path(testBaseDir1(), "file");
Path linkToFile = new Path(testBaseDir1(), "linkToFile");
createAndWriteFile(file);
fc.createSymlink(new Path("file"), linkToFile, false);
assertEquals(fc.getFileStatus(file), fc.getFileStatus(linkToFile));
assertEquals(fc.makeQualified(file),
fc.getFileStatus(linkToFile).getPath());
assertEquals(fc.makeQualified(linkToFile),
fc.getFileLinkStatus(linkToFile).getPath());
}
@Test
/** Stat a link to a directory */
public void testStatLinkToDir() throws IOException {
Path dir = new Path(testBaseDir1());
Path linkToDir = new Path(testBaseDir1()+"/linkToDir");
fc.createSymlink(dir, linkToDir, false);
assertFalse(fc.getFileStatus(linkToDir).isSymlink());
assertTrue(isDir(fc, linkToDir));
assertFalse(fc.getFileLinkStatus(linkToDir).isDirectory());
assertTrue(fc.getFileLinkStatus(linkToDir).isSymlink());
assertFalse(isFile(fc, linkToDir));
assertTrue(isDir(fc, linkToDir));
assertEquals(dir.toUri().getPath(),
fc.getLinkTarget(linkToDir).toString());
}
@Test
/** Stat a dangling link */
public void testStatDanglingLink() throws IOException {
Path file = new Path("/noSuchFile");
Path link = new Path(testBaseDir1()+"/link");
fc.createSymlink(file, link, false);
assertFalse(fc.getFileLinkStatus(link).isDirectory());
assertTrue(fc.getFileLinkStatus(link).isSymlink());
}
@Test
/** Stat a non-existant file */
public void testStatNonExistantFiles() throws IOException {
Path fileAbs = new Path("/doesNotExist");
try {
fc.getFileLinkStatus(fileAbs);
fail("Got FileStatus for non-existant file");
} catch (FileNotFoundException f) {
// Expected
}
try {
fc.getLinkTarget(fileAbs);
fail("Got link target for non-existant file");
} catch (FileNotFoundException f) {
// Expected
}
}
@Test
/** Test stat'ing a regular file and directory */
public void testStatNonLinks() throws IOException {
Path dir = new Path(testBaseDir1());
Path file = new Path(testBaseDir1()+"/file");
createAndWriteFile(file);
try {
fc.getLinkTarget(dir);
fail("Lstat'd a non-symlink");
} catch (IOException e) {
// Expected.
}
try {
fc.getLinkTarget(file);
fail("Lstat'd a non-symlink");
} catch (IOException e) {
// Expected.
}
}
@Test
/** Test links that link to each other */
public void testRecursiveLinks() throws IOException {
Path link1 = new Path(testBaseDir1()+"/link1");
Path link2 = new Path(testBaseDir1()+"/link2");
fc.createSymlink(link1, link2, false);
fc.createSymlink(link2, link1, false);
try {
readFile(link1);
fail("Read recursive link");
} catch (FileNotFoundException f) {
// LocalFs throws sub class of IOException, since File.exists
// returns false for a link to link.
} catch (IOException x) {
assertEquals("Possible cyclic loop while following symbolic link "+
link1.toString(), x.getMessage());
}
}
/* Assert that the given link to a file behaves as expected. */
private void checkLink(Path linkAbs, Path expectedTarget, Path targetQual)
throws IOException {
Path dir = new Path(testBaseDir1());
// isFile/Directory
assertTrue(isFile(fc, linkAbs));
assertFalse(isDir(fc, linkAbs));
// Check getFileStatus
assertFalse(fc.getFileStatus(linkAbs).isSymlink());
assertFalse(fc.getFileStatus(linkAbs).isDirectory());
assertEquals(fileSize, fc.getFileStatus(linkAbs).getLen());
// Check getFileLinkStatus
assertTrue(isSymlink(fc, linkAbs));
assertFalse(fc.getFileLinkStatus(linkAbs).isDirectory());
// Check getSymlink always returns a qualified target, except
// when partially qualified paths are used (see tests below).
assertEquals(targetQual.toString(),
fc.getFileLinkStatus(linkAbs).getSymlink().toString());
assertEquals(targetQual, fc.getFileLinkStatus(linkAbs).getSymlink());
// Check that the target is qualified using the file system of the
// path used to access the link (if the link target was not specified
// fully qualified, in that case we use the link target verbatim).
if (!"file".equals(getScheme())) {
FileContext localFc = FileContext.getLocalFSFileContext();
Path linkQual = new Path(testURI().toString(), linkAbs);
assertEquals(targetQual,
localFc.getFileLinkStatus(linkQual).getSymlink());
}
// Check getLinkTarget
assertEquals(expectedTarget, fc.getLinkTarget(linkAbs));
// Now read using all path types..
fc.setWorkingDirectory(dir);
readFile(new Path("linkToFile"));
readFile(linkAbs);
// And fully qualified.. (NB: for local fs this is partially qualified)
readFile(new Path(testURI().toString(), linkAbs));
// And partially qualified..
boolean failureExpected = "file".equals(getScheme()) ? false : true;
try {
readFile(new Path(getScheme()+"://"+testBaseDir1()+"/linkToFile"));
assertFalse(failureExpected);
} catch (Exception e) {
assertTrue(failureExpected);
}
// Now read using a different file context (for HDFS at least)
if (!"file".equals(getScheme())) {
FileContext localFc = FileContext.getLocalFSFileContext();
readFile(localFc, new Path(testURI().toString(), linkAbs));
}
}
@Test
/** Test creating a symlink using relative paths */
public void testCreateLinkUsingRelPaths() throws IOException {
Path fileAbs = new Path(testBaseDir1(), "file");
Path linkAbs = new Path(testBaseDir1(), "linkToFile");
Path schemeAuth = new Path(testURI().toString());
Path fileQual = new Path(schemeAuth, testBaseDir1()+"/file");
createAndWriteFile(fileAbs);
fc.setWorkingDirectory(new Path(testBaseDir1()));
fc.createSymlink(new Path("file"), new Path("linkToFile"), false);
checkLink(linkAbs, new Path("file"), fileQual);
// Now rename the link's parent. Because the target was specified
// with a relative path the link should still resolve.
Path dir1 = new Path(testBaseDir1());
Path dir2 = new Path(testBaseDir2());
Path linkViaDir2 = new Path(testBaseDir2(), "linkToFile");
Path fileViaDir2 = new Path(schemeAuth, testBaseDir2()+"/file");
fc.rename(dir1, dir2, Rename.OVERWRITE);
assertEquals(fileViaDir2, fc.getFileLinkStatus(linkViaDir2).getSymlink());
readFile(linkViaDir2);
}
@Test
/** Test creating a symlink using absolute paths */
public void testCreateLinkUsingAbsPaths() throws IOException {
Path fileAbs = new Path(testBaseDir1()+"/file");
Path linkAbs = new Path(testBaseDir1()+"/linkToFile");
Path schemeAuth = new Path(testURI().toString());
Path fileQual = new Path(schemeAuth, testBaseDir1()+"/file");
createAndWriteFile(fileAbs);
fc.createSymlink(fileAbs, linkAbs, false);
checkLink(linkAbs, fileAbs, fileQual);
// Now rename the link's parent. The target doesn't change and
// now no longer exists so accessing the link should fail.
Path dir1 = new Path(testBaseDir1());
Path dir2 = new Path(testBaseDir2());
Path linkViaDir2 = new Path(testBaseDir2(), "linkToFile");
fc.rename(dir1, dir2, Rename.OVERWRITE);
assertEquals(fileQual, fc.getFileLinkStatus(linkViaDir2).getSymlink());
try {
readFile(linkViaDir2);
fail("The target should not exist");
} catch (FileNotFoundException x) {
// Expected
}
}
@Test
/**
* Test creating a symlink using fully and partially qualified paths.
* NB: For local fs this actually tests partially qualified paths,
* as they don't support fully qualified paths.
*/
public void testCreateLinkUsingFullyQualPaths() throws IOException {
Path fileAbs = new Path(testBaseDir1(), "file");
Path linkAbs = new Path(testBaseDir1(), "linkToFile");
Path fileQual = new Path(testURI().toString(), fileAbs);
Path linkQual = new Path(testURI().toString(), linkAbs);
createAndWriteFile(fileAbs);
fc.createSymlink(fileQual, linkQual, false);
checkLink(linkAbs,
"file".equals(getScheme()) ? fileAbs : fileQual,
fileQual);
// Now rename the link's parent. The target doesn't change and
// now no longer exists so accessing the link should fail.
Path dir1 = new Path(testBaseDir1());
Path dir2 = new Path(testBaseDir2());
Path linkViaDir2 = new Path(testBaseDir2(), "linkToFile");
fc.rename(dir1, dir2, Rename.OVERWRITE);
assertEquals(fileQual, fc.getFileLinkStatus(linkViaDir2).getSymlink());
try {
readFile(linkViaDir2);
fail("The target should not exist");
} catch (FileNotFoundException x) {
// Expected
}
}
@Test
/**
* Test creating a symlink using partially qualified paths, ie a scheme
* but no authority and vice versa. We just test link targets here since
* creating using a partially qualified path is file system specific.
*/
public void testCreateLinkUsingPartQualPath1() throws IOException {
// Partially qualified paths are covered for local file systems
// in the previous test.
assumeTrue(!"file".equals(getScheme()));
Path schemeAuth = new Path(testURI().toString());
Path fileWoHost = new Path(getScheme()+"://"+testBaseDir1()+"/file");
Path link = new Path(testBaseDir1()+"/linkToFile");
Path linkQual = new Path(schemeAuth, testBaseDir1()+"/linkToFile");
FileContext localFc = FileContext.getLocalFSFileContext();
fc.createSymlink(fileWoHost, link, false);
// Partially qualified path is stored
assertEquals(fileWoHost, fc.getLinkTarget(linkQual));
// NB: We do not add an authority
assertEquals(fileWoHost.toString(),
fc.getFileLinkStatus(link).getSymlink().toString());
assertEquals(fileWoHost.toString(),
fc.getFileLinkStatus(linkQual).getSymlink().toString());
// Ditto even from another file system
assertEquals(fileWoHost.toString(),
localFc.getFileLinkStatus(linkQual).getSymlink().toString());
// Same as if we accessed a partially qualified path directly
try {
readFile(link);
fail("DFS requires URIs with schemes have an authority");
} catch (java.lang.RuntimeException e) {
// Expected
}
}
@Test
/** Same as above but vice versa (authority but no scheme) */
public void testCreateLinkUsingPartQualPath2() throws IOException {
Path link = new Path(testBaseDir1(), "linkToFile");
Path fileWoScheme = new Path("//"+testURI().getAuthority()+
testBaseDir1()+"/file");
if ("file".equals(getScheme())) {
return;
}
fc.createSymlink(fileWoScheme, link, false);
assertEquals(fileWoScheme, fc.getLinkTarget(link));
assertEquals(fileWoScheme.toString(),
fc.getFileLinkStatus(link).getSymlink().toString());
try {
readFile(link);
fail("Accessed a file with w/o scheme");
} catch (IOException e) {
// Expected
assertEquals("No AbstractFileSystem for scheme: null", e.getMessage());
}
}
@Test
/** Lstat and readlink on a normal file and directory */
public void testLinkStatusAndTargetWithNonLink() throws IOException {
Path schemeAuth = new Path(testURI().toString());
Path dir = new Path(testBaseDir1());
Path dirQual = new Path(schemeAuth, dir.toString());
Path file = new Path(testBaseDir1(), "file");
Path fileQual = new Path(schemeAuth, file.toString());
createAndWriteFile(file);
assertEquals(fc.getFileStatus(file), fc.getFileLinkStatus(file));
assertEquals(fc.getFileStatus(dir), fc.getFileLinkStatus(dir));
try {
fc.getLinkTarget(file);
fail("Get link target on non-link should throw an IOException");
} catch (IOException x) {
assertEquals("Path "+fileQual+" is not a symbolic link", x.getMessage());
}
try {
fc.getLinkTarget(dir);
fail("Get link target on non-link should throw an IOException");
} catch (IOException x) {
assertEquals("Path "+dirQual+" is not a symbolic link", x.getMessage());
}
}
@Test
/** Test create symlink to a directory */
public void testCreateLinkToDirectory() throws IOException {
Path dir1 = new Path(testBaseDir1());
Path file = new Path(testBaseDir1(), "file");
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
createAndWriteFile(file);
fc.createSymlink(dir1, linkToDir, false);
assertFalse(isFile(fc, linkToDir));
assertTrue(isDir(fc, linkToDir));
assertTrue(fc.getFileStatus(linkToDir).isDirectory());
assertTrue(fc.getFileLinkStatus(linkToDir).isSymlink());
}
@Test
/** Test create and remove a file through a symlink */
public void testCreateFileViaSymlink() throws IOException {
Path dir = new Path(testBaseDir1());
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
Path fileViaLink = new Path(linkToDir, "file");
fc.createSymlink(dir, linkToDir, false);
createAndWriteFile(fileViaLink);
assertTrue(isFile(fc, fileViaLink));
assertFalse(isDir(fc, fileViaLink));
assertFalse(fc.getFileLinkStatus(fileViaLink).isSymlink());
assertFalse(fc.getFileStatus(fileViaLink).isDirectory());
readFile(fileViaLink);
fc.delete(fileViaLink, true);
assertFalse(exists(fc, fileViaLink));
}
@Test
/** Test make and delete directory through a symlink */
public void testCreateDirViaSymlink() throws IOException {
Path dir1 = new Path(testBaseDir1());
Path subDir = new Path(testBaseDir1(), "subDir");
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
Path subDirViaLink = new Path(linkToDir, "subDir");
fc.createSymlink(dir1, linkToDir, false);
fc.mkdir(subDirViaLink, FileContext.DEFAULT_PERM, true);
assertTrue(isDir(fc, subDirViaLink));
fc.delete(subDirViaLink, false);
assertFalse(exists(fc, subDirViaLink));
assertFalse(exists(fc, subDir));
}
@Test
/** Create symlink through a symlink */
public void testCreateLinkViaLink() throws IOException {
Path dir1 = new Path(testBaseDir1());
Path file = new Path(testBaseDir1(), "file");
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
Path fileViaLink = new Path(linkToDir, "file");
Path linkToFile = new Path(linkToDir, "linkToFile");
/*
* /b2/linkToDir -> /b1
* /b2/linkToDir/linkToFile -> /b2/linkToDir/file
*/
createAndWriteFile(file);
fc.createSymlink(dir1, linkToDir, false);
fc.createSymlink(fileViaLink, linkToFile, false);
assertTrue(isFile(fc, linkToFile));
assertTrue(fc.getFileLinkStatus(linkToFile).isSymlink());
readFile(linkToFile);
assertEquals(fileSize, fc.getFileStatus(linkToFile).getLen());
assertEquals(fileViaLink, fc.getLinkTarget(linkToFile));
}
@Test
/** Test create symlink to a directory */
public void testListStatusUsingLink() throws IOException {
Path file = new Path(testBaseDir1(), "file");
Path link = new Path(testBaseDir1(), "link");
createAndWriteFile(file);
fc.createSymlink(new Path(testBaseDir1()), link, false);
// The size of the result is file system dependent, Hdfs is 2 (file
// and link) and LocalFs is 3 (file, link, file crc).
FileStatus[] stats = fc.util().listStatus(link);
assertTrue(stats.length == 2 || stats.length == 3);
RemoteIterator<FileStatus> statsItor = fc.listStatus(link);
int dirLen = 0;
while(statsItor.hasNext()) {
statsItor.next();
dirLen++;
}
assertTrue(dirLen == 2 || dirLen == 3);
}
@Test
/** Test create symlink using the same path */
public void testCreateLinkTwice() throws IOException {
Path file = new Path(testBaseDir1(), "file");
Path link = new Path(testBaseDir1(), "linkToFile");
createAndWriteFile(file);
fc.createSymlink(file, link, false);
try {
fc.createSymlink(file, link, false);
fail("link already exists");
} catch (IOException x) {
// Expected
}
}
@Test
/** Test access via a symlink to a symlink */
public void testCreateLinkToLink() throws IOException {
Path dir1 = new Path(testBaseDir1());
Path file = new Path(testBaseDir1(), "file");
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
Path linkToLink = new Path(testBaseDir2(), "linkToLink");
Path fileViaLink = new Path(testBaseDir2(), "linkToLink/file");
createAndWriteFile(file);
fc.createSymlink(dir1, linkToDir, false);
fc.createSymlink(linkToDir, linkToLink, false);
assertTrue(isFile(fc, fileViaLink));
assertFalse(isDir(fc, fileViaLink));
assertFalse(fc.getFileLinkStatus(fileViaLink).isSymlink());
assertFalse(fc.getFileStatus(fileViaLink).isDirectory());
readFile(fileViaLink);
}
@Test
/** Can not create a file with path that refers to a symlink */
public void testCreateFileDirExistingLink() throws IOException {
Path file = new Path(testBaseDir1(), "file");
Path link = new Path(testBaseDir1(), "linkToFile");
createAndWriteFile(file);
fc.createSymlink(file, link, false);
try {
createAndWriteFile(link);
fail("link already exists");
} catch (IOException x) {
// Expected
}
try {
fc.mkdir(link, FsPermission.getDefault(), false);
fail("link already exists");
} catch (IOException x) {
// Expected
}
}
@Test
/** Test deleting and recreating a symlink */
public void testUseLinkAferDeleteLink() throws IOException {
Path file = new Path(testBaseDir1(), "file");
Path link = new Path(testBaseDir1(), "linkToFile");
createAndWriteFile(file);
fc.createSymlink(file, link, false);
fc.delete(link, false);
try {
readFile(link);
fail("link was deleted");
} catch (IOException x) {
// Expected
}
readFile(file);
fc.createSymlink(file, link, false);
readFile(link);
}
@Test
/** Test create symlink to . */
public void testCreateLinkToDot() throws IOException {
Path dir = new Path(testBaseDir1());
Path file = new Path(testBaseDir1(), "file");
Path link = new Path(testBaseDir1(), "linkToDot");
createAndWriteFile(file);
fc.setWorkingDirectory(dir);
try {
fc.createSymlink(new Path("."), link, false);
fail("Created symlink to dot");
} catch (IOException x) {
// Expected. Path(".") resolves to "" because URI normalizes
// the dot away and AbstractFileSystem considers "" invalid.
}
}
@Test
/** Test create symlink to .. */
public void testCreateLinkToDotDot() throws IOException {
Path file = new Path(testBaseDir1(), "test/file");
Path dotDot = new Path(testBaseDir1(), "test/..");
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
Path fileViaLink = new Path(linkToDir, "test/file");
// Symlink to .. is not a problem since the .. is squashed early
assertEquals(testBaseDir1(), dotDot.toString());
createAndWriteFile(file);
fc.createSymlink(dotDot, linkToDir, false);
readFile(fileViaLink);
assertEquals(fileSize, fc.getFileStatus(fileViaLink).getLen());
}
@Test
/** Test create symlink to ../file */
public void testCreateLinkToDotDotPrefix() throws IOException {
Path file = new Path(testBaseDir1(), "file");
Path dir = new Path(testBaseDir1(), "test");
Path link = new Path(testBaseDir1(), "test/link");
createAndWriteFile(file);
fc.mkdir(dir, FsPermission.getDefault(), false);
fc.setWorkingDirectory(dir);
fc.createSymlink(new Path("../file"), link, false);
readFile(link);
assertEquals(new Path("../file"), fc.getLinkTarget(link));
}
@Test
/** Test rename file using a path that contains a symlink. The rename should
* work as if the path did not contain a symlink */
public void testRenameFileViaSymlink() throws IOException {
Path dir = new Path(testBaseDir1());
Path file = new Path(testBaseDir1(), "file");
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
Path fileViaLink = new Path(linkToDir, "file");
Path fileNewViaLink = new Path(linkToDir, "fileNew");
createAndWriteFile(file);
fc.createSymlink(dir, linkToDir, false);
fc.rename(fileViaLink, fileNewViaLink);
assertFalse(exists(fc, fileViaLink));
assertFalse(exists(fc, file));
assertTrue(exists(fc, fileNewViaLink));
}
@Test
/** Test rename a file through a symlink but this time only the
* destination path has an intermediate symlink. The rename should work
* as if the path did not contain a symlink */
public void testRenameFileToDestViaSymlink() throws IOException {
Path dir = new Path(testBaseDir1());
Path file = new Path(testBaseDir1(), "file");
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
Path subDir = new Path(linkToDir, "subDir");
createAndWriteFile(file);
fc.createSymlink(dir, linkToDir, false);
fc.mkdir(subDir, FileContext.DEFAULT_PERM, false);
try {
fc.rename(file, subDir);
fail("Renamed file to a directory");
} catch (IOException e) {
// Expected. Both must be directories.
assertTrue(unwrapException(e) instanceof IOException);
}
assertTrue(exists(fc, file));
}
@Test
/** Similar tests as the previous ones but rename a directory */
public void testRenameDirViaSymlink() throws IOException {
Path baseDir = new Path(testBaseDir1());
Path dir = new Path(baseDir, "dir");
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
Path dirViaLink = new Path(linkToDir, "dir");
Path dirNewViaLink = new Path(linkToDir, "dirNew");
fc.mkdir(dir, FileContext.DEFAULT_PERM, false);
fc.createSymlink(baseDir, linkToDir, false);
assertTrue(exists(fc, dirViaLink));
fc.rename(dirViaLink, dirNewViaLink);
assertFalse(exists(fc, dirViaLink));
assertFalse(exists(fc, dir));
assertTrue(exists(fc, dirNewViaLink));
}
@Test
/** Similar tests as the previous ones but rename a symlink */
public void testRenameSymlinkViaSymlink() throws IOException {
Path baseDir = new Path(testBaseDir1());
Path file = new Path(testBaseDir1(), "file");
Path link = new Path(testBaseDir1(), "link");
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
Path linkViaLink = new Path(linkToDir, "link");
Path linkNewViaLink = new Path(linkToDir, "linkNew");
createAndWriteFile(file);
fc.createSymlink(file, link, false);
fc.createSymlink(baseDir, linkToDir, false);
fc.rename(linkViaLink, linkNewViaLink);
assertFalse(exists(fc, linkViaLink));
// Check that we didn't rename the link target
assertTrue(exists(fc, file));
assertTrue(fc.getFileLinkStatus(linkNewViaLink).isSymlink());
readFile(linkNewViaLink);
}
@Test
/** Test rename a directory to a symlink to a directory */
public void testRenameDirToSymlinkToDir() throws IOException {
Path dir1 = new Path(testBaseDir1());
Path subDir = new Path(testBaseDir2(), "subDir");
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
fc.mkdir(subDir, FileContext.DEFAULT_PERM, false);
fc.createSymlink(subDir, linkToDir, false);
try {
fc.rename(dir1, linkToDir, Rename.OVERWRITE);
fail("Renamed directory to a symlink");
} catch (IOException e) {
// Expected. Both must be directories.
assertTrue(unwrapException(e) instanceof IOException);
}
assertTrue(exists(fc, dir1));
assertTrue(exists(fc, linkToDir));
}
@Test
/** Test rename a directory to a symlink to a file */
public void testRenameDirToSymlinkToFile() throws IOException {
Path dir1 = new Path(testBaseDir1());
Path file = new Path(testBaseDir2(), "file");
Path linkToFile = new Path(testBaseDir2(), "linkToFile");
createAndWriteFile(file);
fc.createSymlink(file, linkToFile, false);
try {
fc.rename(dir1, linkToFile, Rename.OVERWRITE);
fail("Renamed directory to a symlink");
} catch (IOException e) {
// Expected. Both must be directories.
assertTrue(unwrapException(e) instanceof IOException);
}
assertTrue(exists(fc, dir1));
assertTrue(exists(fc, linkToFile));
}
@Test
/** Test rename a directory to a dangling symlink */
public void testRenameDirToDanglingSymlink() throws IOException {
Path dir = new Path(testBaseDir1());
Path link = new Path(testBaseDir2(), "linkToFile");
fc.createSymlink(new Path("/doesNotExist"), link, false);
try {
fc.rename(dir, link, Rename.OVERWRITE);
fail("Renamed directory to a symlink");
} catch (IOException e) {
// Expected. Both must be directories.
assertTrue(unwrapException(e) instanceof IOException);
}
assertTrue(exists(fc, dir));
assertTrue(fc.getFileLinkStatus(link) != null);
}
@Test
/** Test rename a file to a symlink to a directory */
public void testRenameFileToSymlinkToDir() throws IOException {
Path file = new Path(testBaseDir1(), "file");
Path subDir = new Path(testBaseDir1(), "subDir");
Path link = new Path(testBaseDir1(), "link");
fc.mkdir(subDir, FileContext.DEFAULT_PERM, false);
fc.createSymlink(subDir, link, false);
createAndWriteFile(file);
try {
fc.rename(file, link);
fail("Renamed file to symlink w/o overwrite");
} catch (IOException e) {
// Expected
assertTrue(unwrapException(e) instanceof FileAlreadyExistsException);
}
fc.rename(file, link, Rename.OVERWRITE);
assertFalse(exists(fc, file));
assertTrue(exists(fc, link));
assertTrue(isFile(fc, link));
assertFalse(fc.getFileLinkStatus(link).isSymlink());
}
@Test
/** Test rename a file to a symlink to a file */
public void testRenameFileToSymlinkToFile() throws IOException {
Path file1 = new Path(testBaseDir1(), "file1");
Path file2 = new Path(testBaseDir1(), "file2");
Path link = new Path(testBaseDir1(), "linkToFile");
createAndWriteFile(file1);
createAndWriteFile(file2);
fc.createSymlink(file2, link, false);
try {
fc.rename(file1, link);
fail("Renamed file to symlink w/o overwrite");
} catch (IOException e) {
// Expected
assertTrue(unwrapException(e) instanceof FileAlreadyExistsException);
}
fc.rename(file1, link, Rename.OVERWRITE);
assertFalse(exists(fc, file1));
assertTrue(exists(fc, link));
assertTrue(isFile(fc, link));
assertFalse(fc.getFileLinkStatus(link).isSymlink());
}
@Test
/** Test rename a file to a dangling symlink */
public void testRenameFileToDanglingSymlink() throws IOException {
/* NB: Local file system doesn't handle dangling links correctly
* since File.exists(danglinLink) returns false. */
if ("file".equals(getScheme())) {
return;
}
Path file1 = new Path(testBaseDir1(), "file1");
Path link = new Path(testBaseDir1(), "linkToFile");
createAndWriteFile(file1);
fc.createSymlink(new Path("/doesNotExist"), link, false);
try {
fc.rename(file1, link);
} catch (IOException e) {
// Expected
}
fc.rename(file1, link, Rename.OVERWRITE);
assertFalse(exists(fc, file1));
assertTrue(exists(fc, link));
assertTrue(isFile(fc, link));
assertFalse(fc.getFileLinkStatus(link).isSymlink());
}
@Test
/** Rename a symlink to a new non-existant name */
public void testRenameSymlinkNonExistantDest() throws IOException {
Path file = new Path(testBaseDir1(), "file");
Path link1 = new Path(testBaseDir1(), "linkToFile1");
Path link2 = new Path(testBaseDir1(), "linkToFile2");
createAndWriteFile(file);
fc.createSymlink(file, link1, false);
fc.rename(link1, link2);
assertTrue(fc.getFileLinkStatus(link2).isSymlink());
readFile(link2);
readFile(file);
assertFalse(exists(fc, link1));
}
@Test
/** Rename a symlink to a file that exists */
public void testRenameSymlinkToExistingFile() throws IOException {
Path file1 = new Path(testBaseDir1(), "file");
Path file2 = new Path(testBaseDir1(), "someFile");
Path link = new Path(testBaseDir1(), "linkToFile");
createAndWriteFile(file1);
createAndWriteFile(file2);
fc.createSymlink(file2, link, false);
try {
fc.rename(link, file1);
fail("Renamed w/o passing overwrite");
} catch (IOException e) {
// Expected
assertTrue(unwrapException(e) instanceof FileAlreadyExistsException);
}
fc.rename(link, file1, Rename.OVERWRITE);
assertFalse(exists(fc, link));
assertTrue(fc.getFileLinkStatus(file1).isSymlink());
assertEquals(file2, fc.getLinkTarget(file1));
}
@Test
/** Rename a symlink to a directory that exists */
public void testRenameSymlinkToExistingDir() throws IOException {
Path dir1 = new Path(testBaseDir1());
Path dir2 = new Path(testBaseDir2());
Path subDir = new Path(testBaseDir2(), "subDir");
Path link = new Path(testBaseDir1(), "linkToDir");
fc.createSymlink(dir1, link, false);
try {
fc.rename(link, dir2);
fail("Renamed link to a directory");
} catch (IOException e) {
// Expected. Both must be directories.
assertTrue(unwrapException(e) instanceof IOException);
}
try {
fc.rename(link, dir2, Rename.OVERWRITE);
fail("Renamed link to a directory");
} catch (IOException e) {
// Expected. Both must be directories.
assertTrue(unwrapException(e) instanceof IOException);
}
// Also fails when dir2 has a sub-directory
fc.mkdir(subDir, FsPermission.getDefault(), false);
try {
fc.rename(link, dir2, Rename.OVERWRITE);
fail("Renamed link to a directory");
} catch (IOException e) {
// Expected. Both must be directories.
assertTrue(unwrapException(e) instanceof IOException);
}
}
@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
/** Rename a symlink */
public void testRenameSymlink() throws IOException {
Path file = new Path(testBaseDir1(), "file");
Path link1 = new Path(testBaseDir1(), "linkToFile1");
Path link2 = new Path(testBaseDir1(), "linkToFile2");
createAndWriteFile(file);
fc.createSymlink(file, link1, false);
fc.rename(link1, link2);
assertTrue(fc.getFileLinkStatus(link2).isSymlink());
assertFalse(fc.getFileStatus(link2).isDirectory());
readFile(link2);
readFile(file);
try {
createAndWriteFile(link2);
fail("link was not renamed");
} catch (IOException x) {
// 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(isFile(fc, file));
assertTrue(exists(fc, link));
assertTrue(isSymlink(fc, link));
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(isFile(fc, file));
assertTrue(exists(fc, link));
assertTrue(isSymlink(fc, link));
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(isDir(fc, dir));
assertTrue(exists(fc, link));
assertTrue(isSymlink(fc, link));
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(isDir(fc, dir));
assertTrue(exists(fc, link));
assertTrue(isSymlink(fc, link));
assertEquals(dir, fc.getLinkTarget(link));
}
@Test
/** Test rename the symlink's target */
public void testRenameLinkTarget() throws IOException {
Path file = new Path(testBaseDir1(), "file");
Path fileNew = new Path(testBaseDir1(), "fileNew");
Path link = new Path(testBaseDir1(), "linkToFile");
createAndWriteFile(file);
fc.createSymlink(file, link, false);
fc.rename(file, fileNew, Rename.OVERWRITE);
try {
readFile(link);
fail("Link should be dangling");
} catch (IOException x) {
// Expected
}
fc.rename(fileNew, file, Rename.OVERWRITE);
readFile(link);
}
@Test
/** Test rename a file to path with destination that has symlink parent */
public void testRenameFileWithDestParentSymlink() throws IOException {
Path link = new Path(testBaseDir1(), "link");
Path file1 = new Path(testBaseDir1(), "file1");
Path file2 = new Path(testBaseDir1(), "file2");
Path file3 = new Path(link, "file3");
Path dir2 = new Path(testBaseDir2());
// Renaming /dir1/file1 to non-existant file /dir1/link/file3 is OK
// if link points to a directory...
fc.createSymlink(dir2, link, false);
createAndWriteFile(file1);
fc.rename(file1, file3);
assertFalse(exists(fc, file1));
assertTrue(exists(fc, file3));
fc.rename(file3, file1);
// But fails if link is dangling...
fc.delete(link, false);
fc.createSymlink(file2, link, false);
try {
fc.rename(file1, file3);
} catch (IOException e) {
// Expected
assertTrue(unwrapException(e) instanceof FileNotFoundException);
}
// And if link points to a file...
createAndWriteFile(file2);
try {
fc.rename(file1, file3);
} catch (IOException e) {
// Expected
assertTrue(unwrapException(e) instanceof ParentNotDirectoryException);
}
}
@Test
/**
* Create, write, read, append, rename, get the block locations,
* checksums, and delete a file using a path with a symlink as an
* intermediate path component where the link target was specified
* using an absolute path. Rename is covered in more depth below.
*/
public void testAccessFileViaInterSymlinkAbsTarget() throws IOException {
Path baseDir = new Path(testBaseDir1());
Path file = new Path(testBaseDir1(), "file");
Path fileNew = new Path(baseDir, "fileNew");
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
Path fileViaLink = new Path(linkToDir, "file");
Path fileNewViaLink = new Path(linkToDir, "fileNew");
fc.createSymlink(baseDir, linkToDir, false);
createAndWriteFile(fileViaLink);
assertTrue(exists(fc, fileViaLink));
assertTrue(isFile(fc, fileViaLink));
assertFalse(isDir(fc, fileViaLink));
assertFalse(fc.getFileLinkStatus(fileViaLink).isSymlink());
assertFalse(isDir(fc, fileViaLink));
assertEquals(fc.getFileStatus(file),
fc.getFileLinkStatus(file));
assertEquals(fc.getFileStatus(fileViaLink),
fc.getFileLinkStatus(fileViaLink));
readFile(fileViaLink);
appendToFile(fileViaLink);
fc.rename(fileViaLink, fileNewViaLink);
assertFalse(exists(fc, fileViaLink));
assertTrue(exists(fc, fileNewViaLink));
readFile(fileNewViaLink);
assertEquals(fc.getFileBlockLocations(fileNew, 0, 1).length,
fc.getFileBlockLocations(fileNewViaLink, 0, 1).length);
assertEquals(fc.getFileChecksum(fileNew),
fc.getFileChecksum(fileNewViaLink));
fc.delete(fileNewViaLink, true);
assertFalse(exists(fc, fileNewViaLink));
}
@Test
/**
* Operate on a file using a path with an intermediate symlink where
* the link target was specified as a fully qualified path.
*/
public void testAccessFileViaInterSymlinkQualTarget() throws IOException {
Path baseDir = new Path(testBaseDir1());
Path file = new Path(testBaseDir1(), "file");
Path fileNew = new Path(baseDir, "fileNew");
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
Path fileViaLink = new Path(linkToDir, "file");
Path fileNewViaLink = new Path(linkToDir, "fileNew");
fc.createSymlink(fc.makeQualified(baseDir), linkToDir, false);
createAndWriteFile(fileViaLink);
assertEquals(fc.getFileStatus(file),
fc.getFileLinkStatus(file));
assertEquals(fc.getFileStatus(fileViaLink),
fc.getFileLinkStatus(fileViaLink));
readFile(fileViaLink);
}
@Test
/**
* Operate on a file using a path with an intermediate symlink where
* the link target was specified as a relative path.
*/
public void testAccessFileViaInterSymlinkRelTarget() throws IOException {
assumeTrue(!"file".equals(getScheme()));
Path baseDir = new Path(testBaseDir1());
Path dir = new Path(testBaseDir1(), "dir");
Path file = new Path(dir, "file");
Path linkToDir = new Path(testBaseDir1(), "linkToDir");
Path fileViaLink = new Path(linkToDir, "file");
fc.mkdir(dir, FileContext.DEFAULT_PERM, false);
fc.createSymlink(new Path("dir"), linkToDir, false);
createAndWriteFile(fileViaLink);
// Note that getFileStatus returns fully qualified paths even
// when called on an absolute path.
assertEquals(fc.makeQualified(file),
fc.getFileStatus(file).getPath());
// In each case getFileLinkStatus returns the same FileStatus
// as getFileStatus since we're not calling it on a link and
// FileStatus objects are compared by Path.
assertEquals(fc.getFileStatus(file),
fc.getFileLinkStatus(file));
assertEquals(fc.getFileStatus(fileViaLink),
fc.getFileLinkStatus(fileViaLink));
assertEquals(fc.getFileStatus(fileViaLink),
fc.getFileLinkStatus(file));
}
@Test
/** Test create, list, and delete a directory through a symlink */
public void testAccessDirViaSymlink() throws IOException {
Path baseDir = new Path(testBaseDir1());
Path dir = new Path(testBaseDir1(), "dir");
Path linkToDir = new Path(testBaseDir2(), "linkToDir");
Path dirViaLink = new Path(linkToDir, "dir");
fc.createSymlink(baseDir, linkToDir, false);
fc.mkdir(dirViaLink, FileContext.DEFAULT_PERM, true);
assertTrue(fc.getFileStatus(dirViaLink).isDirectory());
FileStatus[] stats = fc.util().listStatus(dirViaLink);
assertEquals(0, stats.length);
RemoteIterator<FileStatus> statsItor = fc.listStatus(dirViaLink);
assertFalse(statsItor.hasNext());
fc.delete(dirViaLink, false);
assertFalse(exists(fc, dirViaLink));
assertFalse(exists(fc, dir));
}
@Test
/** setTimes affects the target not the link */
public void testSetTimes() throws IOException {
Path file = new Path(testBaseDir1(), "file");
Path link = new Path(testBaseDir1(), "linkToFile");
createAndWriteFile(file);
fc.createSymlink(file, link, false);
long at = fc.getFileLinkStatus(link).getAccessTime();
fc.setTimes(link, 2L, 3L);
// NB: local file systems don't implement setTimes
if (!"file".equals(getScheme())) {
assertEquals(at, fc.getFileLinkStatus(link).getAccessTime());
assertEquals(3, fc.getFileStatus(file).getAccessTime());
assertEquals(2, fc.getFileStatus(file).getModificationTime());
}
}
}