You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2020/11/12 15:05:55 UTC

[mina-sshd] branch master updated: [SSHD-1102] Provide filter support for SftpDirectoryStream

This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git


The following commit(s) were added to refs/heads/master by this push:
     new fbc5d18  [SSHD-1102] Provide filter support for SftpDirectoryStream
fbc5d18 is described below

commit fbc5d187bb2d792f57bf584e2f1c3922e4fd1444
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Nov 12 11:38:40 2020 +0200

    [SSHD-1102] Provide filter support for SftpDirectoryStream
---
 CHANGES.md                                         |   2 +
 .../sshd/sftp/client/fs/SftpDirectoryStream.java   |  55 ++++++-
 .../sftp/client/fs/SftpFileSystemProvider.java     |   2 +-
 .../sshd/sftp/client/fs/SftpPathIterator.java      |  70 +++++++--
 .../client/fs/AbstractSftpFilesSystemSupport.java  | 165 +++++++++++++++++++++
 .../sftp/client/fs/SftpDirectoryScannersTest.java  |  75 ++++++++++
 .../sshd/sftp/client/fs/SftpFileSystemTest.java    | 136 -----------------
 7 files changed, 349 insertions(+), 156 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index d6d1370..0c8196a 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -60,3 +60,5 @@ or `-key-file` command line option.
 * [SSHD-1066](https://issues.apache.org/jira/browse/SSHD-1066) Allow multiple binding to local port tunnel on different addresses
 * [SSHD-1070](https://issues.apache.org/jira/browse/SSHD-1070) OutOfMemoryError when use async port forwarding
 * [SSHD-1100](https://issues.apache.org/jira/browse/SSHD-1100) Updated used moduli for DH group KEX
+* [SSHD-1102](https://issues.apache.org/jira/browse/SSHD-1102) Provide filter support for SftpDirectoryStream
+
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpDirectoryStream.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpDirectoryStream.java
index fab00fd..c53625b 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpDirectoryStream.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpDirectoryStream.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Path;
 import java.util.Iterator;
+import java.util.Objects;
 
 import org.apache.sshd.sftp.client.SftpClient;
 
@@ -31,19 +32,36 @@ import org.apache.sshd.sftp.client.SftpClient;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public class SftpDirectoryStream implements DirectoryStream<Path> {
+    protected SftpPathIterator pathIterator;
+
+    private final SftpPath path;
+    private final Filter<? super Path> filter;
     private final SftpClient sftp;
-    private final Iterable<SftpClient.DirEntry> iter;
-    private final SftpPath p;
 
     /**
      * @param  path        The remote {@link SftpPath}
      * @throws IOException If failed to initialize the directory access handle
      */
     public SftpDirectoryStream(SftpPath path) throws IOException {
+        this(path, null);
+    }
+
+    /**
+     *
+     * @param  path        The remote {@link SftpPath}
+     * @param  filter      An <U>optional</U> {@link java.nio.file.DirectoryStream.Filter filter} - ignored if
+     *                     {@code null}
+     * @throws IOException If failed to initialize the directory access handle
+     */
+    public SftpDirectoryStream(SftpPath path, Filter<? super Path> filter) throws IOException {
+        this.path = Objects.requireNonNull(path, "No path specified");
+        this.filter = filter;
+
         SftpFileSystem fs = path.getFileSystem();
-        p = path;
         sftp = fs.getClient();
-        iter = sftp.readDir(path.toString());
+
+        Iterable<SftpClient.DirEntry> iter = sftp.readDir(path.toString());
+        pathIterator = new SftpPathIterator(getRootPath(), iter, getFilter());
     }
 
     /**
@@ -55,9 +73,36 @@ public class SftpDirectoryStream implements DirectoryStream<Path> {
         return sftp;
     }
 
+    /**
+     * @return The root {@link SftpPath} for this directory stream
+     */
+    public final SftpPath getRootPath() {
+        return path;
+    }
+
+    /**
+     * @return The original filter - may be {@code null} to indicate no filter
+     */
+    public final Filter<? super Path> getFilter() {
+        return filter;
+    }
+
     @Override
     public Iterator<Path> iterator() {
-        return new SftpPathIterator(p, iter);
+        if (!sftp.isOpen()) {
+            throw new IllegalStateException("Stream has been closed");
+        }
+
+        /*
+         * According to documentation this method can be called only once
+         */
+        if (pathIterator == null) {
+            throw new IllegalStateException("Iterator has already been consumed");
+        }
+
+        Iterator<Path> iter = pathIterator;
+        pathIterator = null;
+        return iter;
     }
 
     @Override
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemProvider.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemProvider.java
index e1645c2..f4b62b6 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemProvider.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemProvider.java
@@ -520,7 +520,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
     @Override
     public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException {
         final SftpPath p = toSftpPath(dir);
-        return new SftpDirectoryStream(p);
+        return new SftpDirectoryStream(p, filter);
     }
 
     @Override
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpPathIterator.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpPathIterator.java
index 259dd06..d34a66d 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpPathIterator.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpPathIterator.java
@@ -19,30 +19,65 @@
 
 package org.apache.sshd.sftp.client.fs;
 
+import java.io.IOException;
+import java.nio.file.DirectoryIteratorException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.DirectoryStream.Filter;
 import java.nio.file.Path;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 
 import org.apache.sshd.sftp.client.SftpClient;
 
 /**
+ * Implements and {@link Iterator} of {@link SftpPath}-s returned by a {@link DirectoryStream#iterator()} method.
+ *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public class SftpPathIterator implements Iterator<Path> {
-    private final SftpPath p;
-    private final Iterator<? extends SftpClient.DirEntry> it;
-    private boolean dotIgnored;
-    private boolean dotdotIgnored;
-    private SftpClient.DirEntry curEntry;
+    protected final Iterator<? extends SftpClient.DirEntry> it;
+    protected boolean dotIgnored;
+    protected boolean dotdotIgnored;
+    protected SftpPath curEntry;
+
+    private final SftpPath path;
+    private DirectoryStream.Filter<? super Path> filter;
 
     public SftpPathIterator(SftpPath path, Iterable<? extends SftpClient.DirEntry> iter) {
-        this(path, (iter == null) ? null : iter.iterator());
+        this(path, iter, null);
+    }
+
+    public SftpPathIterator(SftpPath path, Iterable<? extends SftpClient.DirEntry> iter,
+                            DirectoryStream.Filter<? super Path> filter) {
+        this(path, (iter == null) ? null : iter.iterator(), filter);
     }
 
     public SftpPathIterator(SftpPath path, Iterator<? extends SftpClient.DirEntry> iter) {
-        p = path;
+        this(path, iter, null);
+    }
+
+    public SftpPathIterator(SftpPath path, Iterator<? extends SftpClient.DirEntry> iter,
+                            DirectoryStream.Filter<? super Path> filter) {
+        this.path = Objects.requireNonNull(path, "No root path provided");
+        this.filter = filter;
+
         it = iter;
-        curEntry = nextEntry();
+        curEntry = nextEntry(path, filter);
+    }
+
+    /**
+     * @return The root {@link SftpPath} for this directory iterator
+     */
+    public final SftpPath getRootPath() {
+        return path;
+    }
+
+    /**
+     * @return The original filter - may be {@code null} to indicate no filter
+     */
+    public final Filter<? super Path> getFilter() {
+        return filter;
     }
 
     @Override
@@ -56,12 +91,12 @@ public class SftpPathIterator implements Iterator<Path> {
             throw new NoSuchElementException("No next entry");
         }
 
-        SftpClient.DirEntry entry = curEntry;
-        curEntry = nextEntry();
-        return p.resolve(entry.getFilename());
+        SftpPath returnValue = curEntry;
+        curEntry = nextEntry(getRootPath(), getFilter());
+        return returnValue;
     }
 
-    private SftpClient.DirEntry nextEntry() {
+    protected SftpPath nextEntry(SftpPath root, DirectoryStream.Filter<? super Path> selector) {
         while ((it != null) && it.hasNext()) {
             SftpClient.DirEntry entry = it.next();
             String name = entry.getFilename();
@@ -70,7 +105,14 @@ public class SftpPathIterator implements Iterator<Path> {
             } else if ("..".equals(name) && (!dotdotIgnored)) {
                 dotdotIgnored = true;
             } else {
-                return entry;
+                SftpPath candidate = root.resolve(entry.getFilename());
+                try {
+                    if ((selector == null) || selector.accept(candidate)) {
+                        return candidate;
+                    }
+                } catch (IOException e) {
+                    throw new DirectoryIteratorException(e);
+                }
             }
         }
 
@@ -79,6 +121,6 @@ public class SftpPathIterator implements Iterator<Path> {
 
     @Override
     public void remove() {
-        throw new UnsupportedOperationException("newDirectoryStream(" + p + ") Iterator#remove() N/A");
+        throw new UnsupportedOperationException("newDirectoryStream(" + getRootPath() + ") Iterator#remove() N/A");
     }
 }
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/fs/AbstractSftpFilesSystemSupport.java b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/fs/AbstractSftpFilesSystemSupport.java
index 7ede88f..b516290 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/fs/AbstractSftpFilesSystemSupport.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/fs/AbstractSftpFilesSystemSupport.java
@@ -21,14 +21,31 @@ package org.apache.sshd.sftp.client.fs;
 
 import java.io.IOException;
 import java.net.URI;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileAlreadyExistsException;
 import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.AclEntry;
+import java.nio.file.attribute.AclFileAttributeView;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.util.OsUtils;
 import org.apache.sshd.sftp.client.AbstractSftpClientTestSupport;
 import org.apache.sshd.sftp.client.SftpClientFactory;
 import org.apache.sshd.sftp.client.SftpVersionSelector;
+import org.apache.sshd.sftp.common.SftpConstants;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -38,6 +55,154 @@ public abstract class AbstractSftpFilesSystemSupport extends AbstractSftpClientT
         super();
     }
 
+    protected void testFileSystem(FileSystem fs, int version) throws Exception {
+        testRootDirs(fs);
+
+        Path targetPath = detectTargetFolder();
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
+                SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(lclSftp);
+
+        Path current = fs.getPath(".").toRealPath().normalize();
+        outputDebugMessage("CWD: %s", current);
+
+        Path parentPath = targetPath.getParent();
+        Path clientFolder = lclSftp.resolve("client");
+        String remFile1Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-1.txt"));
+        Path file1 = fs.getPath(remFile1Path);
+        assertHierarchyTargetFolderExists(file1.getParent());
+
+        String expected = "Hello world: " + getCurrentTestName();
+        outputDebugMessage("Write initial data to %s", file1);
+        Files.write(file1, expected.getBytes(StandardCharsets.UTF_8));
+        String buf = new String(Files.readAllBytes(file1), StandardCharsets.UTF_8);
+        assertEquals("Mismatched read test data", expected, buf);
+
+        if (version >= SftpConstants.SFTP_V4) {
+            testAclFileAttributeView(file1);
+        }
+
+        String remFile2Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-2.txt"));
+        Path file2 = fs.getPath(remFile2Path);
+        String remFile3Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-3.txt"));
+        Path file3 = fs.getPath(remFile3Path);
+        try {
+            outputDebugMessage("Move with failure expected %s => %s", file2, file3);
+            Files.move(file2, file3, LinkOption.NOFOLLOW_LINKS);
+            fail("Unexpected success in moving " + file2 + " => " + file3);
+        } catch (NoSuchFileException e) {
+            // expected
+        }
+
+        Files.write(file2, "h".getBytes(StandardCharsets.UTF_8));
+        try {
+            outputDebugMessage("Move with failure expected %s => %s", file1, file2);
+            Files.move(file1, file2, LinkOption.NOFOLLOW_LINKS);
+            fail("Unexpected success in moving " + file1 + " => " + file2);
+        } catch (FileAlreadyExistsException e) {
+            // expected
+        }
+
+        outputDebugMessage("Move with success expected %s => %s", file1, file2);
+        Files.move(file1, file2, LinkOption.NOFOLLOW_LINKS, StandardCopyOption.REPLACE_EXISTING);
+        outputDebugMessage("Move with success expected %s => %s", file2, file1);
+        Files.move(file2, file1, LinkOption.NOFOLLOW_LINKS);
+
+        Map<String, Object> attrs = Files.readAttributes(file1, "*");
+        outputDebugMessage("%s attributes: %s", file1, attrs);
+
+        // TODO there are many issues with symbolic links on Windows
+        if (OsUtils.isUNIX()) {
+            Path link = fs.getPath(remFile2Path);
+            Path linkParent = link.getParent();
+            Path relPath = linkParent.relativize(file1);
+
+            testSymbolicLinks(link, relPath);
+        }
+
+        attrs = Files.readAttributes(file1, "*", LinkOption.NOFOLLOW_LINKS);
+        outputDebugMessage("%s no-follow attributes: %s", file1, attrs);
+        assertEquals("Mismatched symlink data", expected, new String(Files.readAllBytes(file1), StandardCharsets.UTF_8));
+
+        testFileChannelLock(file1);
+
+        Files.delete(file1);
+    }
+
+    protected static Iterable<Path> testRootDirs(FileSystem fs) throws IOException {
+        Iterable<Path> rootDirs = fs.getRootDirectories();
+        for (Path root : rootDirs) {
+            String rootName = root.toString();
+            try (DirectoryStream<Path> ds = Files.newDirectoryStream(root)) {
+                for (Path child : ds) {
+                    String name = child.getFileName().toString();
+                    assertNotEquals("Unexpected dot name", ".", name);
+                    assertNotEquals("Unexpected dotdot name", "..", name);
+                    outputDebugMessage("[%s] %s", rootName, child);
+                }
+            } catch (IOException | RuntimeException e) {
+                // TODO on Windows one might get share problems for *.sys files
+                // e.g. "C:\hiberfil.sys: The process cannot access the file because it is being used by another
+                // process"
+                // for now, Windows is less of a target so we are lenient with it
+                if (OsUtils.isWin32()) {
+                    System.err.println(
+                            e.getClass().getSimpleName() + " while accessing children of root=" + root + ": " + e.getMessage());
+                } else {
+                    throw e;
+                }
+            }
+        }
+
+        return rootDirs;
+    }
+
+    protected static AclFileAttributeView testAclFileAttributeView(Path file) throws IOException {
+        outputDebugMessage("getFileAttributeView(%s)", file);
+        AclFileAttributeView aclView
+                = Files.getFileAttributeView(file, AclFileAttributeView.class, LinkOption.NOFOLLOW_LINKS);
+        assertNotNull("No ACL view for " + file, aclView);
+
+        Map<String, ?> attrs = Files.readAttributes(file, "acl:*", LinkOption.NOFOLLOW_LINKS);
+        outputDebugMessage("readAttributes(%s) %s", file, attrs);
+        assertEquals("Mismatched owner for " + file, aclView.getOwner(), attrs.get("owner"));
+
+        @SuppressWarnings("unchecked")
+        List<AclEntry> acl = (List<AclEntry>) attrs.get("acl");
+        outputDebugMessage("acls(%s) %s", file, acl);
+        assertListEquals("Mismatched ACLs for " + file, aclView.getAcl(), acl);
+
+        return aclView;
+    }
+
+    protected static void testSymbolicLinks(Path link, Path relPath) throws IOException {
+        outputDebugMessage("Create symlink %s => %s", link, relPath);
+        Files.createSymbolicLink(link, relPath);
+        assertTrue("Not a symbolic link: " + link, Files.isSymbolicLink(link));
+
+        Path symLink = Files.readSymbolicLink(link);
+        assertEquals("mismatched symbolic link name", relPath.toString(), symLink.toString());
+
+        outputDebugMessage("Delete symlink %s", link);
+        Files.delete(link);
+    }
+
+    protected static void testFileChannelLock(Path file) throws IOException {
+        try (FileChannel channel = FileChannel.open(file)) {
+            try (FileLock lock = channel.lock()) {
+                outputDebugMessage("Lock %s: %s", file, lock);
+
+                try (FileChannel channel2 = FileChannel.open(file)) {
+                    try (FileLock lock2 = channel2.lock()) {
+                        fail("Unexpected success in re-locking " + file + ": " + lock2);
+                    } catch (OverlappingFileLockException e) {
+                        // expected
+                    }
+                }
+            }
+        }
+    }
+
     protected static FileSystem createSftpFileSystem(ClientSession session, SftpVersionSelector selector) throws IOException {
         return SftpClientFactory.instance().createSftpFileSystem(session, selector);
     }
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/fs/SftpDirectoryScannersTest.java b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/fs/SftpDirectoryScannersTest.java
index 12ee16b..995eb35 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/fs/SftpDirectoryScannersTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/fs/SftpDirectoryScannersTest.java
@@ -22,11 +22,13 @@ package org.apache.sshd.sftp.client.fs;
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.DirectoryStream;
 import java.nio.file.FileSystem;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -91,6 +93,79 @@ public class SftpDirectoryScannersTest extends AbstractSftpFilesSystemSupport {
         testSftpClientDirectoryScanner(setupFileSuffixMatching(), "*.txt");
     }
 
+    @Test   // see SSHD-1102
+    public void testDirectoryStreamFilter() throws IOException {
+        SetupDetails details = setupFileSuffixMatching();
+        List<Path> expected = details.getExpected();
+        List<Path> actual = new ArrayList<>();
+        try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(), Collections.emptyMap())) {
+            DirectoryStream.Filter<Path> filter = p -> {
+                if (Files.isDirectory(p)) {
+                    return true;
+                }
+
+                if (!Files.isRegularFile(p)) {
+                    return false;
+                }
+
+                return p.getFileName().toString().endsWith(".txt");
+            };
+            collectMatchingFiles(fs.getPath(details.getRemoteFilePath()), filter, actual);
+        }
+
+        Collections.sort(actual);
+
+        assertListEquals(getCurrentTestName(), expected, actual, PathUtils.EQ_CASE_SENSITIVE_FILENAME);
+    }
+
+    private static void collectMatchingFiles(
+            Path dir, DirectoryStream.Filter<? super Path> filter, Collection<Path> matches)
+            throws IOException {
+        try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir, filter)) {
+            for (Path p : ds) {
+                assertTrue("Unfiltered path: " + p, filter.accept(p));
+
+                if (Files.isDirectory(p)) {
+                    collectMatchingFiles(p, filter, matches);
+                } else if (Files.isRegularFile(p)) {
+                    matches.add(p);
+                }
+            }
+        }
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testClosedDirectoryStreamIteration() throws IOException {
+        SetupDetails details = setupDeepScanning();
+        try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(), Collections.emptyMap())) {
+            Path dir = fs.getPath(details.getRemoteFilePath());
+            try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
+                ds.close();
+
+                for (Path p : ds) {
+                    fail("Unexpected iterated path: " + p);
+                }
+            }
+        }
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testDirectoryStreamRepeatedIteration() throws IOException {
+        SetupDetails details = setupDeepScanning();
+        try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(), Collections.emptyMap())) {
+            Path dir = fs.getPath(details.getRemoteFilePath());
+            try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
+                for (Path p : ds) {
+                    assertNotNull(p);
+                }
+
+                for (Path p : ds) {
+                    fail("Unexpected iterated path: " + p);
+                }
+            }
+        }
+    }
+
     private void testSftpClientDirectoryScanner(SetupDetails setup, String pattern) throws IOException {
         List<Path> expected = setup.getExpected();
         String remRoot = setup.getRemoteFilePath();
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/fs/SftpFileSystemTest.java b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/fs/SftpFileSystemTest.java
index 94727ca..12552ed 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/fs/SftpFileSystemTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/fs/SftpFileSystemTest.java
@@ -26,22 +26,13 @@ import java.io.Reader;
 import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
-import java.nio.channels.FileLock;
-import java.nio.channels.OverlappingFileLockException;
 import java.nio.charset.StandardCharsets;
-import java.nio.file.DirectoryStream;
-import java.nio.file.FileAlreadyExistsException;
 import java.nio.file.FileStore;
 import java.nio.file.FileSystem;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
 import java.nio.file.StandardOpenOption;
-import java.nio.file.attribute.AclEntry;
-import java.nio.file.attribute.AclFileAttributeView;
 import java.nio.file.attribute.FileAttributeView;
 import java.nio.file.attribute.FileTime;
 import java.nio.file.attribute.GroupPrincipal;
@@ -341,131 +332,4 @@ public class SftpFileSystemTest extends AbstractSftpFilesSystemSupport {
 
         assertTrue("No configuration found", found);
     }
-
-    private void testFileSystem(FileSystem fs, int version) throws Exception {
-        Iterable<Path> rootDirs = fs.getRootDirectories();
-        for (Path root : rootDirs) {
-            String rootName = root.toString();
-            try (DirectoryStream<Path> ds = Files.newDirectoryStream(root)) {
-                for (Path child : ds) {
-                    String name = child.getFileName().toString();
-                    assertNotEquals("Unexpected dot name", ".", name);
-                    assertNotEquals("Unexpected dotdot name", "..", name);
-                    outputDebugMessage("[%s] %s", rootName, child);
-                }
-            } catch (IOException | RuntimeException e) {
-                // TODO on Windows one might get share problems for *.sys files
-                // e.g. "C:\hiberfil.sys: The process cannot access the file because it is being used by another
-                // process"
-                // for now, Windows is less of a target so we are lenient with it
-                if (OsUtils.isWin32()) {
-                    System.err.println(
-                            e.getClass().getSimpleName() + " while accessing children of root=" + root + ": " + e.getMessage());
-                } else {
-                    throw e;
-                }
-            }
-        }
-
-        Path targetPath = detectTargetFolder();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
-                SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-        CommonTestSupportUtils.deleteRecursive(lclSftp);
-
-        Path current = fs.getPath(".").toRealPath().normalize();
-        outputDebugMessage("CWD: %s", current);
-
-        Path parentPath = targetPath.getParent();
-        Path clientFolder = lclSftp.resolve("client");
-        String remFile1Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-1.txt"));
-        Path file1 = fs.getPath(remFile1Path);
-        assertHierarchyTargetFolderExists(file1.getParent());
-
-        String expected = "Hello world: " + getCurrentTestName();
-        outputDebugMessage("Write initial data to %s", file1);
-        Files.write(file1, expected.getBytes(StandardCharsets.UTF_8));
-        String buf = new String(Files.readAllBytes(file1), StandardCharsets.UTF_8);
-        assertEquals("Mismatched read test data", expected, buf);
-
-        if (version >= SftpConstants.SFTP_V4) {
-            outputDebugMessage("getFileAttributeView(%s)", file1);
-            AclFileAttributeView aclView
-                    = Files.getFileAttributeView(file1, AclFileAttributeView.class, LinkOption.NOFOLLOW_LINKS);
-            assertNotNull("No ACL view for " + file1, aclView);
-
-            Map<String, ?> attrs = Files.readAttributes(file1, "acl:*", LinkOption.NOFOLLOW_LINKS);
-            outputDebugMessage("readAttributes(%s) %s", file1, attrs);
-            assertEquals("Mismatched owner for " + file1, aclView.getOwner(), attrs.get("owner"));
-
-            @SuppressWarnings("unchecked")
-            List<AclEntry> acl = (List<AclEntry>) attrs.get("acl");
-            outputDebugMessage("acls(%s) %s", file1, acl);
-            assertListEquals("Mismatched ACLs for " + file1, aclView.getAcl(), acl);
-        }
-
-        String remFile2Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-2.txt"));
-        Path file2 = fs.getPath(remFile2Path);
-        String remFile3Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-3.txt"));
-        Path file3 = fs.getPath(remFile3Path);
-        try {
-            outputDebugMessage("Move with failure expected %s => %s", file2, file3);
-            Files.move(file2, file3, LinkOption.NOFOLLOW_LINKS);
-            fail("Unexpected success in moving " + file2 + " => " + file3);
-        } catch (NoSuchFileException e) {
-            // expected
-        }
-
-        Files.write(file2, "h".getBytes(StandardCharsets.UTF_8));
-        try {
-            outputDebugMessage("Move with failure expected %s => %s", file1, file2);
-            Files.move(file1, file2, LinkOption.NOFOLLOW_LINKS);
-            fail("Unexpected success in moving " + file1 + " => " + file2);
-        } catch (FileAlreadyExistsException e) {
-            // expected
-        }
-
-        outputDebugMessage("Move with success expected %s => %s", file1, file2);
-        Files.move(file1, file2, LinkOption.NOFOLLOW_LINKS, StandardCopyOption.REPLACE_EXISTING);
-        outputDebugMessage("Move with success expected %s => %s", file2, file1);
-        Files.move(file2, file1, LinkOption.NOFOLLOW_LINKS);
-
-        Map<String, Object> attrs = Files.readAttributes(file1, "*");
-        outputDebugMessage("%s attributes: %s", file1, attrs);
-
-        // TODO there are many issues with symbolic links on Windows
-        if (OsUtils.isUNIX()) {
-            Path link = fs.getPath(remFile2Path);
-            Path linkParent = link.getParent();
-            Path relPath = linkParent.relativize(file1);
-            outputDebugMessage("Create symlink %s => %s", link, relPath);
-            Files.createSymbolicLink(link, relPath);
-            assertTrue("Not a symbolic link: " + link, Files.isSymbolicLink(link));
-
-            Path symLink = Files.readSymbolicLink(link);
-            assertEquals("mismatched symbolic link name", relPath.toString(), symLink.toString());
-
-            outputDebugMessage("Delete symlink %s", link);
-            Files.delete(link);
-        }
-
-        attrs = Files.readAttributes(file1, "*", LinkOption.NOFOLLOW_LINKS);
-        outputDebugMessage("%s no-follow attributes: %s", file1, attrs);
-        assertEquals("Mismatched symlink data", expected, new String(Files.readAllBytes(file1), StandardCharsets.UTF_8));
-
-        try (FileChannel channel = FileChannel.open(file1)) {
-            try (FileLock lock = channel.lock()) {
-                outputDebugMessage("Lock %s: %s", file1, lock);
-
-                try (FileChannel channel2 = FileChannel.open(file1)) {
-                    try (FileLock lock2 = channel2.lock()) {
-                        fail("Unexpected success in re-locking " + file1 + ": " + lock2);
-                    } catch (OverlappingFileLockException e) {
-                        // expected
-                    }
-                }
-            }
-        }
-
-        Files.delete(file1);
-    }
 }