Fix SFTPClient.mkdirs to not inadvertently prefix with '/' (#415)

This commit is contained in:
Jeroen van Erp
2018-08-02 13:11:09 +02:00
committed by GitHub
parent 7556a7f6f6
commit deff097170
6 changed files with 84 additions and 14 deletions

View File

@@ -18,8 +18,14 @@ package net.schmizz.sshj.sftp;
public class PathComponents { public class PathComponents {
static String adjustForParent(String parent, String path, String pathSep) { static String adjustForParent(String parent, String path, String pathSep) {
return (path.startsWith(pathSep)) ? path // Absolute path, nothing to adjust if (path.startsWith(pathSep)) {
: (parent + (parent.endsWith(pathSep) ? "" : pathSep) + path); // Relative path return path; // Absolute path, nothing to adjust
} else if (parent.endsWith(pathSep)) {
return parent + path; // Relative path, parent endsWith '/'
} else if (parent.isEmpty()) {
return path;
}
return parent + pathSep + path; // Relative path
} }
static String trimTrailingSeparator(String somePath, String pathSep) { static String trimTrailingSeparator(String somePath, String pathSep) {
@@ -33,7 +39,8 @@ public class PathComponents {
public PathComponents(String parent, String name, String pathSep) { public PathComponents(String parent, String name, String pathSep) {
this.parent = parent; this.parent = parent;
this.name = name; this.name = name;
this.path = trimTrailingSeparator(adjustForParent(parent, name, pathSep), pathSep); String adjusted = adjustForParent(parent, name, pathSep);
this.path = !pathSep.equals(adjusted) ? trimTrailingSeparator(adjusted, pathSep) : adjusted;
} }
public String getParent() { public String getParent() {

View File

@@ -70,16 +70,25 @@ public class PathHelper {
*/ */
public PathComponents getComponents(final String path) public PathComponents getComponents(final String path)
throws IOException { throws IOException {
if (path.equals(pathSep)) if (path.equals(pathSep)) {
return getComponents("", ""); return getComponents("", "/");
}
if (path.isEmpty() || ".".equals(path) || ("." + pathSep).equals(path)) if (path.isEmpty() || ".".equals(path) || ("." + pathSep).equals(path)) {
return getComponents(getDotDir()); return getComponents(getDotDir());
}
final String withoutTrailSep = trimTrailingSeparator(path); final String withoutTrailSep = trimTrailingSeparator(path);
final int lastSep = withoutTrailSep.lastIndexOf(pathSep); final int lastSep = withoutTrailSep.lastIndexOf(pathSep);
final String parent = (lastSep == -1) ? "" : withoutTrailSep.substring(0, lastSep); String parent;
final String name = (lastSep == -1) ? withoutTrailSep : withoutTrailSep.substring(lastSep + pathSep.length()); String name;
if (lastSep == -1) {
parent = "";
name = withoutTrailSep;
} else {
parent = lastSep == 0 ? "/" : withoutTrailSep.substring(0, lastSep);
name = withoutTrailSep.substring(lastSep + pathSep.length());
}
if (".".equals(name) || "..".equals(name)) { if (".".equals(name) || "..".equals(name)) {
return getComponents(canonicalizer.canonicalize(path)); return getComponents(canonicalizer.canonicalize(path));
@@ -87,5 +96,4 @@ public class PathHelper {
return getComponents(parent, name); return getComponents(parent, name);
} }
} }
} }

View File

@@ -18,6 +18,7 @@ package com.hierynomus.sshj.sftp
import com.hierynomus.sshj.test.SshFixture import com.hierynomus.sshj.test.SshFixture
import com.hierynomus.sshj.test.util.FileUtil import com.hierynomus.sshj.test.util.FileUtil
import net.schmizz.sshj.SSHClient import net.schmizz.sshj.SSHClient
import net.schmizz.sshj.sftp.FileMode
import net.schmizz.sshj.sftp.SFTPClient import net.schmizz.sshj.sftp.SFTPClient
import org.junit.Rule import org.junit.Rule
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
@@ -167,6 +168,40 @@ class SFTPClientSpec extends Specification {
!new File(dest, "totototo.txt").exists() !new File(dest, "totototo.txt").exists()
} }
def "should mkdirs with existing parent path"() {
given:
SSHClient sshClient = fixture.setupConnectedDefaultClient()
sshClient.authPassword("test", "test")
SFTPClient ftp = sshClient.newSFTPClient()
ftp.mkdir("dir1")
when:
ftp.mkdirs("dir1/dir2/dir3/dir4")
then:
ftp.statExistence("dir1/dir2/dir3/dir4") != null
cleanup:
["dir1/dir2/dir3/dir4", "dir1/dir2/dir3", "dir1/dir2", "dir1"].each {
ftp.rmdir(it)
}
ftp.close()
sshClient.disconnect()
}
def "should stat root"() {
given:
SSHClient sshClient = fixture.setupConnectedDefaultClient()
sshClient.authPassword("test", "test")
SFTPClient ftp = sshClient.newSFTPClient()
when:
def attrs = ftp.statExistence("/")
then:
attrs.type == FileMode.Type.DIRECTORY
}
private void doUpload(File src, File dest) throws IOException { private void doUpload(File src, File dest) throws IOException {
SSHClient sshClient = fixture.setupConnectedDefaultClient() SSHClient sshClient = fixture.setupConnectedDefaultClient()
sshClient.authPassword("test", "test") sshClient.authPassword("test", "test")

View File

@@ -18,6 +18,7 @@ package com.hierynomus.sshj.connection.channel.direct;
import com.hierynomus.sshj.test.SshFixture; import com.hierynomus.sshj.test.SshFixture;
import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.Session; import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.sftp.SFTPClient;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
@@ -46,6 +47,10 @@ public class CommandTest {
exec.join(); exec.join();
assertThat("File should exist", file.exists()); assertThat("File should exist", file.exists());
assertThat("File should be directory", file.isDirectory()); assertThat("File should be directory", file.isDirectory());
SFTPClient sftpClient = sshClient.newSFTPClient();
if (sftpClient.statExistence("&") != null) {
sftpClient.rmdir("&");
// TODO fail here when this is fixed
}
} }
} }

View File

@@ -50,7 +50,7 @@ public class PathHelperTest {
public void root() public void root()
throws IOException { throws IOException {
final PathComponents comp = helper.getComponents("/"); final PathComponents comp = helper.getComponents("/");
assertEquals("", comp.getName()); assertEquals("/", comp.getName());
assertEquals("", comp.getParent()); assertEquals("", comp.getParent());
} }
@@ -67,7 +67,7 @@ public class PathHelperTest {
throws IOException { throws IOException {
final PathComponents comp = helper.getComponents(".."); final PathComponents comp = helper.getComponents("..");
assertEquals("home", comp.getName()); assertEquals("home", comp.getName());
assertEquals("", comp.getParent()); assertEquals("/", comp.getParent());
} }
@Test @Test
@@ -75,9 +75,18 @@ public class PathHelperTest {
throws IOException { throws IOException {
final PathComponents comp = helper.getComponents("somefile"); final PathComponents comp = helper.getComponents("somefile");
assertEquals("somefile", comp.getName()); assertEquals("somefile", comp.getName());
assertEquals("somefile", comp.getPath());
assertEquals("", comp.getParent()); assertEquals("", comp.getParent());
} }
@Test
public void pathInHomeDir() throws IOException {
final PathComponents comp = helper.getComponents("dir1/dir2");
assertEquals("dir2", comp.getName());
assertEquals("dir1/dir2", comp.getPath());
assertEquals("dir1", comp.getParent());
}
@Test @Test
public void fileSomeLevelsDeep() public void fileSomeLevelsDeep()
throws IOException { throws IOException {

View File

@@ -30,15 +30,21 @@ public class SFTPClientTest {
@Before @Before
public void setPathHelper() throws Exception { public void setPathHelper() throws Exception {
PathHelper helper = new PathHelper(new PathHelper.Canonicalizer() { PathHelper helper = new PathHelper(new PathHelper.Canonicalizer() {
/**
* Very basic, it does not try to canonicalize relative bits in the middle of a path.
*/
@Override @Override
public String canonicalize(String path) public String canonicalize(String path)
throws IOException { throws IOException {
if (path.equals(".")) if ("".equals(path) || ".".equals(path) || "./".equals(path))
return "/workingdirectory"; return "/home/me";
if ("..".equals(path) || "../".equals(path))
return "/home";
return path; return path;
} }
}, DEFAULT_PATH_SEPARATOR); }, DEFAULT_PATH_SEPARATOR);
when(sftpEngine.getPathHelper()).thenReturn(helper); when(sftpEngine.getPathHelper()).thenReturn(helper);
when(sftpEngine.stat("/")).thenReturn(new FileAttributes.Builder().withType(FileMode.Type.DIRECTORY).build());
when(sftpEngine.getLoggerFactory()).thenReturn(LoggerFactory.DEFAULT); when(sftpEngine.getLoggerFactory()).thenReturn(LoggerFactory.DEFAULT);
} }