blob: 32131c325bb525df875fee975b5f13fe4c79260b [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.sshd.common.file.root;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.FileChannel;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
import java.util.TreeSet;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.util.test.Utils;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
/**
* Tests the RootedFileSystemProvider implementation of
* {@link java.nio.file.spi.FileSystemProvider} checking that permissions for
* generic FS commands are not permitted outside of the root directory.
*
* Individual tests are form pairs (e.g. testX, testXInvalid) where testXInvalid
* is expected to test a parent path of {@link RootedFileSystem#getRoot()}
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class RootedFileSystemProviderTest extends AssertableFile {
private static RootedFileSystem fileSystem;
private static Path rootSandbox;
public RootedFileSystemProviderTest() {
super();
}
@BeforeClass
public static void onlyOnce() throws IOException {
Path targetFolder = ValidateUtils.checkNotNull(
Utils.detectTargetFolder(RootedFileSystemProviderTest.class), "Failed to detect target folder").toPath();
rootSandbox = FileHelper.createTestSandbox(targetFolder.resolve(TEMP_SUBFOLDER_NAME));
fileSystem = (RootedFileSystem) new RootedFileSystemProvider().newFileSystem(rootSandbox,
Collections.<String, Object> emptyMap());
}
@Test
public void testRoot() {
assertTrue(exists(fileSystem.getRoot()) && isDir(fileSystem.getRoot()) && isReadable(fileSystem.getRoot())
&& isRootedAt(rootSandbox, fileSystem.getRoot()));
}
/* mkdir */
@Test
public void testMkdir() throws IOException {
Path created = FileHelper.createDirectory(fileSystem.getPath(getCurrentTestName()));
assertTrue(exists(created) && isDir(created) && isReadable(created));
}
@Test(expected = InvalidPathException.class)
public void testMkdirInvalid() throws IOException {
Path parent = FileHelper.createDirectory(fileSystem.getPath("../" + getCurrentTestName()));
fail(String.format("Unexpected success in creating directory %s", parent.toString()));
}
/* rmdir */
@Test
public void testRmdir() throws IOException {
Path created = FileHelper.createDirectory(fileSystem.getPath(getCurrentTestName()));
Path deleted = FileHelper.deleteDirectory(created);
notExists(deleted);
}
@Test(expected = InvalidPathException.class)
public void testRmdirInvalid() throws IOException {
Path deleted = FileHelper.deleteDirectory(fileSystem.getPath("../" + getCurrentTestName()));
fail(String.format("Unexpected success in removing directory %s", deleted.toString()));
}
/* chdir */
@Test
public void testChdir() throws IOException {
Path created = FileHelper.createDirectory(fileSystem.getPath(getCurrentTestName()));
Path createdFile = FileHelper.createFile(created.resolve(getCurrentTestName()));
boolean hasFile = false;
try (DirectoryStream<Path> ds = FileHelper.readDirectory(created)) {
for (Path p : ds) {
hasFile |= FileHelper.isSameFile(createdFile,
fileSystem.getPath(created.getFileName() + "/" + p.getFileName()));
}
}
assertTrue(createdFile + " found in ch directory", hasFile);
}
@Test(expected = InvalidPathException.class)
public void testChdirInvalid() throws IOException {
Path chdir = FileHelper.createDirectory(fileSystem.getPath("../" + getCurrentTestName()));
fail(String.format("Unexpected success in changing directory %s", chdir.toString()));
}
/* write */
@Test
public void testWriteFile() throws IOException {
Path created = FileHelper.createFile(fileSystem.getPath(getCurrentTestName()));
assertTrue(exists(created) && isReadable(created));
}
@Test(expected = InvalidPathException.class)
public void testWriteFileInvalid() throws IOException {
Path written = FileHelper.createFile(fileSystem.getPath("../" + getCurrentTestName()));
fail(String.format("Unexpected success in writing file %s", written.toString()));
}
/* read */
@Test
public void testReadFile() throws IOException {
Path created = FileHelper.createFile(fileSystem.getPath(getCurrentTestName()));
isNonEmpty(FileHelper.readFile(created));
}
@Test(expected = InvalidPathException.class)
public void testReadFileInvalid() throws IOException {
Path read = fileSystem.getPath("../" + getCurrentTestName());
FileHelper.readFile(read);
fail(String.format("Unexpected success in reading file %s", read.toString()));
}
/* rm */
@Test
public void testDeleteFile() throws IOException {
Path created = FileHelper.createFile(fileSystem.getPath(getCurrentTestName()));
Path deleted = FileHelper.deleteFile(created);
notExists(deleted);
}
@Test(expected = InvalidPathException.class)
public void testDeleteFileInvalid() throws IOException {
Path deleted = FileHelper.deleteFile(fileSystem.getPath("../" + getCurrentTestName()));
fail(String.format("Unexpected success in deleting file %s", deleted.toString()));
}
/* cp */
@Test
public void testCopyFile() throws IOException {
Path created = FileHelper.createFile(fileSystem.getPath(getCurrentTestName()));
Path destination = fileSystem.getPath(getCurrentTestName() + "dest");
FileHelper.copyFile(created, destination);
assertTrue(exists(destination) && isReadable(destination));
}
@Test(expected = InvalidPathException.class)
public void testCopyFileInvalid() throws IOException {
Path created = FileHelper.createFile(fileSystem.getPath(getCurrentTestName()));
Path copy = FileHelper.copyFile(created, fileSystem.getPath("../" + getCurrentTestName()));
fail(String.format("Unexpected success in copying file to %s", copy.toString()));
}
/* mv */
@Test
public void testMoveFile() throws IOException {
Path created = FileHelper.createFile(fileSystem.getPath(getCurrentTestName()));
Path destination = fileSystem.getPath(getCurrentTestName() + "dest");
FileHelper.moveFile(created, destination);
assertTrue(notExists(created) && exists(destination) && isReadable(destination));
}
@Test(expected = InvalidPathException.class)
public void testMoveFileInvalid() throws IOException {
Path created = FileHelper.createFile(fileSystem.getPath(getCurrentTestName()));
Path moved = FileHelper.moveFile(created, fileSystem.getPath("../" + getCurrentTestName()));
fail(String.format("Unexpected success in moving file to %s", moved.toString()));
}
/* link */
@Test
public void testCreateLink() throws IOException {
Path existing = FileHelper.createFile(fileSystem.getPath(getCurrentTestName()));
Path link = fileSystem.getPath(getCurrentTestName() + "link");
FileHelper.createLink(link, existing);
assertTrue(exists(link) && isReadable(link));
}
@Test(expected = InvalidPathException.class)
public void testCreateLinkInvalid() throws IOException {
Path existing = FileHelper.createFile(fileSystem.getPath(getCurrentTestName()));
Path link = FileHelper.createLink(fileSystem.getPath("../" + getCurrentTestName() + "link"), existing);
fail(String.format("Unexpected success in linking file %s", link.toString()));
}
@Test
public void testNewByteChannelProviderMismatchException() throws IOException {
RootedFileSystemProvider provider = new RootedFileSystemProvider();
Path tempFolder = assertHierarchyTargetFolderExists(getTempTargetFolder());
Path file = Files.createTempFile(tempFolder, getCurrentTestName(), ".txt");
try (FileSystem fs = provider.newFileSystem(tempFolder, Collections.<String, Object>emptyMap());
Channel channel = provider.newByteChannel(fs.getPath(file.getFileName().toString()), Collections.<OpenOption>emptySet())) {
assertTrue("Channel not open", channel.isOpen());
}
}
/* Private helper */
/**
* Wrapper around the FileSystemProvider to test generic FS related
* commands. All created temp directories and files used for testing are
* deleted upon JVM exit.
*/
@SuppressWarnings("synthetic-access")
private static final class FileHelper {
private FileHelper() {
super();
}
/**
* Create a randomized test sandbox on each test execution
*
* @param tempDir location to create the sandbox
* @return the created sandbox Path
* @throws IOException
* on failure to create
*/
public static Path createTestSandbox(Path tempDir) throws IOException {
Path created = Files.createDirectories(tempDir.resolve(RootedFileSystemProviderTest.class.getSimpleName()));
created.toFile().deleteOnExit();
return created;
}
public static Path createFile(Path source) throws InvalidPathException, IOException {
try (FileChannel fc = fileSystem.provider().newFileChannel(source,
new TreeSet<OpenOption>(Arrays.asList(StandardOpenOption.CREATE, StandardOpenOption.WRITE)))) {
byte[] randomBytes = new byte[1000];
new Random().nextBytes(randomBytes);
fc.write(ByteBuffer.wrap(randomBytes));
source.toFile().deleteOnExit();
return source;
}
}
public static Path createLink(Path link, Path existing) throws IOException {
fileSystem.provider().createLink(link, existing);
link.toFile().deleteOnExit();
return link;
}
public static Path createDirectory(Path dir) throws InvalidPathException, IOException {
fileSystem.provider().createDirectory(dir);
dir.toFile().deleteOnExit();
return dir;
}
public static Path deleteDirectory(Path dir) throws InvalidPathException, IOException {
return deleteFile(dir);
}
public static Path deleteFile(Path source) throws InvalidPathException, IOException {
fileSystem.provider().delete(source);
return source;
}
public static byte[] readFile(Path source) throws IOException {
try (FileChannel fc = fileSystem.provider().newFileChannel(source,
new TreeSet<OpenOption>(Arrays.asList(StandardOpenOption.READ)))) {
byte[] readBytes = new byte[(int) source.toFile().length()];
fc.read(ByteBuffer.wrap(readBytes));
return readBytes;
}
}
public static Path copyFile(Path source, Path destination) throws InvalidPathException, IOException {
fileSystem.provider().copy(source, destination, StandardCopyOption.COPY_ATTRIBUTES);
destination.toFile().deleteOnExit();
return destination;
}
public static Path moveFile(Path source, Path destination) throws InvalidPathException, IOException {
fileSystem.provider().move(source, destination, StandardCopyOption.ATOMIC_MOVE);
destination.toFile().deleteOnExit();
return destination;
}
public static DirectoryStream<Path> readDirectory(Path dir) throws InvalidPathException, IOException {
DirectoryStream<Path> dirStream = fileSystem.provider().newDirectoryStream(dir,
new DirectoryStream.Filter<Path>() {
@Override
public boolean accept(Path entry) throws IOException {
return true;
}
});
return dirStream;
}
public static boolean isSameFile(Path source, Path destination) throws IOException {
return fileSystem.provider().isSameFile(source, destination);
}
}
}