You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2015/02/23 16:30:02 UTC
[01/15] mina-sshd git commit: Upgrade to 1.0.0-SNAPSHOT and remove
unsupported modules
Repository: mina-sshd
Updated Branches:
refs/heads/master 964e76890 -> 656f0d17e
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/test/java/org/apache/sshd/sftp/SftpTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/sftp/SftpTest.java b/sshd-sftp/src/test/java/org/apache/sshd/sftp/SftpTest.java
deleted file mode 100644
index 6cd79f7..0000000
--- a/sshd-sftp/src/test/java/org/apache/sshd/sftp/SftpTest.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * 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.sftp;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Arrays;
-import java.util.Vector;
-
-import com.jcraft.jsch.ChannelSftp;
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.Logger;
-import org.apache.sshd.SshServer;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.server.Command;
-import org.apache.sshd.server.command.ScpCommandFactory;
-import org.apache.sshd.sftp.subsystem.SftpSubsystem;
-import org.apache.sshd.sftp.util.BogusPasswordAuthenticator;
-import org.apache.sshd.sftp.util.EchoShellFactory;
-import org.apache.sshd.sftp.util.JSchLogger;
-import org.apache.sshd.sftp.util.SimpleUserInfo;
-import org.apache.sshd.sftp.util.Utils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-public class SftpTest {
-
- private SshServer sshd;
- private int port;
- private com.jcraft.jsch.Session session;
-
- @Before
- public void setUp() throws Exception {
- port = Utils.getFreePort();
-
- sshd = SshServer.setUpDefaultServer();
- sshd.setPort(port);
- sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
- sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
- sshd.setCommandFactory(new ScpCommandFactory());
- sshd.setShellFactory(new EchoShellFactory());
- sshd.setPasswordAuthenticator(new BogusPasswordAuthenticator());
- sshd.start();
-
- JSchLogger.init();
- JSch sch = new JSch();
- session = sch.getSession("sshd", "localhost", port);
- session.setUserInfo(new SimpleUserInfo("sshd"));
- session.connect();
- }
-
- @After
- public void tearDown() throws Exception {
- session.disconnect();
- sshd.stop();
- }
-
- @Test
- @Ignore
- public void testExternal() throws Exception {
- System.out.println("SFTP subsystem available on port " + port);
- Thread.sleep(5 * 60000);
- }
-
- @Test
- public void testSftp() throws Exception {
- String d = "0123456789\n";
-
- File root = new File("target/scp");
- File target = new File("target/scp/out.txt");
- root.mkdirs();
- assertTrue(root.exists());
-
- for (int j = 10; j <= 10; j++) {
- String data = "";
- for (int i = 0; i < j; i++) {
- data = data + d;
- }
-
- target.delete();
- assertFalse(target.exists());
- sendFile("target/scp/out.txt", data);
- assertFileLength(target, data.length(), 5000);
-
- target.delete();
- assertFalse(target.exists());
- }
- root.delete();
- }
-
- @Test
- public void testReadWriteWithOffset() throws Exception {
- File root = new File("target/scp");
- String unixPath = "target/scp/out.txt";
- File target = new File(unixPath);
- root.mkdirs();
- assertTrue(root.exists());
-
- ChannelSftp c = (ChannelSftp) session.openChannel("sftp");
- c.connect();
- c.put(new ByteArrayInputStream("0123456789".getBytes()), unixPath);
-
- assertTrue(target.exists());
- assertEquals("0123456789", readFile(unixPath));
-
- OutputStream os = c.put(unixPath, null, ChannelSftp.APPEND, -5);
- os.write("a".getBytes());
- os.close();
- c.disconnect();
-
- assertTrue(target.exists());
- assertEquals("01234a", readFile(unixPath));
-
- target.delete();
- assertFalse(target.exists());
- root.delete();
- }
-
- @Test
- public void testReadDir() throws Exception {
- ChannelSftp c = (ChannelSftp) session.openChannel("sftp");
- c.connect();
- Vector res = c.ls("target/classes/org/apache/sshd/");
- for (Object f : res) {
- System.out.println(f.toString());
- }
- }
-
- /*
- * TODO: upgrade to a more recent version of ganymed to be able to test that
-
- @Test
- public void testBigFileWithGanymed() throws Exception {
- final Connection conn = new Connection("localhost", port);
- conn.connect(null, 5000, 0);
- conn.authenticateWithPassword("sshd", "sshd");
- final SFTPv3Client sftp_client = new SFTPv3Client(conn);
-
- StringBuilder sb = new StringBuilder();
- for (int i = 1; i < 1000; i++) {
- sb.append("0123456789");
- if (i % 10 == 0) {
- sb.append("\n");
- }
- }
- sb.append("\n");
- byte[] buffer = sb.toString().getBytes();
-
- // Upload
-
- SFTPv3FileHandle handle = sftp_client.openFileRW("target/bigfile.txt");
-
- long offset = 0;
- for (int i = 0; i < 100; i++) {
- sftp_client.write(handle, offset, buffer, 0, buffer.length);
- offset += buffer.length;
- }
-
- sftp_client.closeFile(handle);
-
- handle = sftp_client.openFileRW("target/bigfile.txt");
-
- offset = 0;
- buffer = new byte[32768];
- for (;;) {
- int len = sftp_client.read(handle, offset, buffer, 0, buffer.length);
- if (len >= 0) {
- offset += len;
- } else {
- break;
- }
- }
-
- sftp_client.closeFile(handle);
-
- sftp_client.close();
-
- }
- */
-
- protected void assertFileLength(File file, long length, long timeout) throws Exception {
- boolean ok = false;
- while (timeout > 0) {
- if (file.exists() && file.length() == length) {
- if (!ok) {
- ok = true;
- } else {
- return;
- }
- } else {
- ok = false;
- }
- Thread.sleep(100);
- timeout -= 100;
- }
- assertTrue(file.exists());
- assertEquals(length, file.length());
- }
-
- protected String readFile(String path) throws Exception {
- ChannelSftp c = (ChannelSftp) session.openChannel("sftp");
- c.connect();
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- InputStream is = c.get(path);
- try {
- byte[] buffer = new byte[256];
- int count;
- while (-1 != (count = is.read(buffer))) {
- bos.write(buffer, 0, count);
- }
- } finally {
- is.close();
- }
-
- c.disconnect();
- return new String(bos.toByteArray());
- }
-
- protected void sendFile(String path, String data) throws Exception {
- ChannelSftp c = (ChannelSftp) session.openChannel("sftp");
- c.connect();
- c.put(new ByteArrayInputStream(data.getBytes()), path);
- c.disconnect();
- }
-
- public static void main(String[] args) throws Exception {
- SshServer sshd = SshServer.setUpDefaultServer();
- sshd.setPort(8001);
- sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
- sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
- sshd.setShellFactory(new EchoShellFactory());
- sshd.setCommandFactory(new ScpCommandFactory());
- sshd.setPasswordAuthenticator(new BogusPasswordAuthenticator());
- sshd.start();
- Thread.sleep(100000);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/BogusPasswordAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/BogusPasswordAuthenticator.java b/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/BogusPasswordAuthenticator.java
deleted file mode 100644
index 69e047a..0000000
--- a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/BogusPasswordAuthenticator.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.sftp.util;
-
-import org.apache.sshd.server.PasswordAuthenticator;
-import org.apache.sshd.server.session.ServerSession;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BogusPasswordAuthenticator implements PasswordAuthenticator {
-
- public boolean authenticate(String username, String password, ServerSession session) {
- return username != null && username.equals(password);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/EchoShellFactory.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/EchoShellFactory.java b/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/EchoShellFactory.java
deleted file mode 100644
index c4b4827..0000000
--- a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/EchoShellFactory.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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.sftp.util;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.server.Command;
-import org.apache.sshd.server.Environment;
-import org.apache.sshd.server.ExitCallback;
-
-import java.io.*;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class EchoShellFactory implements Factory<Command> {
-
- public Command create() {
- return new EchoShell();
- }
-
- public static class EchoShell implements Command, Runnable {
-
- private InputStream in;
- private OutputStream out;
- private OutputStream err;
- private ExitCallback callback;
- private Environment environment;
- private Thread thread;
-
- public InputStream getIn() {
- return in;
- }
-
- public OutputStream getOut() {
- return out;
- }
-
- public OutputStream getErr() {
- return err;
- }
-
- public Environment getEnvironment() {
- return environment;
- }
-
- public void setInputStream(InputStream in) {
- this.in = in;
- }
-
- public void setOutputStream(OutputStream out) {
- this.out = out;
- }
-
- public void setErrorStream(OutputStream err) {
- this.err = err;
- }
-
- public void setExitCallback(ExitCallback callback) {
- this.callback = callback;
- }
-
- public void start(Environment env) throws IOException {
- environment = env;
- thread = new Thread(this, "EchoShell");
- thread.start();
- }
-
- public void destroy() {
- thread.interrupt();
- }
-
- public void run() {
- BufferedReader r = new BufferedReader(new InputStreamReader(in));
- try {
- for (;;) {
- String s = r.readLine();
- if (s == null) {
- return;
- }
- out.write((s + "\n").getBytes());
- out.flush();
- if ("exit".equals(s)) {
- return;
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- callback.onExit(0);
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/JSchLogger.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/JSchLogger.java b/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/JSchLogger.java
deleted file mode 100644
index a7a4d62..0000000
--- a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/JSchLogger.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.sftp.util;
-
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class JSchLogger implements Logger {
-
- public static void init() {
- JSch.setLogger(new JSchLogger());
- }
-
- private org.slf4j.Logger log = LoggerFactory.getLogger("org.jcraft.jsch.JSch");
-
- public boolean isEnabled(int level) {
- switch (level) {
- case DEBUG: return log.isDebugEnabled();
- case INFO: return log.isInfoEnabled();
- case WARN: return log.isWarnEnabled();
- case ERROR: return log.isErrorEnabled();
- case FATAL: return log.isErrorEnabled();
- }
- return false;
- }
-
- public void log(int level, String message) {
- switch (level) {
- case DEBUG: log.debug(message); break;
- case INFO: log.info(message); break;
- case WARN: log.warn(message); break;
- case ERROR: log.error(message); break;
- case FATAL: log.error(message); break;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/SimpleUserInfo.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/SimpleUserInfo.java b/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/SimpleUserInfo.java
deleted file mode 100644
index f3cf54f..0000000
--- a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/SimpleUserInfo.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.sftp.util;
-
-import com.jcraft.jsch.UIKeyboardInteractive;
-import com.jcraft.jsch.UserInfo;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SimpleUserInfo implements UserInfo, UIKeyboardInteractive {
-
- private final String password;
-
- public SimpleUserInfo(String password) {
- this.password = password;
- }
-
- public String getPassphrase() {
- return null;
- }
-
- public String getPassword() {
- return password;
- }
-
- public boolean promptPassword(String message) {
- return true;
- }
-
- public boolean promptPassphrase(String message) {
- return false;
- }
-
- public boolean promptYesNo(String message) {
- return true;
- }
-
- public void showMessage(String message) {
- }
-
- public String[] promptKeyboardInteractive(String destination, String name, String instruction, String[] prompt, boolean[] echo) {
- return new String[] { password };
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/Utils.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/Utils.java b/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/Utils.java
deleted file mode 100644
index cc09591..0000000
--- a/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/Utils.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.sftp.util;
-
-import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
-
-import java.io.File;
-import java.net.ServerSocket;
-import java.net.URISyntaxException;
-import java.net.URL;
-
-public class Utils {
-
- public static FileKeyPairProvider createTestHostKeyProvider() {
- return createTestKeyPairProvider("hostkey.pem");
- }
-
- public static FileKeyPairProvider createTestKeyPairProvider(String resource) {
- return new FileKeyPairProvider(new String[] { getFile(resource) });
- }
-
- public static int getFreePort() throws Exception {
- ServerSocket s = new ServerSocket(0);
- try {
- return s.getLocalPort();
- } finally {
- s.close();
- }
- }
-
- private static String getFile(String resource) {
- URL url = Utils.class.getClassLoader().getResource(resource);
- File f;
- try {
- f = new File(url.toURI());
- } catch(URISyntaxException e) {
- f = new File(url.getPath());
- }
- return f.toString();
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/test/resources/hostkey.pem
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/resources/hostkey.pem b/sshd-sftp/src/test/resources/hostkey.pem
deleted file mode 100644
index 18d68ac..0000000
--- a/sshd-sftp/src/test/resources/hostkey.pem
+++ /dev/null
@@ -1,30 +0,0 @@
-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.
-
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQDdfIWeSV4o68dRrKSzFd/Bk51E65UTmmSrmW0O1ohtzi6HzsDP
-jXgCtlTt3FqTcfFfI92IlTr4JWqC9UK1QT1ZTeng0MkPQmv68hDANHbt5CpETZHj
-W5q4OOgWhVvj5IyOC2NZHtKlJBkdsMAa15ouOOJLzBvAvbqOR/yUROsEiQIDAQAB
-AoGBANG3JDW6NoP8rF/zXoeLgLCj+tfVUPSczhGFVrQkAk4mWfyRkhN0WlwHFOec
-K89MpkV1ij/XPVzU4MNbQ2yod1KiDylzvweYv+EaEhASCmYNs6LS03punml42SL9
-97tOmWfVJXxlQoLiY6jHPU97vTc65k8gL+gmmrpchsW0aqmZAkEA/c8zfmKvY37T
-cxcLLwzwsqqH7g2KZGTf9aRmx2ebdW+QKviJJhbdluDgl1TNNFj5vCLznFDRHiqJ
-wq0wkZ39cwJBAN9l5v3kdXj21UrurNPdlV0n2GZBt2vblooQC37XHF97r2zM7Ou+
-Lg6MyfJClyguhWL9dxnGbf3btQ0l3KDstxMCQCRaiEqjAfIjWVATzeNIXDWLHXso
-b1kf5cA+cwY+vdKdTy4IeUR+Y/DXdvPWDqpf0C11aCVMohdLCn5a5ikFUycCQDhV
-K/BuAallJNfmY7JxN87r00fF3ojWMJnT/fIYMFFrkQrwifXQWTDWE76BSDibsosJ
-u1TGksnm8zrDh2UVC/0CQFrHTiSl/3DHvWAbOJawGKg46cnlDcAhSyV8Frs8/dlP
-7YGG3eqkw++lsghqmFO6mRUTKsBmiiB2wgLGhL5pyYY=
------END RSA PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/resources/log4j.properties b/sshd-sftp/src/test/resources/log4j.properties
deleted file mode 100644
index 9172dc9..0000000
--- a/sshd-sftp/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# 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.
-#
-#
-
-#
-# The logging properties used during tests..
-#
-log4j.rootLogger=INFO, stdout
-#log4j.logger.org.apache.sshd=TRACE
-#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
-
-# CONSOLE appender
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
-
-# File appender
-log4j.appender.out=org.apache.log4j.FileAppender
-log4j.appender.out.layout=org.apache.log4j.PatternLayout
-log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
-log4j.appender.out.file=target/servicemix-test.log
-log4j.appender.out.append=true
[12/15] mina-sshd git commit: [SSHD-378] Switch to nio FileSystem api
for commands (scp and sftp subsystem)
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemProvider.java
new file mode 100644
index 0000000..38e6181
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemProvider.java
@@ -0,0 +1,310 @@
+/*
+ * 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.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.channels.AsynchronousFileChannel;
+import java.nio.channels.FileChannel;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.AccessMode;
+import java.nio.file.CopyOption;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystemAlreadyExistsException;
+import java.nio.file.FileSystemNotFoundException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.ProviderMismatchException;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * File system provider which provides a rooted file system.
+ * The file system only gives access to files under the root directory.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RootedFileSystemProvider extends FileSystemProvider {
+
+ final Map<Path, RootedFileSystem> fileSystems = new HashMap<>();
+
+ @Override
+ public String getScheme() {
+ return "root";
+ }
+
+ @Override
+ public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
+ Path path = uriToPath(uri);
+ synchronized (fileSystems)
+ {
+ Path localPath2 = null;
+ if (ensureDirectory(path))
+ {
+ localPath2 = path.toRealPath();
+ if (this.fileSystems.containsKey(localPath2)) {
+ throw new FileSystemAlreadyExistsException();
+ }
+ }
+ RootedFileSystem rootedFs = new RootedFileSystem(this, path, env);
+ this.fileSystems.put(localPath2, rootedFs);
+ return rootedFs;
+ }
+ }
+
+ @Override
+ public FileSystem getFileSystem(URI uri) {
+ synchronized (fileSystems) {
+ RootedFileSystem fileSystem = null;
+ try {
+ fileSystem = fileSystems.get(uriToPath(uri).toRealPath());
+ } catch (IOException ignore) {
+ }
+ if (fileSystem == null) {
+ throw new FileSystemNotFoundException();
+ }
+ return fileSystem;
+ }
+ }
+
+ @Override
+ public FileSystem newFileSystem(Path path, Map<String, ?> env) throws IOException {
+ ensureDirectory(path);
+ return new RootedFileSystem(this, path, env);
+ }
+
+ protected Path uriToPath(URI uri) {
+ String scheme = uri.getScheme();
+ if ((scheme == null) || (!scheme.equalsIgnoreCase(getScheme()))) {
+ throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'");
+ }
+ try {
+ String root = uri.getRawSchemeSpecificPart();
+ int i = root.indexOf("!/");
+ if (i != -1) {
+ root = root.substring(0, i);
+ }
+ return Paths.get(new URI(root)).toAbsolutePath();
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e.getMessage(), e);
+ }
+ }
+
+ private boolean ensureDirectory(Path path) {
+ if (!Files.isDirectory(path)) {
+ throw new UnsupportedOperationException();
+ }
+ return true;
+ }
+
+ @Override
+ public Path getPath(URI uri) {
+ String str = uri.getSchemeSpecificPart();
+ int i = str.indexOf("!/");
+ if (i == -1) {
+ throw new IllegalArgumentException("URI: " + uri + " does not contain path info ex. root:file://foo/bar!/");
+ }
+ return getFileSystem(uri).getPath(str.substring(i + 1));
+ }
+
+ @Override
+ public InputStream newInputStream(Path path, OpenOption... options) throws IOException {
+ Path r = unroot(path);
+ return provider(r).newInputStream(r, options);
+ }
+
+ @Override
+ public OutputStream newOutputStream(Path path, OpenOption... options) throws IOException {
+ Path r = unroot(path);
+ return provider(r).newOutputStream(r, options);
+ }
+
+ @Override
+ public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
+ Path r = unroot(path);
+ return provider(r).newFileChannel(r, options, attrs);
+ }
+
+ @Override
+ public AsynchronousFileChannel newAsynchronousFileChannel(Path path, Set<? extends OpenOption> options, ExecutorService executor, FileAttribute<?>... attrs) throws IOException {
+ Path r = unroot(path);
+ return provider(r).newAsynchronousFileChannel(r, options, executor, attrs);
+ }
+
+ @Override
+ public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
+ Path r = unroot(path);
+ return provider(r).newByteChannel(path, options, attrs);
+ }
+
+ @Override
+ public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException {
+ Path r = unroot(dir);
+ return provider(r).newDirectoryStream(r, filter);
+ }
+
+ @Override
+ public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
+ Path r = unroot(dir);
+ provider(r).createDirectory(r, attrs);
+ }
+
+ @Override
+ public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs) throws IOException {
+ Path l = unroot(link);
+ Path t = unroot(target, false);
+ provider(l).createSymbolicLink(l, t, attrs);
+ }
+
+ @Override
+ public void createLink(Path link, Path existing) throws IOException {
+ Path l = unroot(link);
+ Path e = unroot(existing);
+ provider(l).createLink(l, e);
+ }
+
+ @Override
+ public void delete(Path path) throws IOException {
+ Path r = unroot(path);
+ provider(r).delete(r);
+ }
+
+ @Override
+ public boolean deleteIfExists(Path path) throws IOException {
+ Path r = unroot(path);
+ return provider(r).deleteIfExists(r);
+ }
+
+ @Override
+ public Path readSymbolicLink(Path link) throws IOException {
+ Path r = unroot(link);
+ return root(link.getFileSystem(), provider(r).readSymbolicLink(r));
+
+ }
+
+ @Override
+ public void copy(Path source, Path target, CopyOption... options) throws IOException {
+ Path s = unroot(source);
+ Path t = unroot(target);
+ provider(s).copy(s, t, options);
+ }
+
+ @Override
+ public void move(Path source, Path target, CopyOption... options) throws IOException {
+ Path s = unroot(source);
+ Path t = unroot(target);
+ provider(s).move(s, t, options);
+ }
+
+ @Override
+ public boolean isSameFile(Path path, Path path2) throws IOException {
+ Path r = unroot(path);
+ Path r2 = unroot(path2);
+ return provider(r).isSameFile(r, r2);
+ }
+
+ @Override
+ public boolean isHidden(Path path) throws IOException {
+ Path r = unroot(path);
+ return provider(r).isHidden(r);
+ }
+
+ @Override
+ public FileStore getFileStore(Path path) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void checkAccess(Path path, AccessMode... modes) throws IOException {
+ Path r = unroot(path);
+ provider(r).checkAccess(r, modes);
+ }
+
+ @Override
+ public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
+ Path r = unroot(path);
+ return provider(r).getFileAttributeView(r, type, options);
+ }
+
+ @Override
+ public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options) throws IOException {
+ Path r = unroot(path);
+ return provider(r).readAttributes(r, type, options);
+ }
+
+ @Override
+ public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
+ Path r = unroot(path);
+ return provider(r).readAttributes(r, attributes, options);
+ }
+
+ @Override
+ public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
+ Path r = unroot(path);
+ provider(r).setAttribute(r, attribute, value, options);
+ }
+
+ private FileSystemProvider provider(Path path) {
+ return path.getFileSystem().provider();
+ }
+
+ private Path root(FileSystem fs, Path nat) {
+ RootedFileSystem rfs = (RootedFileSystem) fs;
+ if (nat.isAbsolute()) {
+ return rfs.getPath("/" + rfs.getRoot().relativize(nat).toString());
+ } else {
+ return rfs.getPath(nat.toString());
+ }
+ }
+
+ private Path unroot(Path path) {
+ return unroot(path, true);
+ }
+
+ private Path unroot(Path path, boolean absolute) {
+ if (path == null) {
+ throw new NullPointerException();
+ }
+ if (!(path instanceof RootedPath)) {
+ throw new ProviderMismatchException();
+ }
+ RootedPath p = (RootedPath) path;
+ if (absolute || p.isAbsolute()) {
+ String r = p.toAbsolutePath().toString();
+ return p.getFileSystem().getRoot().resolve(r.substring(1));
+ } else {
+ return p.getFileName().getRoot().getFileSystem().getPath(p.toString());
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedPath.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedPath.java b/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedPath.java
new file mode 100644
index 0000000..e7be13e
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedPath.java
@@ -0,0 +1,48 @@
+/*
+ * 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.net.URI;
+import java.nio.file.LinkOption;
+
+import org.apache.sshd.common.file.util.BasePath;
+import org.apache.sshd.common.file.util.ImmutableList;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RootedPath extends BasePath<RootedPath, RootedFileSystem> {
+
+ public RootedPath(RootedFileSystem fileSystem, String root, ImmutableList<String> names) {
+ super(fileSystem, root, names);
+ }
+
+ public URI toUri() {
+ // TODO
+ return null;
+ }
+
+ public RootedPath toRealPath(LinkOption... options) throws IOException {
+ RootedPath absolute = toAbsolutePath();
+ fileSystem.provider().checkAccess(absolute);
+ return absolute;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemFactory.java
index 21d6cc7..2d74509 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemFactory.java
@@ -18,14 +18,15 @@
*/
package org.apache.sshd.common.file.virtualfs;
-import java.util.HashMap;
+import java.io.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.sshd.common.Session;
import org.apache.sshd.common.file.FileSystemFactory;
-import org.apache.sshd.common.file.FileSystemView;
-import org.apache.sshd.common.file.nativefs.NativeFileSystemView;
+import org.apache.sshd.common.file.root.RootedFileSystemProvider;
/**
* SSHd file system factory to reduce the visibility to a physical folder.
@@ -69,11 +70,9 @@ public class VirtualFileSystemFactory implements FileSystemFactory {
return homeDir;
}
- public FileSystemView createFileSystemView(Session session) {
+ public FileSystem createFileSystem(Session session) throws IOException {
String dir = computeRootDir(session.getUsername());
- Map<String, String> roots = new HashMap<String, String>();
- roots.put("/", dir);
- return new NativeFileSystemView(session.getUsername(), roots, "/", '/', false);
+ return new RootedFileSystemProvider().newFileSystem(Paths.get(dir), null);
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
index 517cb36..9827507 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
@@ -20,17 +20,26 @@ package org.apache.sshd.common.scp;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
+import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFilePermission;
import java.util.EnumSet;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.file.FileSystemView;
-import org.apache.sshd.common.file.SshFile;
import org.apache.sshd.common.util.DirectoryScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -70,22 +79,22 @@ public class ScpHelper {
public static final int S_IWOTH = 0000002;
public static final int S_IXOTH = 0000001;
- protected final FileSystemView root;
+ protected final FileSystem fileSystem;
protected final InputStream in;
protected final OutputStream out;
- public ScpHelper(InputStream in, OutputStream out, FileSystemView root) {
+ public ScpHelper(InputStream in, OutputStream out, FileSystem fileSystem) {
this.in = in;
this.out = out;
- this.root = root;
+ this.fileSystem = fileSystem;
}
- public void receive(SshFile path, boolean recursive, boolean shouldBeDir, boolean preserve, int bufferSize) throws IOException {
+ public void receive(Path path, boolean recursive, boolean shouldBeDir, boolean preserve, int bufferSize) throws IOException {
if (shouldBeDir) {
- if (!path.doesExist()) {
+ if (!Files.exists(path)) {
throw new SshException("Target directory " + path.toString() + " does not exists");
}
- if (!path.isDirectory()) {
+ if (!Files.isDirectory(path)) {
throw new SshException("Target directory " + path.toString() + " is not a directory");
}
}
@@ -136,7 +145,7 @@ public class ScpHelper {
}
- public void receiveDir(String header, SshFile path, long[] time, boolean preserve, int bufferSize) throws IOException {
+ public void receiveDir(String header, Path path, long[] time, boolean preserve, int bufferSize) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Receiving directory {}", path);
}
@@ -151,26 +160,26 @@ public class ScpHelper {
if (length != 0) {
throw new IOException("Expected 0 length for directory but got " + length);
}
- SshFile file;
- if (path.doesExist() && path.isDirectory()) {
- file = root.getFile(path, name);
- } else if (!path.doesExist() && path.getParentFile().doesExist() && path.getParentFile().isDirectory()) {
+ Path file;
+ if (Files.exists(path) && Files.isDirectory(path)) {
+ file = path.resolve(name);
+ } else if (!Files.exists(path) && Files.exists(path.getParent()) && Files.isDirectory(path.getParent())) {
file = path;
} else {
throw new IOException("Can not write to " + path);
}
- if (!(file.doesExist() && file.isDirectory()) && !file.mkdir()) {
- throw new IOException("Could not create directory " + file);
+ if (!(Files.exists(file) && Files.isDirectory(file))) {
+ Files.createDirectory(file);
}
if (preserve) {
- Map<SshFile.Attribute, Object> attrs = new HashMap<SshFile.Attribute, Object>();
- attrs.put(SshFile.Attribute.Permissions, fromOctalPerms(perms));
+ setOctalPerms(file, perms);
if (time != null) {
- attrs.put(SshFile.Attribute.LastModifiedTime, time[0]);
- attrs.put(SshFile.Attribute.LastAccessTime, time[1]);
+ Files.getFileAttributeView(file, BasicFileAttributeView.class)
+ .setTimes(FileTime.from(time[0], TimeUnit.SECONDS),
+ FileTime.from(time[1], TimeUnit.SECONDS),
+ null);
}
- file.setAttributes(attrs);
}
ack();
@@ -198,7 +207,7 @@ public class ScpHelper {
}
- public void receiveFile(String header, SshFile path, long[] time, boolean preserve, int bufferSize) throws IOException {
+ public void receiveFile(String header, Path path, long[] time, boolean preserve, int bufferSize) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Receiving file {}", path);
}
@@ -211,7 +220,7 @@ public class ScpHelper {
}
String perms = header.substring(1, 5);
- long length = Long.parseLong(header.substring(6, header.indexOf(' ', 6)));
+ final long length = Long.parseLong(header.substring(6, header.indexOf(' ', 6)));
String name = header.substring(header.indexOf(' ', 6) + 1);
if (length < 0L) { // TODO consider throwing an exception...
log.warn("receiveFile(" + path + ") bad length in header: " + header);
@@ -225,50 +234,78 @@ public class ScpHelper {
}
- SshFile file;
- if (path.doesExist() && path.isDirectory()) {
- file = root.getFile(path, name);
- } else if (path.doesExist() && path.isFile()) {
+ Path file;
+ if (Files.exists(path) && Files.isDirectory(path)) {
+ file = path.resolve(name);
+ } else if (Files.exists(path) && Files.isRegularFile(path)) {
file = path;
- } else if (!path.doesExist() && path.getParentFile().doesExist() && path.getParentFile().isDirectory()) {
+ } else if (!Files.exists(path) && Files.exists(path.getParent()) && Files.isDirectory(path.getParent())) {
file = path;
} else {
throw new IOException("Can not write to " + path);
}
- if (file.doesExist() && file.isDirectory()) {
+ if (Files.exists(file) && Files.isDirectory(file)) {
throw new IOException("File is a directory: " + file);
- } else if (file.doesExist() && !file.isWritable()) {
+ } else if (Files.exists(file) && !Files.isWritable(file)) {
throw new IOException("Can not write to file: " + file);
}
- if (file.doesExist()) {
- file.truncate();
- }
- OutputStream os = file.createOutputStream(0);
- try {
- ack();
+ InputStream is = new FilterInputStream(in) {
+ long remaining = length;
+ @Override
+ public int read() throws IOException {
+ if (remaining > 0) {
+ remaining--;
+ return super.read();
+ } else{
+ return -1;
+ }
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int nb = len;
+ if (nb > remaining) {
+ nb = (int) remaining;
+ }
+ if (nb > 0) {
+ int read = super.read(b, off, nb);
+ remaining -= read;
+ return read;
+ } else {
+ return -1;
+ }
+ }
- byte[] buffer = new byte[bufSize];
- while (length > 0) {
- int len = (int) Math.min(length, buffer.length);
- len = in.read(buffer, 0, len);
- if (len <= 0) {
- throw new IOException("End of stream reached");
+ @Override
+ public long skip(long n) throws IOException {
+ long skipped = super.skip(n);
+ remaining -= skipped;
+ return skipped;
+ }
+
+ @Override
+ public int available() throws IOException {
+ int av = super.available();
+ if (av > remaining) {
+ return (int) remaining;
+ } else {
+ return av;
}
- os.write(buffer, 0, len);
- length -= len;
}
- } finally {
- os.close();
+ };
+ try (OutputStream os = Files.newOutputStream(file)) {
+ ack();
+ copy(is, os, bufSize);
}
if (preserve) {
- Map<SshFile.Attribute, Object> attrs = new HashMap<SshFile.Attribute, Object>();
- attrs.put(SshFile.Attribute.Permissions, fromOctalPerms(perms));
+ setOctalPerms(file, perms);
if (time != null) {
- attrs.put(SshFile.Attribute.LastModifiedTime, time[0]);
- attrs.put(SshFile.Attribute.LastAccessTime, time[1]);
+ Files.getFileAttributeView(file, BasicFileAttributeView.class)
+ .setTimes(FileTime.from(time[0], TimeUnit.SECONDS),
+ FileTime.from(time[1], TimeUnit.SECONDS),
+ null);
}
- file.setAttributes(attrs);
}
ack();
@@ -309,10 +346,10 @@ public class ScpHelper {
}
String[] included = new DirectoryScanner(basedir, pattern).scan();
for (String path : included) {
- SshFile file = root.getFile(basedir + "/" + path);
- if (file.isFile()) {
+ Path file = fileSystem.getPath(basedir + "/" + path);
+ if (Files.isRegularFile(file)) {
sendFile(file, preserve, bufferSize);
- } else if (file.isDirectory()) {
+ } else if (Files.isDirectory(file)) {
if (!recursive) {
out.write(ScpHelper.WARNING);
out.write((path + " not a regular file\n").getBytes());
@@ -331,13 +368,13 @@ public class ScpHelper {
basedir = pattern.substring(0, lastSep);
pattern = pattern.substring(lastSep + 1);
}
- SshFile file = root.getFile(basedir + "/" + pattern);
- if (!file.doesExist()) {
+ Path file = fileSystem.getPath(basedir + "/" + pattern);
+ if (!Files.exists(file)) {
throw new IOException(file + ": no such file or directory");
}
- if (file.isFile()) {
+ if (Files.isRegularFile(file)) {
sendFile(file, preserve, bufferSize);
- } else if (file.isDirectory()) {
+ } else if (Files.isDirectory(file)) {
if (!recursive) {
throw new IOException(file + " not a regular file");
} else {
@@ -350,7 +387,7 @@ public class ScpHelper {
}
}
- public void sendFile(SshFile path, boolean preserve, int bufferSize) throws IOException {
+ public void sendFile(Path path, boolean preserve, int bufferSize) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Sending file {}", path);
}
@@ -359,15 +396,15 @@ public class ScpHelper {
throw new IOException("sendFile(" + path + ") buffer size (" + bufferSize + ") below minimum (" + MIN_SEND_BUFFER_SIZE + ")");
}
- Map<SshFile.Attribute,Object> attrs = path.getAttributes(true);
+ BasicFileAttributes basic = Files.getFileAttributeView(path, BasicFileAttributeView.class).readAttributes();
if (preserve) {
StringBuffer buf = new StringBuffer();
buf.append("T");
- buf.append(attrs.get(SshFile.Attribute.LastModifiedTime));
+ buf.append(basic.lastModifiedTime().to(TimeUnit.SECONDS));
buf.append(" ");
buf.append("0");
buf.append(" ");
- buf.append(attrs.get(SshFile.Attribute.LastAccessTime));
+ buf.append(basic.lastAccessTime().to(TimeUnit.SECONDS));
buf.append(" ");
buf.append("0");
buf.append("\n");
@@ -378,17 +415,17 @@ public class ScpHelper {
StringBuffer buf = new StringBuffer();
buf.append("C");
- buf.append(preserve ? toOctalPerms((EnumSet<SshFile.Permission>) attrs.get(SshFile.Attribute.Permissions)) : "0644");
+ buf.append(preserve ? getOctalPerms(path) : "0644");
buf.append(" ");
- buf.append(attrs.get(SshFile.Attribute.Size)); // length
+ buf.append(basic.size()); // length
buf.append(" ");
- buf.append(path.getName());
+ buf.append(path.getFileName().toString());
buf.append("\n");
out.write(buf.toString().getBytes());
out.flush();
readAck(false);
- long fileSize = path.getSize();
+ long fileSize = Files.size(path);
if (fileSize < 0L) { // TODO consider throwing an exception...
log.warn("sendFile(" + path + ") bad file size: " + fileSize);
}
@@ -400,36 +437,27 @@ public class ScpHelper {
bufSize = MIN_SEND_BUFFER_SIZE;
}
- InputStream is = path.createInputStream(0);
- try {
- byte[] buffer = new byte[bufSize];
- for (;;) {
- int len = is.read(buffer, 0, buffer.length);
- if (len == -1) {
- break;
- }
- out.write(buffer, 0, len);
- }
- } finally {
- is.close();
+ // TODO: use bufSize
+ try (InputStream in = Files.newInputStream(path)) {
+ copy(in, out, bufSize);
}
ack();
readAck(false);
}
- public void sendDir(SshFile path, boolean preserve, int bufferSize) throws IOException {
+ public void sendDir(Path path, boolean preserve, int bufferSize) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Sending directory {}", path);
}
- Map<SshFile.Attribute,Object> attrs = path.getAttributes(true);
+ BasicFileAttributes basic = Files.getFileAttributeView(path, BasicFileAttributeView.class).readAttributes();
if (preserve) {
StringBuffer buf = new StringBuffer();
buf.append("T");
- buf.append(attrs.get(SshFile.Attribute.LastModifiedTime));
+ buf.append(basic.lastModifiedTime().to(TimeUnit.SECONDS));
buf.append(" ");
buf.append("0");
buf.append(" ");
- buf.append(attrs.get(SshFile.Attribute.LastAccessTime));
+ buf.append(basic.lastAccessTime().to(TimeUnit.SECONDS));
buf.append(" ");
buf.append("0");
buf.append("\n");
@@ -440,21 +468,23 @@ public class ScpHelper {
StringBuffer buf = new StringBuffer();
buf.append("D");
- buf.append(preserve ? toOctalPerms((EnumSet<SshFile.Permission>) attrs.get(SshFile.Attribute.Permissions)) : "0755");
+ buf.append(preserve ? getOctalPerms(path) : "0755");
buf.append(" ");
buf.append("0"); // length
buf.append(" ");
- buf.append(path.getName());
+ buf.append(path.getFileName().toString());
buf.append("\n");
out.write(buf.toString().getBytes());
out.flush();
readAck(false);
- for (SshFile child : path.listSshFiles()) {
- if (child.isFile()) {
- sendFile(child, preserve, bufferSize);
- } else if (child.isDirectory()) {
- sendDir(child, preserve, bufferSize);
+ try (DirectoryStream<Path> children = Files.newDirectoryStream(path)) {
+ for (Path child : children) {
+ if (Files.isRegularFile(child)) {
+ sendFile(child, preserve, bufferSize);
+ } else if (Files.isDirectory(child)) {
+ sendDir(child, preserve, bufferSize);
+ }
}
}
@@ -468,55 +498,90 @@ public class ScpHelper {
return new long[] { Long.parseLong(numbers[0]), Long.parseLong(numbers[2]) };
}
- public static String toOctalPerms(EnumSet<SshFile.Permission> perms) {
+ public static String getOctalPerms(Path path) throws IOException {
int pf = 0;
- for (SshFile.Permission p : perms) {
- switch (p) {
- case UserRead: pf |= S_IRUSR; break;
- case UserWrite: pf |= S_IWUSR; break;
- case UserExecute: pf |= S_IXUSR; break;
- case GroupRead: pf |= S_IRGRP; break;
- case GroupWrite: pf |= S_IWGRP; break;
- case GroupExecute: pf |= S_IXGRP; break;
- case OthersRead: pf |= S_IROTH; break;
- case OthersWrite: pf |= S_IWOTH; break;
- case OthersExecute: pf |= S_IXOTH; break;
+ if (path.getFileSystem().supportedFileAttributeViews().contains("posix")) {
+ Set<PosixFilePermission> perms = Files.getPosixFilePermissions(path);
+ for (PosixFilePermission p : perms) {
+ switch (p) {
+ case OWNER_READ:
+ pf |= S_IRUSR;
+ break;
+ case OWNER_WRITE:
+ pf |= S_IWUSR;
+ break;
+ case OWNER_EXECUTE:
+ pf |= S_IXUSR;
+ break;
+ case GROUP_READ:
+ pf |= S_IRGRP;
+ break;
+ case GROUP_WRITE:
+ pf |= S_IWGRP;
+ break;
+ case GROUP_EXECUTE:
+ pf |= S_IXGRP;
+ break;
+ case OTHERS_READ:
+ pf |= S_IROTH;
+ break;
+ case OTHERS_WRITE:
+ pf |= S_IWOTH;
+ break;
+ case OTHERS_EXECUTE:
+ pf |= S_IXOTH;
+ break;
+ }
+ }
+ } else {
+ if (Files.isReadable(path)) {
+ pf |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ if (Files.isWritable(path)) {
+ pf |= S_IWUSR | S_IWGRP | S_IWOTH;
+ }
+ if (Files.isExecutable(path)) {
+ pf |= S_IXUSR | S_IXGRP | S_IXOTH;
}
}
return String.format("%04o", pf);
}
- public static EnumSet<SshFile.Permission> fromOctalPerms(String str) {
+ public static void setOctalPerms(Path path, String str) throws IOException {
int perms = Integer.parseInt(str, 8);
- EnumSet<SshFile.Permission> p = EnumSet.noneOf(SshFile.Permission.class);
+ EnumSet<PosixFilePermission> p = EnumSet.noneOf(PosixFilePermission.class);
if ((perms & S_IRUSR) != 0) {
- p.add(SshFile.Permission.UserRead);
+ p.add(PosixFilePermission.OWNER_READ);
}
if ((perms & S_IWUSR) != 0) {
- p.add(SshFile.Permission.UserWrite);
+ p.add(PosixFilePermission.OWNER_WRITE);
}
if ((perms & S_IXUSR) != 0) {
- p.add(SshFile.Permission.UserExecute);
+ p.add(PosixFilePermission.OWNER_EXECUTE);
}
if ((perms & S_IRGRP) != 0) {
- p.add(SshFile.Permission.GroupRead);
+ p.add(PosixFilePermission.GROUP_READ);
}
if ((perms & S_IWGRP) != 0) {
- p.add(SshFile.Permission.GroupWrite);
+ p.add(PosixFilePermission.GROUP_WRITE);
}
if ((perms & S_IXGRP) != 0) {
- p.add(SshFile.Permission.GroupExecute);
+ p.add(PosixFilePermission.GROUP_EXECUTE);
}
if ((perms & S_IROTH) != 0) {
- p.add(SshFile.Permission.OthersRead);
+ p.add(PosixFilePermission.OTHERS_READ);
}
if ((perms & S_IWOTH) != 0) {
- p.add(SshFile.Permission.OthersWrite);
+ p.add(PosixFilePermission.OTHERS_WRITE);
}
if ((perms & S_IXOTH) != 0) {
- p.add(SshFile.Permission.OthersExecute);
+ p.add(PosixFilePermission.OTHERS_EXECUTE);
+ }
+ if (path.getFileSystem().supportedFileAttributeViews().contains("posix")) {
+ Files.setPosixFilePermissions(path, p);
+ } else {
+ log.warn("Unable to set file permissions because the underlying file system does not support posix permissions");
}
- return p;
}
public void ack() throws IOException {
@@ -545,4 +610,17 @@ public class ScpHelper {
return c;
}
+ private static long copy(InputStream source, OutputStream sink, int bufferSize)
+ throws IOException
+ {
+ long nread = 0L;
+ byte[] buf = new byte[bufferSize];
+ int n;
+ while ((n = source.read(buf)) > 0) {
+ sink.write(buf, 0, n);
+ nread += n;
+ }
+ return nread;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
index ab10ef4..ac4bab2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
@@ -492,7 +492,7 @@ public class ChannelSession extends AbstractServerChannel {
// If the shell wants to be aware of the file system, let's do that too
if (command instanceof FileSystemAware) {
FileSystemFactory factory = ((ServerSession) session).getFactoryManager().getFileSystemFactory();
- ((FileSystemAware) command).setFileSystemView(factory.createFileSystemView(session));
+ ((FileSystemAware) command).setFileSystem(factory.createFileSystem(session));
}
// If the shell wants to use non-blocking io
if (command instanceof AsyncCommand) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java b/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
index 68dc9ef..d68e0b9 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
@@ -25,9 +25,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
+import java.nio.file.FileSystem;
import org.apache.sshd.common.file.FileSystemAware;
-import org.apache.sshd.common.file.FileSystemView;
import org.apache.sshd.common.scp.ScpHelper;
import org.apache.sshd.common.util.ThreadUtils;
import org.apache.sshd.server.Command;
@@ -53,7 +53,7 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
protected boolean optF;
protected boolean optD;
protected boolean optP; // TODO: handle modification times
- protected FileSystemView root;
+ protected FileSystem fileSystem;
protected String path;
protected InputStream in;
protected OutputStream out;
@@ -237,8 +237,8 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
this.callback = callback;
}
- public void setFileSystemView(FileSystemView view) {
- this.root = view;
+ public void setFileSystem(FileSystem fs) {
+ this.fileSystem = fs;
}
public void start(Environment env) throws IOException {
@@ -274,15 +274,23 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
}
executors = null;
+
+ try {
+ fileSystem.close();
+ } catch (UnsupportedOperationException e) {
+ // Ignore
+ } catch (IOException e) {
+ log.debug("Error closing FileSystem", e);
+ }
}
public void run() {
int exitValue = ScpHelper.OK;
String exitMessage = null;
- ScpHelper helper = new ScpHelper(in, out, root);
+ ScpHelper helper = new ScpHelper(in, out, fileSystem);
try {
if (optT) {
- helper.receive(root.getFile(path), optR, optD, optP, receiveBufferSize);
+ helper.receive(fileSystem.getPath(path), optR, optD, optP, receiveBufferSize);
} else if (optF) {
helper.send(Collections.singletonList(path), optR, optP, sendBufferSize);
} else {
[03/15] mina-sshd git commit: Upgrade to 1.0.0-SNAPSHOT and remove
unsupported modules
Posted by gn...@apache.org.
Upgrade to 1.0.0-SNAPSHOT and remove unsupported modules
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/80d8a51a
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/80d8a51a
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/80d8a51a
Branch: refs/heads/master
Commit: 80d8a51afe356e3ebc91676a1ccba4fd845a24b4
Parents: 964e768
Author: Guillaume Nodet <gn...@apache.org>
Authored: Mon Feb 23 16:20:07 2015 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Mon Feb 23 16:20:07 2015 +0100
----------------------------------------------------------------------
assembly/pom.xml | 4 +-
pom.xml | 2 +-
sshd-core/pom.xml | 4 +-
sshd-git/pom.xml | 4 +-
sshd-pam/pom.xml | 52 --
.../server/pam/PAMPasswordAuthenticator.java | 58 --
sshd-sftp/pom.xml | 82 --
.../main/java/org/apache/sshd/sftp/Handle.java | 36 -
.../main/java/org/apache/sshd/sftp/Reply.java | 43 -
.../main/java/org/apache/sshd/sftp/Request.java | 50 --
.../java/org/apache/sshd/sftp/SftpSession.java | 36 -
.../main/java/org/apache/sshd/sftp/Sftplet.java | 74 --
.../org/apache/sshd/sftp/reply/BaseReply.java | 53 --
.../apache/sshd/sftp/reply/FileAttributes.java | 166 ----
.../sshd/sftp/reply/SshFxpAttrsReply.java | 66 --
.../apache/sshd/sftp/reply/SshFxpDataReply.java | 96 ---
.../sshd/sftp/reply/SshFxpHandleReply.java | 66 --
.../apache/sshd/sftp/reply/SshFxpNameReply.java | 150 ----
.../sshd/sftp/reply/SshFxpStatusReply.java | 164 ----
.../sshd/sftp/reply/SshFxpVersionReply.java | 61 --
.../apache/sshd/sftp/request/BaseRequest.java | 58 --
.../sshd/sftp/request/SshFxpCloseRequest.java | 83 --
.../sftp/request/SshFxpFsetstatRequest.java | 52 --
.../sshd/sftp/request/SshFxpFstatRequest.java | 74 --
.../sshd/sftp/request/SshFxpInitRequest.java | 51 --
.../sshd/sftp/request/SshFxpLstatRequest.java | 72 --
.../sshd/sftp/request/SshFxpMkdirRequest.java | 65 --
.../sshd/sftp/request/SshFxpOpenRequest.java | 99 ---
.../sshd/sftp/request/SshFxpOpendirRequest.java | 64 --
.../sshd/sftp/request/SshFxpReadRequest.java | 109 ---
.../sshd/sftp/request/SshFxpReaddirRequest.java | 74 --
.../sftp/request/SshFxpRealpathRequest.java | 78 --
.../sshd/sftp/request/SshFxpRemoveRequest.java | 64 --
.../sshd/sftp/request/SshFxpRenameRequest.java | 76 --
.../sshd/sftp/request/SshFxpRmdirRequest.java | 64 --
.../sshd/sftp/request/SshFxpSetstatRequest.java | 52 --
.../sshd/sftp/request/SshFxpStatRequest.java | 71 --
.../sshd/sftp/request/SshFxpWriteRequest.java | 110 ---
.../sshd/sftp/request/UnsupportedRequest.java | 56 --
.../apache/sshd/sftp/subsystem/BaseHandle.java | 48 --
.../sftp/subsystem/DefaultSftpletContainer.java | 90 ---
.../sshd/sftp/subsystem/DirectoryHandle.java | 65 --
.../apache/sshd/sftp/subsystem/FileHandle.java | 86 --
.../apache/sshd/sftp/subsystem/Serializer.java | 535 -------------
.../sshd/sftp/subsystem/SftpConstants.java | 305 -------
.../sshd/sftp/subsystem/SftpSubsystem.java | 786 -------------------
.../java/org/apache/sshd/sftp/SftpTest.java | 263 -------
.../sftp/util/BogusPasswordAuthenticator.java | 34 -
.../apache/sshd/sftp/util/EchoShellFactory.java | 111 ---
.../org/apache/sshd/sftp/util/JSchLogger.java | 56 --
.../apache/sshd/sftp/util/SimpleUserInfo.java | 63 --
.../java/org/apache/sshd/sftp/util/Utils.java | 58 --
sshd-sftp/src/test/resources/hostkey.pem | 30 -
sshd-sftp/src/test/resources/log4j.properties | 38 -
54 files changed, 7 insertions(+), 5200 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/assembly/pom.xml
----------------------------------------------------------------------
diff --git a/assembly/pom.xml b/assembly/pom.xml
index 7ebd56e..aa24ba0 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd</artifactId>
- <version>0.13.1-SNAPSHOT</version>
+ <version>1.0.0-SNAPSHOT</version>
</parent>
<groupId>org.apache.sshd</groupId>
<artifactId>apache-sshd</artifactId>
- <version>0.13.1-SNAPSHOT</version>
+ <version>1.0.0-SNAPSHOT</version>
<name>Apache Mina SSHD :: Assembly</name>
<packaging>pom</packaging>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index ca326d2..063f47e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@
<groupId>org.apache.sshd</groupId>
<artifactId>sshd</artifactId>
- <version>0.13.1-SNAPSHOT</version>
+ <version>1.0.0-SNAPSHOT</version>
<name>Apache Mina SSHD</name>
<packaging>pom</packaging>
<inceptionYear>2008</inceptionYear>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-core/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-core/pom.xml b/sshd-core/pom.xml
index 15753b7..d8ff2a1 100644
--- a/sshd-core/pom.xml
+++ b/sshd-core/pom.xml
@@ -24,11 +24,11 @@
<parent>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd</artifactId>
- <version>0.13.1-SNAPSHOT</version>
+ <version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>sshd-core</artifactId>
- <version>0.13.1-SNAPSHOT</version>
+ <version>1.0.0-SNAPSHOT</version>
<name>Apache Mina SSHD :: Core</name>
<packaging>jar</packaging>
<inceptionYear>2008</inceptionYear>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-git/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-git/pom.xml b/sshd-git/pom.xml
index 8cdebdd..c1c1f5b 100644
--- a/sshd-git/pom.xml
+++ b/sshd-git/pom.xml
@@ -24,11 +24,11 @@
<parent>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd</artifactId>
- <version>0.13.1-SNAPSHOT</version>
+ <version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>sshd-git</artifactId>
- <version>0.13.1-SNAPSHOT</version>
+ <version>1.0.0-SNAPSHOT</version>
<name>Apache Mina SSHD :: Git</name>
<packaging>jar</packaging>
<inceptionYear>2008</inceptionYear>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-pam/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-pam/pom.xml b/sshd-pam/pom.xml
deleted file mode 100644
index 15ff731..0000000
--- a/sshd-pam/pom.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-
- <!--
-
- 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.
- -->
-
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd</artifactId>
- <version>0.13.0-SNAPSHOT</version>
- </parent>
-
- <artifactId>sshd-pam</artifactId>
- <version>0.13.0-SNAPSHOT</version>
- <name>Apache Mina SSHD :: PAM</name>
- <!--
- <packaging>bundle</packaging>
- -->
-
- <properties>
- <projectRoot>${basedir}/..</projectRoot>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-core</artifactId>
- </dependency>
- <dependency>
- <groupId>net.sf.jpam</groupId>
- <artifactId>jpam</artifactId>
- </dependency>
- </dependencies>
-
-</project>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-pam/src/main/java/org/apache/sshd/server/pam/PAMPasswordAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-pam/src/main/java/org/apache/sshd/server/pam/PAMPasswordAuthenticator.java b/sshd-pam/src/main/java/org/apache/sshd/server/pam/PAMPasswordAuthenticator.java
deleted file mode 100644
index a63c6a1..0000000
--- a/sshd-pam/src/main/java/org/apache/sshd/server/pam/PAMPasswordAuthenticator.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.server.pam;
-
-import net.sf.jpam.Pam;
-import net.sf.jpam.PamReturnValue;
-import org.apache.sshd.server.PasswordAuthenticator;
-import org.apache.sshd.server.session.ServerSession;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A password authenticator using PAM (Pluggable Authentication Module).
- * Such an authenticator can be used to integrate into an Unix operating
- * system.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class PAMPasswordAuthenticator implements PasswordAuthenticator {
-
- private static final Logger LOG = LoggerFactory.getLogger(PAMPasswordAuthenticator.class);
-
- private String service = "sshd";
-
- public String getService() {
- return service;
- }
-
- public void setService(String service) {
- this.service = service;
- }
-
- public boolean authenticate(String username, String password, ServerSession session) {
- LOG.info("Authenticating user {} using PAM", username);
- PamReturnValue val = new Pam(service).authenticate(username, password);
- LOG.info("Result: {}", val);
- if (PamReturnValue.PAM_SUCCESS.equals(val)) {
- return true;
- }
- return false;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-sftp/pom.xml b/sshd-sftp/pom.xml
deleted file mode 100644
index 50cb5b4..0000000
--- a/sshd-sftp/pom.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-
- <!--
-
- 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.
- -->
-
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd</artifactId>
- <version>0.13.0-SNAPSHOT</version>
- </parent>
-
- <artifactId>sshd-sftp</artifactId>
- <version>0.13.0-SNAPSHOT</version>
- <name>Apache Mina SSHD :: SFTP</name>
- <packaging>jar</packaging>
- <inceptionYear>2008</inceptionYear>
-
- <properties>
- <projectRoot>${basedir}/..</projectRoot>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-core</artifactId>
- </dependency>
-
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.jcraft</groupId>
- <artifactId>jsch</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk15on</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk15on</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <resources>
- <resource>
- <directory>src/main/filtered-resources</directory>
- <filtering>true</filtering>
- </resource>
- </resources>
- </build>
-
-</project>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/Handle.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/Handle.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/Handle.java
deleted file mode 100644
index c2a726d..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/Handle.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
-* 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.sftp;
-
-import org.apache.sshd.common.file.SshFile;
-
-import java.io.Closeable;
-
-/**
- * A sftp handle.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public interface Handle extends Closeable {
-
- String getId();
-
- SshFile getFile();
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/Reply.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/Reply.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/Reply.java
deleted file mode 100644
index 98f9c87..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/Reply.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-* 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.sftp;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Common ssh reply interface.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public interface Reply {
-
- /**
- * Returns the reply message type.
- *
- * @return the message type
- */
- SftpConstants.Type getMessage();
-
- /**
- * Returns the name of the reply code.
- *
- * @return The name of the reply code.
- */
- String getName();
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/Request.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/Request.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/Request.java
deleted file mode 100644
index 7803a8f..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/Request.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
-* 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.sftp;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Common ssh request interface.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public interface Request {
-
- /**
- * Returns the request id.
- *
- * @return The request id.
- */
- int getId();
-
- /**
- * Returns the message type.
- *
- * @return The message type.
- */
- SftpConstants.Type getMessage();
-
- /**
- * Returns the request name.
- *
- * @return The request name.
- */
- String getName();
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/SftpSession.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/SftpSession.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/SftpSession.java
deleted file mode 100644
index a6956db..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/SftpSession.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.sftp;
-
-import org.apache.sshd.common.Session;
-import org.apache.sshd.common.file.SshFile;
-
-public interface SftpSession {
-
- int getVersion();
-
- Session getSession();
-
- Handle getHandle(String id);
-
- Handle createFileHandle(SshFile file, int flags);
-
- Handle createDirectoryHandle(SshFile file);
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/Sftplet.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/Sftplet.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/Sftplet.java
deleted file mode 100644
index 6cd4542..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/Sftplet.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-* 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.sftp;
-
-import org.apache.sshd.common.Session;
-
-import java.io.IOException;
-
-
-
-/**
- * Similar to org.apache.ftpserver.ftplet.Ftplet.
- * For custom command handling. For example adopting monoring tools, individual loggings, event processing,
- * customizing command processing.
- * There should be a ServerSession interface to avoid an implementation dependency.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public interface Sftplet {
- /**
- * Client connect notification method.
- *
- * @param session The according session.
- */
- void onConnect(SftpSession session);
-
- /**
- * Client disconnect notification method.
- *
- * @param session The according session.
- */
- void onDisconnect(SftpSession session);
-
- /**
- * Called before the server invoke the command.
- *
- * @param session The according session.
- * @param sftpRequest The sftp request.
- *
- * @return If null, the standard processing goes on.
- * If not null, there will be no further processing and this reply will be returned to client.
- */
- Reply beforeCommand(SftpSession session, Request sftpRequest);
-
- /**
- * Called after the server as invoked the command.
- *
- * @param session The according session.
- * @param sftpRequest The sftp request.
- * @param sftpReply The sftp reply.
- *
- * @return If null, default sftp reply will be used.
- * If not null, there will be no further processing and this reply will be returned to client.
- *
- * @throws IOException If an error occured.
- */
- Reply afterCommand(SftpSession session, Request sftpRequest, Reply sftpReply) throws IOException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/BaseReply.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/BaseReply.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/BaseReply.java
deleted file mode 100644
index 3cbea00..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/BaseReply.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.sftp.reply;
-
-import org.apache.sshd.sftp.Reply;
-
-public abstract class BaseReply implements Reply {
-
- private final int id;
-
- /**
- * Creates a Request instance.
- *
- * @param id The request id.
- */
- public BaseReply(final int id) {
- this.id = id;
- }
-
- /**
- * Returns the request id.
- *
- * @return The request id.
- */
- public int getId() {
- return id;
- }
-
- /**
- * Returns the request name.
- *
- * @return The request name.
- */
- public String getName() {
- return getMessage().toString();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/FileAttributes.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/FileAttributes.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/FileAttributes.java
deleted file mode 100644
index 16c0f52..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/FileAttributes.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
-* 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.sftp.reply;
-
-import org.apache.sshd.common.file.SshFile;
-
-import static org.apache.sshd.sftp.subsystem.SftpConstants.*;
-
-/**
- * Data container for file attributes in relies.
- * TODO: implement
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class FileAttributes {
-
- int flags = 0;
- byte type;
- long size;
- long allocationSize;
- String owner;
- String group;
- int permissions;
- long accessTime;
- int accessTimeNanos;
- long modifyTime;
- int modifyTimeNanos;
- long createTime;
- int createTimeNanos;
- String acl;
- int attribBits;
- int attribBitsValid;
- byte textHint;
- String mimeType;
- int linkCount;
- String untranslatedName;
- int extended;
-
- public FileAttributes() {
- }
-
- public FileAttributes(SshFile file, int flags) {
- // Type
- if (file.isFile()) {
- setType((byte) SSH_FILEXFER_TYPE_REGULAR);
- } else if (file.isDirectory()) {
- setType((byte) SSH_FILEXFER_TYPE_DIRECTORY);
- } else {
- setType((byte) SSH_FILEXFER_TYPE_UNKNOWN);
- }
- // Size
- if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
- setSize(file.getSize());
- }
- // Permissions
- if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
- setPermissions((file.isReadable() ? S_IRUSR : 0) | (file.isWritable() ? S_IWUSR : 0) | (file.isExecutable() ? S_IXUSR : 0));
- }
- // Times
- if ((flags & SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
- setAccessTime(file.getLastModified() / 1000);
- }
- if ((flags & SSH_FILEXFER_ATTR_CREATETIME) != 0) {
- setCreateTime(file.getLastModified() / 1000);
- }
- if ((flags & SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
- setModifyTime(file.getLastModified() / 1000);
- }
- }
-
- public int getFlags() {
- return flags;
- }
-
- public byte getType() {
- return type;
- }
-
- public void setType(byte type) {
- this.type = type;
- }
-
- public long getSize() {
- return size;
- }
-
- public void setSize(long size) {
- this.flags |= SSH_FILEXFER_ATTR_SIZE;
- this.size = size;
- }
-
- public long getAllocationSize() {
- return allocationSize;
- }
-
- public void setAllocationSize(long allocationSize) {
- this.flags |= SSH_FILEXFER_ATTR_ALLOCATION_SIZE;
- this.allocationSize = allocationSize;
- }
-
- public String getOwner() {
- return owner;
- }
-
- public String getGroup() {
- return group;
- }
-
- public void setOwnerGroup(String owner, String group) {
- this.flags |= SSH_FILEXFER_ATTR_OWNERGROUP;
- this.owner = owner;
- this.group = group;
- }
-
- public int getPermissions() {
- return permissions;
- }
-
- public void setPermissions(int permissions) {
- this.flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
- this.permissions = permissions;
- }
-
- public long getAccessTime() {
- return accessTime;
- }
-
- public void setAccessTime(long accessTime) {
- this.flags |= SSH_FILEXFER_ATTR_ACCESSTIME;
- this.accessTime = accessTime;
- }
-
- public long getModifyTime() {
- return modifyTime;
- }
-
- public void setModifyTime(long modifyTime) {
- this.flags |= SSH_FILEXFER_ATTR_MODIFYTIME;
- this.modifyTime = modifyTime;
- }
-
- public long getCreateTime() {
- return createTime;
- }
-
- public void setCreateTime(long createTime) {
- this.flags |= SSH_FILEXFER_ATTR_CREATETIME;
- this.createTime = createTime;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpAttrsReply.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpAttrsReply.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpAttrsReply.java
deleted file mode 100644
index e825cd0..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpAttrsReply.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-* 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.sftp.reply;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_ATTRS' reply.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpAttrsReply extends BaseReply {
-
- private final FileAttributes attrs;
-
- /**
- * Creates a SshFxpAttrsReply instance.
- *
- * @param id The reply id.
- * @param attrs The attributes.
- */
- public SshFxpAttrsReply(final int id, final FileAttributes attrs) {
- super(id);
- this.attrs = attrs;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_ATTRS;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[attrs=" + attrs + "]";
- }
-
- /**
- * Returns the attributes.
- *
- * @return the attributes.
- */
- public FileAttributes getAttributes() {
- return attrs;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpDataReply.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpDataReply.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpDataReply.java
deleted file mode 100644
index 7a02d4e..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpDataReply.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
-* 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.sftp.reply;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_DATA' reply.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpDataReply extends BaseReply {
-
- private final byte[] data;
- private final int offset;
- private final int length;
- private final boolean eof;
-
- /**
- * Creates a SshFxpData instance.
- *
- * @param id The reply id.
- * @param data The transfer data.
- */
- public SshFxpDataReply(final int id, final byte[] data) {
- this(id, data, 0, data.length, false);
- }
-
- /**
- * Creates a SshFxpData instance.
- *
- * @param id The reply id.
- * @param data The transfer data.
- * @param offset The offset in the data.
- * @param length The length of data.
- * @param eof The EOF flag.
- */
- public SshFxpDataReply(final int id, final byte[] data, final int offset, final int length, final boolean eof) {
- super(id);
- this.data = data;
- this.offset = offset;
- this.length = length;
- this.eof = eof;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_DATA;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[data=<data(len=" + length + ")>, eof=" + eof + "]";
- }
-
- /**
- * Returns the data.
- *
- * @return The data.
- */
- public byte[] getData() {
- return data;
- }
-
- public int getOffset() {
- return offset;
- }
-
- public int getLength() {
- return length;
- }
-
- public boolean isEof() {
- return eof;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpHandleReply.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpHandleReply.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpHandleReply.java
deleted file mode 100644
index 266ebf7..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpHandleReply.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-* 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.sftp.reply;
-
-import org.apache.sshd.sftp.Handle;
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_HANDLE' reply.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpHandleReply extends BaseReply {
-
- private final Handle handle;
-
- /**
- * Creates a SshFxpHandleReply instance.
- *
- * @param id The reply id.
- * @param handle The handle.
- */
- public SshFxpHandleReply(final int id, final Handle handle) {
- super(id);
- this.handle = handle;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_HANDLE;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[handle=" + handle.getId() + ", file=" + handle.getFile().getAbsolutePath();
- }
-
- /**
- * Returns the handle.
- *
- * @return The handle.
- */
- public Handle getHandle() {
- return handle;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpNameReply.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpNameReply.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpNameReply.java
deleted file mode 100644
index 17e4efe..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpNameReply.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
-* 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.sftp.reply;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.sshd.common.file.SshFile;
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_NAME' reply.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpNameReply extends BaseReply {
- /**
- * Contains informations of requested files.
- */
- public static class ReplyFile {
-
- private final SshFile file;
- private final String fileName;
- private final String longName;
- private final FileAttributes attrs;
-
- /**
- * Creates ReplyFile instance.
- *
- * @param fileName The file name.
- * @param longName The virtual absolute file path.
- * @param attrs File attributes.
- */
- public ReplyFile(final SshFile file, final String fileName, final String longName, final FileAttributes attrs) {
- this.file = file;
- this.fileName = fileName;
- this.longName = longName;
- this.attrs = attrs;
- }
-
- public SshFile getFile() {
- return file;
- }
-
- public String getFileName() {
- return fileName;
- }
-
- public String getLongName() {
- return longName;
- }
-
- /**
- * Returns the file attributes.
- *
- * @return The file attributes.
- */
- public FileAttributes getAttrs() {
- return attrs;
- }
-
- public String toString() {
- return "fileName=" + fileName + ", longName=" + longName;
- }
-
- }
-
- private List<ReplyFile> files = new ArrayList<ReplyFile>();
- private boolean eol;
-
- /**
- * Creates a SshFxpHandleReply instance.
- *
- * @param id The reply id.
- */
- public SshFxpNameReply(final int id) {
- super(id);
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_NAME;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- StringBuffer fs = new StringBuffer();
- fs.append(getName());
- fs.append("[");
- fs.append("\n");
- for (ReplyFile f : files) {
- fs.append(" ");
- fs.append(f.toString());
- fs.append(",\n");
- }
- fs.append("]");
-
- return fs.toString();
- }
-
- /**
- * Add a file to the reply.
- *
- * @param sshFile The ssh file.
- * @param filename The file name.
- * @param longname The long file message.
- * @param attrs The file attributes.
- */
- public void addFile(final SshFile sshFile, final String filename, final String longname, final FileAttributes attrs) {
- files.add(new ReplyFile(sshFile, filename, longname, attrs));
- }
-
- /**
- * Returns the files.
- *
- * @return the files.
- */
- public Collection<ReplyFile> getFiles() {
- return files;
- }
-
- public boolean isEol() {
- return eol;
- }
-
- public void setEol(boolean eol) {
- this.eol = eol;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpStatusReply.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpStatusReply.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpStatusReply.java
deleted file mode 100644
index 9722480..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpStatusReply.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
-* 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.sftp.reply;
-
-import java.io.IOException;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-import static org.apache.sshd.sftp.subsystem.SftpConstants.*;
-
-/**
- * Data container for 'SSH_FXP_STATUS' reply.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpStatusReply extends BaseReply {
-
- private String substatusAsSTR;
- private String msg;
- private String lang;
- private final int substatus;
-
- /**
- * Creates a SshFxpStatusReply instance.
- *
- * @param id The reply id.
- * @param substatus The sub status.
- * @param msg The status message.
- *
- * @throws IOException If the given reply is unsupported.
- */
- public SshFxpStatusReply(final int id, final int substatus, final String msg)
- throws IOException {
- this(id, substatus, msg, "");
- }
-
- /**
- * Creates a SshFxpStatusReply instance.
- *
- * @param id The reply id.
- * @param substatus The sub status.
- * @param msg The status message.
- * @param lang The lang status message.
- *
- * @throws IOException If the given reply is unsupported.
- */
- public SshFxpStatusReply(final int id, final int substatus, final String msg, final String lang)
- throws IOException {
- super(id);
- this.substatus = substatus;
- this.lang = lang;
- this.msg = msg;
-
- switch (substatus) {
- case SSH_FX_FILE_ALREADY_EXISTS:
- substatusAsSTR = "SSH_FX_FILE_ALREADY_EXISTS";
- break;
- case SSH_FX_DIR_NOT_EMPTY:
- substatusAsSTR = "SSH_FX_DIR_NOT_EMPTY";
- break;
- case SSH_FX_EOF:
- substatusAsSTR = "SSH_FX_EOF";
- break;
- case SSH_FX_FILE_IS_A_DIRECTORY:
- substatusAsSTR = "SSH_FX_FILE_IS_A_DIRECTORY";
- break;
- case SSH_FX_INVALID_HANDLE:
- substatusAsSTR = "SSH_FX_INVALID_HANDLE";
- break;
- case SSH_FX_NO_SUCH_FILE:
- substatusAsSTR = "SSH_FX_NO_SUCH_FILE";
- break;
- case SSH_FX_NO_SUCH_PATH:
- substatusAsSTR = "SSH_FX_NO_SUCH_PATH";
- break;
- case SSH_FX_NOT_A_DIRECTORY:
- substatusAsSTR = "SSH_FX_NOT_A_DIRECTORY";
- break;
- case SSH_FX_OK:
- substatusAsSTR = "SSH_FX_OK";
- break;
- case SSH_FX_OP_UNSUPPORTED:
- substatusAsSTR = "SSH_FX_OP_UNSUPPORTED";
- break;
- case SSH_FX_FAILURE:
- substatusAsSTR = "SSH_FX_FAILURE";
- break;
- case SSH_FX_PERMISSION_DENIED:
- substatusAsSTR = "SSH_FX_PERMISSION_DENIED";
- break;
- case SSH_FXP_MKDIR:
- substatusAsSTR = "SSH_FXP_MKDIR";
- break;
- case SSH_FXP_REMOVE:
- substatusAsSTR = "SSH_FXP_REMOVE";
- break;
- case SSH_FXP_RMDIR:
- substatusAsSTR = "SSH_FXP_RMDIR";
- break;
- case SSH_FX_WRITE_PROTECT:
- substatusAsSTR = "SSH_FX_WRITE_PROTECT";
- break;
- default:
- throw new IOException("Internal error - unexpected substatus: " + substatus);
- }
- }
-
- /**
- * Returns the substatus.
- *
- * @return The substatus.
- */
- public int getSubstatus() {
- return substatus;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[status=" + substatusAsSTR + ", msg=" + msg + "]";
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_STATUS;
- }
-
- /**
- * Returns the status message.
- *
- * @return The status message.
- */
- public String getMsg() {
- return msg;
- }
-
- /**
- * Returns the long status message.
- *
- * @return The long status message.
- */
- public String getLang() {
- return lang;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpVersionReply.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpVersionReply.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpVersionReply.java
deleted file mode 100644
index c744942..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpVersionReply.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
-* 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.sftp.reply;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_VERSION' reply.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpVersionReply extends BaseReply {
-
- /**
- * Creates a SshFxpVersionReply instance.
- *
- * @param version The requested version.
- */
- public SshFxpVersionReply(final int version) {
- super(version);
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_VERSION;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[version=" + getId() + "]";
- }
-
- /**
- * Returns the requested version.
- *
- * @return The requested version.
- */
- public int getVersion() {
- return getId();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/BaseRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/BaseRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/BaseRequest.java
deleted file mode 100644
index 2e2cfc9..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/BaseRequest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.Request;
-
-/**
- * Common ssh request interface.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public abstract class BaseRequest implements Request {
-
- private final int id;
-
- /**
- * Creates a Request instance.
- *
- * @param id The request id.
- */
- public BaseRequest(final int id) {
- this.id = id;
- }
-
- /**
- * Returns the request id.
- *
- * @return The request id.
- */
- public int getId() {
- return id;
- }
-
- /**
- * Returns the request name.
- *
- * @return The request name.
- */
- public String getName() {
- return getMessage().toString();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpCloseRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpCloseRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpCloseRequest.java
deleted file mode 100644
index 8ebfd35..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpCloseRequest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.Handle;
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_CLOSE' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpCloseRequest extends BaseRequest {
- private final Handle handle;
- private final String handleId;
-
- /**
- * Create SshFxpCloseRequest instance.
- *
- * @param id The request id.
- * @param handleId The according handle id.
- * @param handle The according file handle.
- */
- public SshFxpCloseRequest(final int id, final String handleId, final Handle handle) {
- super(id);
- this.handleId = handleId;
- this.handle = handle;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_CLOSE;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- String sh;
- if (handle != null && handle.getFile() != null) {
- sh = handle.getFile().getAbsolutePath();
- } else {
- sh = "";
- }
- return getName() + "[handle=" + handleId + ", file=" + sh + "]";
- }
-
- /**
- * Returns the according handle.
- *
- * @return The according handle.
- */
- public Handle getHandle() {
- return handle;
- }
-
- /**
- * Returns the handle id.
- *
- * @return The handle id.
- */
- public String getHandleId() {
- return handleId;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpFsetstatRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpFsetstatRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpFsetstatRequest.java
deleted file mode 100644
index a19c08c..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpFsetstatRequest.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_FSETSTAT' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpFsetstatRequest extends BaseRequest {
-
- /**
- * Creates a SshFxpFsetstatRequest instance.
- *
- * @param id The request id.
- */
- public SshFxpFsetstatRequest(final int id) {
- super(id);
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_SETSTAT;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[]";
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpFstatRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpFstatRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpFstatRequest.java
deleted file mode 100644
index 6dbfba4..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpFstatRequest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.Handle;
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_FSTAT' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpFstatRequest extends BaseRequest {
- private final String handle;
- private final Handle handleRef;
-
- /**
- * Creates a SshFxpFstatRequest instance.
- *
- * @param id The request id.
- * @param handle The handle.
- * @param handleRef The handle reference.
- */
- public SshFxpFstatRequest(final int id, final String handle, final Handle handleRef) {
- super(id);
- this.handle = handle;
- this.handleRef = handleRef;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_FSTAT;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- String ps;
- if (handleRef != null && handleRef.getFile() != null) {
- ps = handleRef.getFile().getAbsolutePath();
- } else {
- ps = "";
- }
- return getName() + "[handle=" + handle + ", file=" + ps + "]";
- }
-
- /**
- * Returns the handle id.
- *
- * @return The handle id.
- */
- public String getHandleId() {
- return handle;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpInitRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpInitRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpInitRequest.java
deleted file mode 100644
index be54767..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpInitRequest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_INIT' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpInitRequest extends BaseRequest {
- /**
- * Creates a SshFxpInitRequest instance.
- *
- * @param id The request id.
- */
- public SshFxpInitRequest(final int id) {
- super(id);
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_INIT;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[version=" + getId() + "]";
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpLstatRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpLstatRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpLstatRequest.java
deleted file mode 100644
index 9251063..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpLstatRequest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_STAT' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpLstatRequest extends BaseRequest {
-
- private final String path;
- private final int flags;
-
- /**
- * Creates a SshFxpLstatRequest instance.
- *
- * @param id The request id.
- * @param path The requested path.
- * @param flags The stat flags.
- */
- public SshFxpLstatRequest(final int id, final String path, final int flags) {
- super(id);
- this.path = path;
- this.flags = flags;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_STAT;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[path=" + path + "]";
- }
-
- /**
- * Returns the path.
- *
- * @return The path.
- */
- public String getPath() {
- return path;
- }
-
- public int getFlags() {
- return flags;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpMkdirRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpMkdirRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpMkdirRequest.java
deleted file mode 100644
index 8c04e49..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpMkdirRequest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_MKDIR' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpMkdirRequest extends BaseRequest {
-
- private final String path;
-
- /**
- * Creates a SshFxpMkdirRequest instance.
- *
- * @param id The request id.
- * @param path The path to create.
- */
- public SshFxpMkdirRequest(final int id, final String path) {
- super(id);
- this.path = path;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_MKDIR;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[path=" + path + "]";
- }
-
- /**
- * Returns the path.
- *
- * @return The path.
- */
- public String getPath() {
- return path;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpOpenRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpOpenRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpOpenRequest.java
deleted file mode 100644
index 9dec41e..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpOpenRequest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_OPEN' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpOpenRequest extends BaseRequest {
- private final String path;
- private final int acc;
- private final int flags;
-
- /**
- * Creates a SshFxpOpenRequest instance.
- *
- * @param id The request id.
- * @param path The path.
- * @param flags The flags.
- */
- public SshFxpOpenRequest(final int id, String path, final int flags) {
- this(id, path, 0, flags);
- }
-
- /**
- * Creates a SshFxpOpenRequest instance.
- *
- * @param id The request id.
- * @param path The path.
- * @param acc The acc.
- * @param flags The flags.
- */
- public SshFxpOpenRequest(final int id, String path, final int acc, final int flags) {
- super(id);
- this.path = path;
- this.acc = acc;
- this.flags = flags;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_OPEN;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[path=" + path + ", acc=" + acc + ", flags=" + flags + "]";
- }
-
- /**
- * Returns the path.
- *
- * @return The path.
- */
- public String getPath() {
- return path;
- }
-
- /**
- * Returns the flags.
- *
- * @return The flags.
- */
- public int getFlags() {
- return flags;
- }
-
- /**
- * Returns the acc.
- *
- * @return The acc.
- */
- public int getAcc() {
- return acc;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpOpendirRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpOpendirRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpOpendirRequest.java
deleted file mode 100644
index ac2e281..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpOpendirRequest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_OPENDIR' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpOpendirRequest extends BaseRequest {
- private final String path;
-
- /**
- * Create a SshFxpOpendirRequest instance.
- *
- * @param id The request id.
- * @param path The directory path to open.
- */
- public SshFxpOpendirRequest(final int id, final String path) {
- super(id);
- this.path = path;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_OPENDIR;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[path=" + path + "]";
- }
-
- /**
- * Returns the path.
- *
- * @return The path.
- */
- public String getPath() {
- return path;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpReadRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpReadRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpReadRequest.java
deleted file mode 100644
index 74fa654..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpReadRequest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.Handle;
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_READ' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpReadRequest extends BaseRequest {
- private final String handleId;
- private final long offset;
- private final Handle handle;
- private final int length;
-
- /**
- * Creates a SshFxpReadRequest instance.
- *
- * @param id The request id.
- * @param handleId The according file handle id.
- * @param offset The read offset.
- * @param length The length.
- * @param handle The according file handle.
- */
- public SshFxpReadRequest(
- final int id, final String handleId, final long offset, final int length, final Handle handle) {
- super(id);
- this.handleId = handleId;
- this.offset = offset;
- this.length = length;
- this.handle = handle;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_READ;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- String ps;
- if (handle != null && handle.getFile() != null) {
- ps = handle.getFile().getAbsolutePath();
- } else {
- ps = "";
- }
- return getName() + "[handle=" + handleId + ", file=" + ps + ", offset=" + offset + ", length=" + length + "]";
- }
-
- /**
- * Returns the according handle.
- *
- * @return The according handle.
- */
- public Handle getHandle() {
- return handle;
- }
-
- /**
- * Returns the handle id.
- *
- * @return The handle id.
- */
- public String getHandleId() {
- return handleId;
- }
-
- /**
- * Returns the length.
- *
- * @return The length.
- */
- public int getLength() {
- return length;
- }
-
- /**
- * Returns the offset.
- *
- * @return The offset.
- */
- public long getOffset() {
- return offset;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpReaddirRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpReaddirRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpReaddirRequest.java
deleted file mode 100644
index e3338fe..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpReaddirRequest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.Handle;
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_READDIR' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpReaddirRequest extends BaseRequest {
- private final String handleId;
- private final Handle handle;
-
- /**
- * Creates a SshFxpReaddirRequest instance.
- *
- * @param id The request id.
- * @param handleId The according handle id.
- * @param handle The according file handle.
- */
- public SshFxpReaddirRequest(final int id, final String handleId, final Handle handle) {
- super(id);
- this.handleId = handleId;
- this.handle = handle;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_READDIR;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- String ps;
- if (handle != null && handle.getFile() != null) {
- ps = handle.getFile().getAbsolutePath();
- } else {
- ps = "";
- }
- return getName() + "[handle=" + handleId + ", file=" + ps + "]";
- }
-
- /**
- * Returns the handle id.
- *
- * @return The handle id.
- */
- public String getHandleId() {
- return handleId;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRealpathRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRealpathRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRealpathRequest.java
deleted file mode 100644
index 064c651..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRealpathRequest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-* 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.sftp.request;
-
-import java.util.List;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_REALPATH' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpRealpathRequest extends BaseRequest {
- private final String path;
- private final byte options;
- private final List<String> compose;
-
- /**
- * Creates a SshFxpRealpathRequest instance.
- *
- * @param id The request id.
- * @param path The requested file path.
- */
- public SshFxpRealpathRequest(final int id, final String path, final byte options, final List<String> compose) {
- super(id);
- this.path = path;
- this.options = options;
- this.compose = compose;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_REALPATH;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[path=" + path + "]";
- }
-
- /**
- * Returns the path.
- *
- * @return The path.
- */
- public String getPath() {
- return path;
- }
-
- public byte getOptions() {
- return options;
- }
-
- public List<String> getCompose() {
- return compose;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRemoveRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRemoveRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRemoveRequest.java
deleted file mode 100644
index 664b4b2..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRemoveRequest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_REMOVE' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpRemoveRequest extends BaseRequest {
- private final String path;
-
- /**
- * Create a SshFxpRemoveRequest instance.
- *
- * @param id The request id.
- * @param path The file path to remove.
- */
- public SshFxpRemoveRequest(final int id, final String path) {
- super(id);
- this.path = path;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_REMOVE;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[path=" + path + "]";
- }
-
- /**
- * Returns the path.
- *
- * @return The path.
- */
- public String getPath() {
- return path;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRenameRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRenameRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRenameRequest.java
deleted file mode 100644
index cd0a3c6..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRenameRequest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_RENAME' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpRenameRequest extends BaseRequest {
- private final String oldPath;
- private final String newPath;
-
- /**
- * Create a SshFxpRenameRequest instance.
- *
- * @param id The request id.
- * @param oldPath The old path.
- * @param newPath The new path.
- */
- public SshFxpRenameRequest(final int id, final String oldPath, final String newPath) {
- super(id);
- this.oldPath = oldPath;
- this.newPath = newPath;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_RENAME;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[old=" + oldPath + ", new=" + newPath + "]";
- }
-
- /**
- * Returns the old path.
- *
- * @return The old path.
- */
- public String getOldPath() {
- return oldPath;
- }
-
- /**
- * Returns the new path.
- *
- * @return The new path.
- */
- public String getNewPath() {
- return newPath;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRmdirRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRmdirRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRmdirRequest.java
deleted file mode 100644
index 46263ef..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpRmdirRequest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_RMDIR' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpRmdirRequest extends BaseRequest {
- private final String path;
-
- /**
- * Creates a SshFxpRmdirRequest instance.
- *
- * @param id The request id.
- * @param path The requested file path.
- */
- public SshFxpRmdirRequest(final int id, final String path) {
- super(id);
- this.path = path;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_RMDIR;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[path=" + path + "]";
- }
-
- /**
- * Returns the path.
- *
- * @return The path.
- */
- public String getPath() {
- return path;
- }
-}
[06/15] mina-sshd git commit: [SSHD-377] Create a nio FileSystem
implementation for sftp
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/test/java/org/apache/sshd/common/file/util/BasePathTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/file/util/BasePathTest.java b/sshd-core/src/test/java/org/apache/sshd/common/file/util/BasePathTest.java
new file mode 100644
index 0000000..55c6b74
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/file/util/BasePathTest.java
@@ -0,0 +1,560 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.FileSystem;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.UserPrincipalLookupService;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class BasePathTest {
+
+ private TestFileSystem fileSystem;
+
+ @Before
+ public void setUp() {
+ fileSystem = new TestFileSystem(null);
+ }
+
+ @Test
+ public void testPathParsing() {
+ assertPathEquals("/", "/");
+ assertPathEquals("/foo", "/foo");
+ assertPathEquals("/foo", "/", "foo");
+ assertPathEquals("/foo/bar", "/foo/bar");
+ assertPathEquals("/foo/bar", "/", "foo", "bar");
+ assertPathEquals("/foo/bar", "/foo", "bar");
+ assertPathEquals("/foo/bar", "/", "foo/bar");
+ assertPathEquals("foo/bar/baz", "foo/bar/baz");
+ assertPathEquals("foo/bar/baz", "foo", "bar", "baz");
+ assertPathEquals("foo/bar/baz", "foo/bar", "baz");
+ assertPathEquals("foo/bar/baz", "foo", "bar/baz");
+ }
+
+ @Test
+ public void testPathParsing_withExtraSeparators() {
+ assertPathEquals("/foo/bar", "///foo/bar");
+ assertPathEquals("/foo/bar", "/foo///bar//");
+ assertPathEquals("/foo/bar/baz", "/foo", "/bar", "baz/");
+ //assertPathEquals("/foo/bar/baz", "/foo\\/bar//\\\\/baz\\/");
+ }
+
+ @Test
+ public void testRootPath() {
+ new PathTester(fileSystem, "/")
+ .root("/")
+ .test("/");
+ }
+
+ @Test
+ public void testRelativePath_singleName() {
+ new PathTester(fileSystem, "test")
+ .names("test")
+ .test("test");
+
+ Path path = parsePath("test");
+ assertEquals(path, path.getFileName());
+ }
+
+ @Test
+ public void testRelativePath_twoNames() {
+ PathTester tester = new PathTester(fileSystem, "foo/bar")
+ .names("foo", "bar");
+
+ tester.test("foo/bar");
+ }
+
+ @Test
+ public void testRelativePath_fourNames() {
+ new PathTester(fileSystem, "foo/bar/baz/test")
+ .names("foo", "bar", "baz", "test")
+ .test("foo/bar/baz/test");
+ }
+
+ @Test
+ public void testAbsolutePath_singleName() {
+ new PathTester(fileSystem, "/foo")
+ .root("/")
+ .names("foo")
+ .test("/foo");
+ }
+
+ @Test
+ public void testAbsolutePath_twoNames() {
+ new PathTester(fileSystem, "/foo/bar")
+ .root("/")
+ .names("foo", "bar")
+ .test("/foo/bar");
+ }
+
+ @Test
+ public void testAbsoluteMultiNamePath_fourNames() {
+ new PathTester(fileSystem, "/foo/bar/baz/test")
+ .root("/")
+ .names("foo", "bar", "baz", "test")
+ .test("/foo/bar/baz/test");
+ }
+
+ @Test
+ public void testResolve_fromRoot() {
+ Path root = parsePath("/");
+
+ assertResolvedPathEquals("/foo", root, "foo");
+ assertResolvedPathEquals("/foo/bar", root, "foo/bar");
+ assertResolvedPathEquals("/foo/bar", root, "foo", "bar");
+ assertResolvedPathEquals("/foo/bar/baz/test", root, "foo/bar/baz/test");
+ assertResolvedPathEquals("/foo/bar/baz/test", root, "foo", "bar/baz", "test");
+ }
+
+ @Test
+ public void testResolve_fromAbsolute() {
+ Path path = parsePath("/foo");
+
+ assertResolvedPathEquals("/foo/bar", path, "bar");
+ assertResolvedPathEquals("/foo/bar/baz/test", path, "bar/baz/test");
+ assertResolvedPathEquals("/foo/bar/baz/test", path, "bar/baz", "test");
+ assertResolvedPathEquals("/foo/bar/baz/test", path, "bar", "baz", "test");
+ }
+
+ @Test
+ public void testResolve_fromRelative() {
+ Path path = parsePath("foo");
+
+ assertResolvedPathEquals("foo/bar", path, "bar");
+ assertResolvedPathEquals("foo/bar/baz/test", path, "bar/baz/test");
+ assertResolvedPathEquals("foo/bar/baz/test", path, "bar", "baz", "test");
+ assertResolvedPathEquals("foo/bar/baz/test", path, "bar/baz", "test");
+ }
+
+ @Test
+ public void testResolve_withThisAndParentDirNames() {
+ Path path = parsePath("/foo");
+
+ assertResolvedPathEquals("/foo/bar/../baz", path, "bar/../baz");
+ assertResolvedPathEquals("/foo/bar/../baz", path, "bar", "..", "baz");
+ assertResolvedPathEquals("/foo/./bar/baz", path, "./bar/baz");
+ assertResolvedPathEquals("/foo/./bar/baz", path, ".", "bar/baz");
+ }
+
+ @Test
+ public void testResolve_givenAbsolutePath() {
+ assertResolvedPathEquals("/test", parsePath("/foo"), "/test");
+ assertResolvedPathEquals("/test", parsePath("foo"), "/test");
+ }
+
+ @Test
+ public void testResolve_givenEmptyPath() {
+ assertResolvedPathEquals("/foo", parsePath("/foo"), "");
+ assertResolvedPathEquals("foo", parsePath("foo"), "");
+ }
+
+ @Test
+ public void testResolve_againstEmptyPath() {
+ assertResolvedPathEquals("foo/bar", parsePath(""), "foo/bar");
+ }
+
+ @Test
+ public void testResolveSibling_givenEmptyPath() {
+ Path path = parsePath("foo/bar");
+ Path resolved = path.resolveSibling("");
+ assertPathEquals("foo", resolved);
+
+ path = parsePath("foo");
+ resolved = path.resolveSibling("");
+ assertPathEquals("", resolved);
+ }
+
+ @Test
+ public void testResolveSibling_againstEmptyPath() {
+ Path path = parsePath("");
+ Path resolved = path.resolveSibling("foo");
+ assertPathEquals("foo", resolved);
+
+ path = parsePath("");
+ resolved = path.resolveSibling("");
+ assertPathEquals("", resolved);
+ }
+
+ @Test
+ public void testRelativize_bothAbsolute() {
+ assertRelativizedPathEquals("b/c", parsePath("/a"), "/a/b/c");
+ assertRelativizedPathEquals("c/d", parsePath("/a/b"), "/a/b/c/d");
+ }
+
+ @Test
+ public void testRelativize_bothRelative() {
+ assertRelativizedPathEquals("b/c", parsePath("a"), "a/b/c");
+ assertRelativizedPathEquals("d", parsePath("a/b/c"), "a/b/c/d");
+ }
+
+ @Test
+ public void testRelativize_againstEmptyPath() {
+ assertRelativizedPathEquals("foo/bar", parsePath(""), "foo/bar");
+ }
+
+ @Test
+ public void testRelativize_oneAbsoluteOneRelative() {
+ try {
+ parsePath("/foo/bar").relativize(parsePath("foo"));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ parsePath("foo").relativize(parsePath("/foo/bar"));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testNormalize_withParentDirName() {
+ assertNormalizedPathEquals("/foo/baz", "/foo/bar/../baz");
+ assertNormalizedPathEquals("/foo/baz", "/foo", "bar", "..", "baz");
+ }
+
+ @Test
+ public void testNormalize_withThisDirName() {
+ assertNormalizedPathEquals("/foo/bar/baz", "/foo/bar/./baz");
+ assertNormalizedPathEquals("/foo/bar/baz", "/foo", "bar", ".", "baz");
+ }
+
+ @Test
+ public void testNormalize_withThisAndParentDirNames() {
+ assertNormalizedPathEquals("foo/test", "foo/./bar/../././baz/../test");
+ }
+
+ @Test
+ public void testNormalize_withLeadingParentDirNames() {
+ assertNormalizedPathEquals("../../foo/baz", "../../foo/bar/../baz");
+ }
+
+ @Test
+ public void testNormalize_withLeadingThisAndParentDirNames() {
+ assertNormalizedPathEquals("../../foo/baz", "./.././.././foo/bar/../baz");
+ }
+
+ @Test
+ public void testNormalize_withExtraParentDirNamesAtRoot() {
+ assertNormalizedPathEquals("/", "/..");
+ assertNormalizedPathEquals("/", "/../../..");
+ assertNormalizedPathEquals("/", "/foo/../../..");
+ assertNormalizedPathEquals("/", "/../foo/../../bar/baz/../../../..");
+ }
+
+ @Test
+ public void testPathWithExtraSlashes() {
+ assertPathEquals("/foo/bar/baz", parsePath("/foo/bar/baz/"));
+ assertPathEquals("/foo/bar/baz", parsePath("/foo//bar///baz"));
+ assertPathEquals("/foo/bar/baz", parsePath("///foo/bar/baz"));
+ }
+
+ private void assertResolvedPathEquals(String expected, Path path, String firstResolvePath,
+ String... moreResolvePaths) {
+ Path resolved = path.resolve(firstResolvePath);
+ for (String additionalPath : moreResolvePaths) {
+ resolved = resolved.resolve(additionalPath);
+ }
+ assertPathEquals(expected, resolved);
+
+ Path relative = parsePath(firstResolvePath, moreResolvePaths);
+ resolved = path.resolve(relative);
+ assertPathEquals(expected, resolved);
+
+ // assert the invariant that p.relativize(p.resolve(q)).equals(q) when q does not have a root
+ // p = path, q = relative, p.resolve(q) = resolved
+ if (relative.getRoot() == null) {
+ assertEquals(relative, path.relativize(resolved));
+ }
+ }
+
+ private void assertRelativizedPathEquals(String expected, Path path, String relativizePath) {
+ Path relativized = path.relativize(parsePath(relativizePath));
+ assertPathEquals(expected, relativized);
+ }
+
+ private void assertNormalizedPathEquals(String expected, String first, String... more) {
+ assertPathEquals(expected, parsePath(first, more).normalize());
+ }
+
+ private void assertPathEquals(String expected, String first, String... more) {
+ assertPathEquals(expected, parsePath(first, more));
+ }
+
+ private void assertPathEquals(String expected, Path path) {
+ assertEquals(parsePath(expected), path);
+ }
+
+ private Path parsePath(String first, String... more) {
+ return fileSystem.getPath(first, more);
+ }
+
+ private static class TestFileSystem extends BaseFileSystem<TestPath> {
+
+ public TestFileSystem(FileSystemProvider fileSystemProvider) {
+ super(fileSystemProvider);
+ }
+
+ @Override
+ protected TestPath create(String root, ImmutableList<String> names) {
+ return new TestPath(this, root, names);
+ }
+
+ @Override
+ public void close() throws IOException {
+
+ }
+
+ @Override
+ public boolean isOpen() {
+ return false;
+ }
+
+ @Override
+ public Set<String> supportedFileAttributeViews() {
+ return null;
+ }
+
+ @Override
+ public UserPrincipalLookupService getUserPrincipalLookupService() {
+ return null;
+ }
+ }
+
+ private static class TestPath extends BasePath<TestPath, TestFileSystem> {
+
+ public TestPath(TestFileSystem fileSystem, String root, ImmutableList<String> names) {
+ super(fileSystem, root, names);
+ }
+
+ @Override
+ protected TestPath create(String root, ImmutableList<String> names) {
+ return new TestPath(fileSystem, root, names);
+ }
+
+ @Override
+ public URI toUri() {
+ return null;
+ }
+
+ @Override
+ public Path toRealPath(LinkOption... options) throws IOException {
+ return null;
+ }
+ }
+
+ public static class PathTester {
+
+ private final FileSystem fileSystem;
+ private final String string;
+ private String root;
+ private ImmutableList<String> names = new ImmutableList<>(new String[0]);
+
+ public PathTester(FileSystem fileSystem, String string) {
+ this.fileSystem = fileSystem;
+ this.string = string;
+ }
+
+ public PathTester root(String root) {
+ this.root = root;
+ return this;
+ }
+
+ public PathTester names(Collection<String> names) {
+ this.names = new ImmutableList<>(names.toArray(new String[names.size()]));
+ return this;
+ }
+
+ public PathTester names(String... names) {
+ return names(Arrays.asList(names));
+ }
+
+ public void test(String first, String... more) {
+ Path path = fileSystem.getPath(first, more);
+ test(path);
+ }
+
+ public void test(Path path) {
+ assertEquals(string, path.toString());
+
+ testRoot(path);
+ testNames(path);
+ testParents(path);
+ testStartsWith(path);
+ testEndsWith(path);
+ testSubpaths(path);
+ }
+
+ private void testRoot(Path path) {
+ if (root != null) {
+ assertTrue(path + ".isAbsolute() should be true", path.isAbsolute());
+ assertNotNull(path + ".getRoot() should not be null", path.getRoot());
+ assertEquals(root, path.getRoot().toString());
+ } else {
+ assertFalse(path + ".isAbsolute() should be false", path.isAbsolute());
+ assertNull(path + ".getRoot() should be null", path.getRoot());
+ }
+ }
+
+ private void testNames(Path path) {
+ assertEquals(names.size(), path.getNameCount());
+ assertEquals(names, names(path));
+ for (int i = 0; i < names.size(); i++) {
+ assertEquals(names.get(i), path.getName(i).toString());
+ // don't test individual names if this is an individual name
+ if (names.size() > 1) {
+ new PathTester(fileSystem, names.get(i))
+ .names(names.get(i))
+ .test(path.getName(i));
+ }
+ }
+ if (names.size() > 0) {
+ String fileName = names.get(names.size() - 1);
+ assertEquals(fileName, path.getFileName().toString());
+ // don't test individual names if this is an individual name
+ if (names.size() > 1) {
+ new PathTester(fileSystem, fileName)
+ .names(fileName)
+ .test(path.getFileName());
+ }
+ }
+ }
+
+ private void testParents(Path path) {
+ Path parent = path.getParent();
+
+ if (root != null && names.size() >= 1 || names.size() > 1) {
+ assertNotNull(parent);
+ }
+
+ if (parent != null) {
+ String parentName = names.size() == 1 ? root :
+ string.substring(0, string.lastIndexOf('/'));
+ new PathTester(fileSystem, parentName)
+ .root(root)
+ .names(names.subList(0, names.size() - 1))
+ .test(parent);
+ }
+ }
+
+ private void testSubpaths(Path path) {
+ if (path.getRoot() == null) {
+ assertEquals(path, path.subpath(0, path.getNameCount()));
+ }
+
+ if (path.getNameCount() > 1) {
+ String stringWithoutRoot = root == null ? string : string.substring(root.length());
+
+ // test start + 1 to end and start to end - 1 subpaths... this recursively tests all subpaths
+ // actually tests most possible subpaths multiple times but... eh
+ Path startSubpath = path.subpath(1, path.getNameCount());
+ List<String> startNames = split(stringWithoutRoot, "/")
+ .subList(1, path.getNameCount());
+
+ new PathTester(fileSystem, join(startNames, "/"))
+ .names(startNames)
+ .test(startSubpath);
+
+ Path endSubpath = path.subpath(0, path.getNameCount() - 1);
+ List<String> endNames = split(stringWithoutRoot, "/")
+ .subList(0, path.getNameCount() - 1);
+
+ new PathTester(fileSystem, join(endNames, "/"))
+ .names(endNames)
+ .test(endSubpath);
+ }
+ }
+
+ private void testStartsWith(Path path) {
+ // empty path doesn't start with any path
+ if (root != null || !names.isEmpty()) {
+ Path other = path;
+ while (other != null) {
+ assertTrue(path + ".startsWith(" + other + ") should be true",
+ path.startsWith(other));
+ assertTrue(path + ".startsWith(" + other + ") should be true",
+ path.startsWith(other.toString()));
+ other = other.getParent();
+ }
+ }
+ }
+
+ private void testEndsWith(Path path) {
+ // empty path doesn't start with any path
+ if (root != null || !names.isEmpty()) {
+ Path other = path;
+ while (other != null) {
+ assertTrue(path + ".endsWith(" + other + ") should be true",
+ path.endsWith(other));
+ assertTrue(path + ".endsWith(" + other + ") should be true",
+ path.endsWith(other.toString()));
+ if (other.getRoot() != null && other.getNameCount() > 0) {
+ other = other.subpath(0, other.getNameCount());
+ } else if (other.getNameCount() > 1) {
+ other = other.subpath(1, other.getNameCount());
+ } else {
+ other = null;
+ }
+ }
+ }
+ }
+
+ private static List<String> names(Path path) {
+ List<String> list = new ArrayList<>();
+ for (Path p : path) {
+ list.add(p.toString());
+ }
+ return list;
+ }
+
+ private List<String> split(String string, String sep) {
+ return Arrays.asList(string.split(sep));
+ }
+
+ private static String join(Iterable<String> strings, String sep) {
+ StringBuilder sb = new StringBuilder();
+ for (String s : strings) {
+ if (sb.length() > 0) {
+ sb.append(sep);
+ }
+ sb.append(s);
+ }
+ return sb.toString();
+ }
+ }
+
+}
[09/15] mina-sshd git commit: [SSHD-408] Implement sftp v4, v5 and v6
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/22581fb8/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
index 5313f6e..700e61d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
@@ -25,35 +25,52 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
-import java.nio.channels.SeekableByteChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
+import java.nio.charset.StandardCharsets;
import java.nio.file.AccessDeniedException;
+import java.nio.file.CopyOption;
+import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.DirectoryStream;
+import java.nio.file.FileAlreadyExistsException;
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.OpenOption;
import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.AclEntryFlag;
+import java.nio.file.attribute.AclEntryPermission;
+import java.nio.file.attribute.AclEntryType;
+import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.GroupPrincipal;
import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
import java.nio.file.attribute.UserPrincipal;
import java.nio.file.attribute.UserPrincipalLookupService;
+import java.nio.file.attribute.AclEntry;
+import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
-import java.util.EnumSet;
+import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.file.FileSystemAware;
@@ -131,40 +148,6 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
}
- //
- // File attributes
- //
- enum Attribute {
- Size, // long
- Uid, // int
- Owner, // String
- Gid, // int
- Group, // String
- IsDirectory, // boolean
- IsRegularFile, // boolean
- IsSymbolicLink, // boolean
- Permissions, // EnumSet<Permission>
- CreationTime, // long
- LastModifiedTime, // long
- LastAccessTime, // long
- NLink // int
- }
-
- //
- // File permissions
- //
- enum Permission {
- UserRead,
- UserWrite,
- UserExecute,
- GroupRead,
- GroupWrite,
- GroupExecute,
- OthersRead,
- OthersWrite,
- OthersExecute
- }
-
public enum UnsupportedAttributePolicy {
Ignore,
Warn,
@@ -176,9 +159,14 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
*/
public static final String MAX_OPEN_HANDLES_PER_SESSION = "max-open-handles-per-session";
+ /**
+ * Force the use of a given sftp version
+ */
+ public static final String SFTP_VERSION = "sftp-version";
+
public static final int LOWER_SFTP_IMPL = 3; // Working implementation from v3
- public static final int HIGHER_SFTP_IMPL = 3; // .. up to
- public static final String ALL_SFTP_IMPL = "3";
+ public static final int HIGHER_SFTP_IMPL = 6; // .. up to
+ public static final String ALL_SFTP_IMPL = "3,4,5,6";
public static final int MAX_PACKET_LENGTH = 1024 * 16;
public static final int SSH_FXP_INIT = 1;
@@ -200,7 +188,10 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
public static final int SSH_FXP_STAT = 17;
public static final int SSH_FXP_RENAME = 18;
public static final int SSH_FXP_READLINK = 19;
- public static final int SSH_FXP_SYMLINK = 20;
+ public static final int SSH_FXP_SYMLINK = 20; // v3 -> v5
+ public static final int SSH_FXP_LINK = 21; // v6
+ public static final int SSH_FXP_BLOCK = 22; // v6
+ public static final int SSH_FXP_UNBLOCK = 23; // v6
public static final int SSH_FXP_STATUS = 101;
public static final int SSH_FXP_HANDLE = 102;
public static final int SSH_FXP_DATA = 103;
@@ -209,23 +200,81 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
public static final int SSH_FXP_EXTENDED = 200;
public static final int SSH_FXP_EXTENDED_REPLY = 201;
- public static final int SSH_FX_OK = 0;
- public static final int SSH_FX_EOF = 1;
- public static final int SSH_FX_NO_SUCH_FILE = 2;
- public static final int SSH_FX_PERMISSION_DENIED = 3;
- public static final int SSH_FX_FAILURE = 4;
- public static final int SSH_FX_BAD_MESSAGE = 5;
- public static final int SSH_FX_NO_CONNECTION = 6;
- public static final int SSH_FX_CONNECTION_LOST = 7;
- public static final int SSH_FX_OP_UNSUPPORTED = 8;
-
- public static final int SSH_FX_FILE_ALREADY_EXISTS = 11; // Not in v3, but we need it
-
- public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
- public static final int SSH_FILEXFER_ATTR_UIDGID = 0x00000002;
- public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
- public static final int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008; //v3 naming convention
- public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
+ public static final int SSH_FX_OK = 0;
+ public static final int SSH_FX_EOF = 1;
+ public static final int SSH_FX_NO_SUCH_FILE = 2;
+ public static final int SSH_FX_PERMISSION_DENIED = 3;
+ public static final int SSH_FX_FAILURE = 4;
+ public static final int SSH_FX_BAD_MESSAGE = 5;
+ public static final int SSH_FX_NO_CONNECTION = 6;
+ public static final int SSH_FX_CONNECTION_LOST = 7;
+ public static final int SSH_FX_OP_UNSUPPORTED = 8;
+ public static final int SSH_FX_INVALID_HANDLE = 9;
+ public static final int SSH_FX_NO_SUCH_PATH = 10;
+ public static final int SSH_FX_FILE_ALREADY_EXISTS = 11;
+ public static final int SSH_FX_WRITE_PROTECT = 12;
+ public static final int SSH_FX_NO_MEDIA = 13;
+ public static final int SSH_FX_NO_SPACE_ON_FILESYSTEM = 14;
+ public static final int SSH_FX_QUOTA_EXCEEDED = 15;
+ public static final int SSH_FX_UNKNOWN_PRINCIPLE = 16;
+ public static final int SSH_FX_LOCK_CONFLICT = 17;
+ public static final int SSH_FX_DIR_NOT_EMPTY = 18;
+ public static final int SSH_FX_NOT_A_DIRECTORY = 19;
+ public static final int SSH_FX_INVALID_FILENAME = 20;
+ public static final int SSH_FX_LINK_LOOP = 21;
+ public static final int SSH_FX_CANNOT_DELETE = 22;
+ public static final int SSH_FX_INVALID_PARAMETER = 23;
+ public static final int SSH_FX_FILE_IS_A_DIRECTORY = 24;
+ public static final int SSH_FX_BYTE_RANGE_LOCK_CONFLICT = 25;
+ public static final int SSH_FX_BYTE_RANGE_LOCK_REFUSED = 26;
+ public static final int SSH_FX_DELETE_PENDING = 27;
+ public static final int SSH_FX_FILE_CORRUPT = 28;
+ public static final int SSH_FX_OWNER_INVALID = 29;
+ public static final int SSH_FX_GROUP_INVALID = 30;
+ public static final int SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31;
+
+ public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
+ public static final int SSH_FILEXFER_ATTR_UIDGID = 0x00000002;
+ public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
+ public static final int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008; // v3 naming convention
+ public static final int SSH_FILEXFER_ATTR_ACCESSTIME = 0x00000008; // v4
+ public static final int SSH_FILEXFER_ATTR_CREATETIME = 0x00000010; // v4
+ public static final int SSH_FILEXFER_ATTR_MODIFYTIME = 0x00000020; // v4
+ public static final int SSH_FILEXFER_ATTR_ACL = 0x00000040; // v4
+ public static final int SSH_FILEXFER_ATTR_OWNERGROUP = 0x00000080; // v4
+ public static final int SSH_FILEXFER_ATTR_SUBSECOND_TIMES = 0x00000100; // v5
+ public static final int SSH_FILEXFER_ATTR_BITS = 0x00000200; // v5
+ public static final int SSH_FILEXFER_ATTR_ALLOCATION_SIZE = 0x00000400; // v6
+ public static final int SSH_FILEXFER_ATTR_TEXT_HINT = 0x00000800; // v6
+ public static final int SSH_FILEXFER_ATTR_MIME_TYPE = 0x00001000; // v6
+ public static final int SSH_FILEXFER_ATTR_LINK_COUNT = 0x00002000; // v6
+ public static final int SSH_FILEXFER_ATTR_UNTRANSLATED_NAME = 0x00004000; // v6
+ public static final int SSH_FILEXFER_ATTR_CTIME = 0x00008000; // v6
+ public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
+
+ public static final int SSH_FILEXFER_ATTR_ALL = 0x0000FFFF; // All attributes
+
+ public static final int SSH_FILEXFER_ATTR_FLAGS_READONLY = 0x00000001;
+ public static final int SSH_FILEXFER_ATTR_FLAGS_SYSTEM = 0x00000002;
+ public static final int SSH_FILEXFER_ATTR_FLAGS_HIDDEN = 0x00000004;
+ public static final int SSH_FILEXFER_ATTR_FLAGS_CASE_INSENSITIVE = 0x00000008;
+ public static final int SSH_FILEXFER_ATTR_FLAGS_ARCHIVE = 0x00000010;
+ public static final int SSH_FILEXFER_ATTR_FLAGS_ENCRYPTED = 0x00000020;
+ public static final int SSH_FILEXFER_ATTR_FLAGS_COMPRESSED = 0x00000040;
+ public static final int SSH_FILEXFER_ATTR_FLAGS_SPARSE = 0x00000080;
+ public static final int SSH_FILEXFER_ATTR_FLAGS_APPEND_ONLY = 0x00000100;
+ public static final int SSH_FILEXFER_ATTR_FLAGS_IMMUTABLE = 0x00000200;
+ public static final int SSH_FILEXFER_ATTR_FLAGS_SYNC = 0x00000400;
+
+ public static final int SSH_FILEXFER_TYPE_REGULAR = 1;
+ public static final int SSH_FILEXFER_TYPE_DIRECTORY = 2;
+ public static final int SSH_FILEXFER_TYPE_SYMLINK = 3;
+ public static final int SSH_FILEXFER_TYPE_SPECIAL = 4;
+ public static final int SSH_FILEXFER_TYPE_UNKNOWN = 5;
+ public static final int SSH_FILEXFER_TYPE_SOCKET = 6; // v5
+ public static final int SSH_FILEXFER_TYPE_CHAR_DEVICE = 7; // v5
+ public static final int SSH_FILEXFER_TYPE_BLOCK_DEVICE = 8; // v5
+ public static final int SSH_FILEXFER_TYPE_FIFO = 9; // v5
public static final int SSH_FXF_READ = 0x00000001;
public static final int SSH_FXF_WRITE = 0x00000002;
@@ -233,6 +282,63 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
public static final int SSH_FXF_CREAT = 0x00000008;
public static final int SSH_FXF_TRUNC = 0x00000010;
public static final int SSH_FXF_EXCL = 0x00000020;
+ public static final int SSH_FXF_TEXT = 0x00000040;
+
+ public static final int SSH_FXF_ACCESS_DISPOSITION = 0x00000007;
+ public static final int SSH_FXF_CREATE_NEW = 0x00000000;
+ public static final int SSH_FXF_CREATE_TRUNCATE = 0x00000001;
+ public static final int SSH_FXF_OPEN_EXISTING = 0x00000002;
+ public static final int SSH_FXF_OPEN_OR_CREATE = 0x00000003;
+ public static final int SSH_FXF_TRUNCATE_EXISTING = 0x00000004;
+ public static final int SSH_FXF_APPEND_DATA = 0x00000008;
+ public static final int SSH_FXF_APPEND_DATA_ATOMIC = 0x00000010;
+ public static final int SSH_FXF_TEXT_MODE = 0x00000020;
+ public static final int SSH_FXF_READ_LOCK = 0x00000040;
+ public static final int SSH_FXF_WRITE_LOCK = 0x00000080;
+ public static final int SSH_FXF_DELETE_LOCK = 0x00000100;
+
+ public static final int SSH_FXP_RENAME_OVERWRITE = 0x00000001;
+ public static final int SSH_FXP_RENAME_ATOMIC = 0x00000002;
+ public static final int SSH_FXP_RENAME_NATIVE = 0x00000004;
+
+ public static final int SSH_FXP_REALPATH_NO_CHECK = 0x00000001;
+ public static final int SSH_FXP_REALPATH_STAT_IF = 0x00000002;
+ public static final int SSH_FXP_REALPATH_STAT_ALWAYS = 0x00000003;
+
+ public static final int SSH_FXF_RENAME_OVERWRITE = 0x00000001;
+ public static final int SSH_FXF_RENAME_ATOMIC = 0x00000002;
+ public static final int SSH_FXF_RENAME_NATIVE = 0x00000004;
+
+ public static final int ACE4_ACCESS_ALLOWED_ACE_TYPE = 0x00000000;
+ public static final int ACE4_ACCESS_DENIED_ACE_TYPE = 0x00000001;
+ public static final int ACE4_SYSTEM_AUDIT_ACE_TYPE = 0x00000002;
+ public static final int ACE4_SYSTEM_ALARM_ACE_TYPE = 0x00000003;
+
+ public static final int ACE4_FILE_INHERIT_ACE = 0x00000001;
+ public static final int ACE4_DIRECTORY_INHERIT_ACE = 0x00000002;
+ public static final int ACE4_NO_PROPAGATE_INHERIT_ACE = 0x00000004;
+ public static final int ACE4_INHERIT_ONLY_ACE = 0x00000008;
+ public static final int ACE4_SUCCESSFUL_ACCESS_ACE_FLAG = 0x00000010;
+ public static final int ACE4_FAILED_ACCESS_ACE_FLAG = 0x00000020;
+ public static final int ACE4_IDENTIFIER_GROUP = 0x00000040;
+
+ public static final int ACE4_READ_DATA = 0x00000001;
+ public static final int ACE4_LIST_DIRECTORY = 0x00000001;
+ public static final int ACE4_WRITE_DATA = 0x00000002;
+ public static final int ACE4_ADD_FILE = 0x00000002;
+ public static final int ACE4_APPEND_DATA = 0x00000004;
+ public static final int ACE4_ADD_SUBDIRECTORY = 0x00000004;
+ public static final int ACE4_READ_NAMED_ATTRS = 0x00000008;
+ public static final int ACE4_WRITE_NAMED_ATTRS = 0x00000010;
+ public static final int ACE4_EXECUTE = 0x00000020;
+ public static final int ACE4_DELETE_CHILD = 0x00000040;
+ public static final int ACE4_READ_ATTRIBUTES = 0x00000080;
+ public static final int ACE4_WRITE_ATTRIBUTES = 0x00000100;
+ public static final int ACE4_DELETE = 0x00010000;
+ public static final int ACE4_READ_ACL = 0x00020000;
+ public static final int ACE4_WRITE_ACL = 0x00040000;
+ public static final int ACE4_WRITE_OWNER = 0x00080000;
+ public static final int ACE4_SYNCHRONIZE = 0x00100000;
public static final int S_IFMT = 0170000; // bitmask for the file type bitfields
public static final int S_IFSOCK = 0140000; // socket
@@ -255,6 +361,10 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
public static final int S_IWOTH = 0000002;
public static final int S_IXOTH = 0000001;
+ public static int SFTP_V3 = 3;
+ public static int SFTP_V4 = 4;
+ public static int SFTP_V5 = 5;
+ public static int SFTP_V6 = 6;
private ExitCallback callback;
private InputStream in;
@@ -271,7 +381,8 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
private Path defaultDir = fileSystem.getPath(System.getProperty("user.dir"));
private int version;
- private Map<String, Handle> handles = new HashMap<>();
+ private final Map<String, byte[]> extensions = new HashMap<>();
+ private final Map<String, Handle> handles = new HashMap<>();
private UnsupportedAttributePolicy unsupportedAttributePolicy = UnsupportedAttributePolicy.Warn;
@@ -335,25 +446,64 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
}
- protected static class FileHandle extends Handle {
- SeekableByteChannel channel;
+ protected class FileHandle extends Handle {
+ final FileChannel channel;
+ final List<FileLock> locks = new ArrayList<>();
- public FileHandle(Path file, int flags) throws IOException {
+ public FileHandle(Path file, int flags, int access, Map<String, Object> attrs) throws IOException {
super(file);
Set<OpenOption> options = new HashSet<>();
- if ((flags & SSH_FXF_READ) != 0) {
+ if ((access & ACE4_READ_DATA) != 0 || (access & ACE4_READ_ATTRIBUTES) != 0) {
options.add(StandardOpenOption.READ);
}
- if ((flags & SSH_FXF_WRITE) != 0) {
+ if ((access & ACE4_WRITE_DATA) != 0 || (access & ACE4_WRITE_ATTRIBUTES) != 0) {
options.add(StandardOpenOption.WRITE);
}
- if ((flags & SSH_FXF_APPEND) != 0) {
+ switch (flags & SSH_FXF_ACCESS_DISPOSITION) {
+ case SSH_FXF_CREATE_NEW:
+ options.add(StandardOpenOption.CREATE_NEW);
+ break;
+ case SSH_FXF_CREATE_TRUNCATE:
+ options.add(StandardOpenOption.CREATE);
+ options.add(StandardOpenOption.TRUNCATE_EXISTING);
+ break;
+ case SSH_FXF_OPEN_EXISTING:
+ break;
+ case SSH_FXF_OPEN_OR_CREATE:
+ options.add(StandardOpenOption.CREATE);
+ break;
+ case SSH_FXF_TRUNCATE_EXISTING:
+ options.add(StandardOpenOption.TRUNCATE_EXISTING);
+ break;
+ }
+ if ((flags & SSH_FXF_APPEND_DATA) != 0) {
options.add(StandardOpenOption.APPEND);
}
- if ((flags & SSH_FXF_TRUNC) != 0) {
- options.add(StandardOpenOption.TRUNCATE_EXISTING);
+ FileAttribute<?>[] attributes = new FileAttribute<?>[attrs.size()];
+ int index = 0;
+ for (Map.Entry<String, Object> attr : attrs.entrySet()) {
+ final String key = attr.getKey();
+ final Object val = attr.getValue();
+ attributes[index++] = new FileAttribute<Object>() {
+ @Override
+ public String name() {
+ return key;
+ }
+
+ @Override
+ public Object value() {
+ return val;
+ }
+ };
+ }
+ FileChannel channel;
+ try {
+ channel = FileChannel.open(file, options, attributes);
+ } catch (UnsupportedOperationException e) {
+ channel = FileChannel.open(file, options);
+ setAttributes(file, attrs);
}
- channel = Files.newByteChannel(file, options);
+ this.channel = channel;
}
public int read(byte[] data, long offset) throws IOException {
@@ -370,6 +520,32 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
public void close() throws IOException {
channel.close();
}
+
+ public void lock(long offset, long length, int mask) throws IOException {
+ long size = length == 0 ? channel.size() - offset : length;
+ FileLock lock = channel.tryLock(offset, size, false);
+ synchronized (locks) {
+ locks.add(lock);
+ }
+ }
+
+ public boolean unlock(long offset, long length) throws IOException {
+ long size = length == 0 ? channel.size() - offset : length;
+ FileLock lock = null;
+ for (Iterator<FileLock> iterator = locks.iterator(); iterator.hasNext();) {
+ FileLock l = iterator.next();
+ if (l.position() == offset && l.size() == size) {
+ iterator.remove();
+ lock = l;
+ break;
+ }
+ }
+ if (lock != null) {
+ lock.release();
+ return true;
+ }
+ return false;
+ }
}
public SftpSubsystem() {
@@ -411,7 +587,10 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
public void setFileSystem(FileSystem fileSystem) {
- this.fileSystem = fileSystem;
+ if (fileSystem != this.fileSystem) {
+ this.fileSystem = fileSystem;
+ this.defaultDir = fileSystem.getRootDirectories().iterator().next();
+ }
}
public void setExitCallback(ExitCallback callback) {
@@ -435,7 +614,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
try {
pendingFuture = executors.submit(this);
} catch (RuntimeException e) { // e.g., RejectedExecutionException
- log.error("Failed (" + e.getClass().getSimpleName() + ") to start command: " + e.getMessage(), e);
+ log.error("Failed (" + e.getClass().getSimpleName() + ") to start command: " + e.toString(), e);
throw new IOException(e);
}
}
@@ -495,405 +674,95 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
int id = buffer.getInt();
switch (type) {
case SSH_FXP_INIT: {
- log.debug("Received SSH_FXP_INIT (version={})", id);
- // see https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt - section 4 - Protocol Initialization
- if (length < 5) { // we don't care about extensions
- throw new IllegalArgumentException("Incomplete SSH_FXP_INIT data: length=" + length);
- }
- version = id;
- if (version >= LOWER_SFTP_IMPL) {
- version = Math.min(version, HIGHER_SFTP_IMPL);
- buffer.clear();
- buffer.putByte((byte) SSH_FXP_VERSION);
- buffer.putInt(version);
- send(buffer);
- } else {
- // We only support version 3 (Version 1 and 2 are not common)
- sendStatus(id, SSH_FX_OP_UNSUPPORTED, "SFTP server only support versions " + ALL_SFTP_IMPL);
- }
-
+ doInit(buffer, id);
break;
}
case SSH_FXP_OPEN: {
- if (session.getFactoryManager().getProperties() != null) {
- String maxHandlesString = session.getFactoryManager().getProperties().get(MAX_OPEN_HANDLES_PER_SESSION);
- if (maxHandlesString != null) {
- int maxHandleCount = Integer.parseInt(maxHandlesString);
- if (handles.size() > maxHandleCount) {
- sendStatus(id, SSH_FX_FAILURE, "Too many open handles");
- break;
- }
- }
- }
-
- String path = buffer.getString();
- int pflags = buffer.getInt();
- Map<Attribute, Object> attrs = readAttrs(buffer);
- log.debug("Received SSH_FXP_OPEN (path={}, pflags={}, attrs={})", new Object[] { path, pflags, attrs });
- try {
- Path file = resolveFile(path);
- if (Files.exists(file)) {
- if ((pflags & SSH_FXF_READ) != 0 && !Files.isReadable(file)) {
- sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not read " + path);
- return;
- }
- if ((pflags & SSH_FXF_WRITE) != 0 && !Files.isWritable(file)) {
- sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not write " + path);
- return;
- }
- if (((pflags & SSH_FXF_CREAT) != 0) && ((pflags & SSH_FXF_EXCL) != 0)) {
- sendStatus(id, SSH_FX_FAILURE, path);
- return;
- }
- } else {
- if (((pflags & SSH_FXF_CREAT) != 0)) {
- Files.createFile(file);
- } else {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, "No such file " + path);
- return;
- }
- }
- if (((pflags & SSH_FXF_CREAT) != 0)) {
- setAttributes(file, attrs);
- }
- String handle = UUID.randomUUID().toString();
- handles.put(handle, new FileHandle(file, pflags));
- sendHandle(id, handle);
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage() == null ? "" : e.getMessage());
- }
+ doOpen(buffer, id);
break;
}
case SSH_FXP_CLOSE: {
- String handle = buffer.getString();
- log.debug("Received SSH_FXP_CLOSE (handle={})", handle);
- try {
- Handle h = handles.get(handle);
- if (h == null) {
- sendStatus(id, SSH_FX_FAILURE, handle, "");
- } else {
- handles.remove(handle);
- h.close();
- sendStatus(id, SSH_FX_OK, "", "");
- }
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doClose(buffer, id);
break;
}
case SSH_FXP_READ: {
- String handle = buffer.getString();
- long offset = buffer.getLong();
- int len = buffer.getInt();
- log.debug("Received SSH_FXP_READ (handle={}, offset={}, length={})", new Object[] { handle, offset, len });
- try {
- Handle p = handles.get(handle);
- if (!(p instanceof FileHandle)) {
- sendStatus(id, SSH_FX_FAILURE, handle);
- } else {
- FileHandle fh = (FileHandle) p;
- byte[] b = new byte[Math.min(len, Buffer.MAX_LEN)];
- len = fh.read(b, offset);
- if (len >= 0) {
- Buffer buf = new Buffer(len + 5);
- buf.putByte((byte) SSH_FXP_DATA);
- buf.putInt(id);
- buf.putBytes(b, 0, len);
- send(buf);
- } else {
- sendStatus(id, SSH_FX_EOF, "");
- }
- }
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doRead(buffer, id);
break;
}
case SSH_FXP_WRITE: {
- String handle = buffer.getString();
- long offset = buffer.getLong();
- byte[] data = buffer.getBytes();
- log.debug("Received SSH_FXP_WRITE (handle={}, offset={}, data=byte[{}])", new Object[] { handle, offset, data.length });
- try {
- Handle p = handles.get(handle);
- if (!(p instanceof FileHandle)) {
- sendStatus(id, SSH_FX_FAILURE, handle);
- } else {
- FileHandle fh = (FileHandle) p;
- fh.write(data, offset);
- sendStatus(id, SSH_FX_OK, "");
- }
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doWrite(buffer, id);
break;
}
case SSH_FXP_LSTAT: {
- String path = buffer.getString();
- log.debug("Received SSH_FXP_LSTAT (path={})", path);
- try {
- Path p = resolveFile(path);
- sendAttrs(id, p, false);
- } catch (FileNotFoundException e) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage());
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doLStat(buffer, id);
break;
}
case SSH_FXP_FSTAT: {
- String handle = buffer.getString();
- log.debug("Received SSH_FXP_FSTAT (handle={})", handle);
- try {
- Handle p = handles.get(handle);
- if (p == null) {
- sendStatus(id, SSH_FX_FAILURE, handle);
- } else {
- sendAttrs(id, p.getFile(), true);
- }
- } catch (FileNotFoundException e) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage());
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doFStat(buffer, id);
break;
}
case SSH_FXP_SETSTAT: {
- String path = buffer.getString();
- Map<Attribute, Object> attrs = readAttrs(buffer);
- log.debug("Received SSH_FXP_SETSTAT (path={}, attrs={})", path, attrs);
- try {
- Path p = resolveFile(path);
- setAttributes(p, attrs);
- sendStatus(id, SSH_FX_OK, "");
- } catch (FileNotFoundException e) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage());
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- } catch (UnsupportedOperationException e) {
- sendStatus(id, SSH_FX_FAILURE, "");
- }
+ doSetStat(buffer, id);
break;
}
case SSH_FXP_FSETSTAT: {
- String handle = buffer.getString();
- Map<Attribute, Object> attrs = readAttrs(buffer);
- log.debug("Received SSH_FXP_FSETSTAT (handle={}, attrs={})", handle, attrs);
- try {
- Handle p = handles.get(handle);
- if (p == null) {
- sendStatus(id, SSH_FX_FAILURE, handle);
- } else {
- setAttributes(p.getFile(), attrs);
- sendStatus(id, SSH_FX_OK, "");
- }
- } catch (FileNotFoundException e) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage());
- } catch (IOException | UnsupportedOperationException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doFSetStat(buffer, id);
break;
}
case SSH_FXP_OPENDIR: {
- String path = buffer.getString();
- log.debug("Received SSH_FXP_OPENDIR (path={})", path);
- try {
- Path p = resolveFile(path);
- if (!Files.exists(p)) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, path);
- } else if (!Files.isDirectory(p)) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, path);
- } else if (!Files.isReadable(p)) {
- sendStatus(id, SSH_FX_PERMISSION_DENIED, path);
- } else {
- String handle = UUID.randomUUID().toString();
- handles.put(handle, new DirectoryHandle(p));
- sendHandle(id, handle);
- }
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doOpenDir(buffer, id);
break;
}
case SSH_FXP_READDIR: {
- String handle = buffer.getString();
- log.debug("Received SSH_FXP_READDIR (handle={})", handle);
- try {
- Handle p = handles.get(handle);
- if (!(p instanceof DirectoryHandle)) {
- sendStatus(id, SSH_FX_FAILURE, handle);
- } else if (((DirectoryHandle) p).isDone()) {
- sendStatus(id, SSH_FX_EOF, "", "");
- } else if (!Files.exists(p.getFile())) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getFile().toString());
- } else if (!Files.isDirectory(p.getFile())) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getFile().toString());
- } else if (!Files.isReadable(p.getFile())) {
- sendStatus(id, SSH_FX_PERMISSION_DENIED, p.getFile().toString());
- } else {
- DirectoryHandle dh = (DirectoryHandle) p;
- if (dh.hasNext()) {
- // There is at least one file in the directory.
- // Send only a few files at a time to not create packets of a too
- // large size or have a timeout to occur.
- sendName(id, dh);
- if (!dh.hasNext()) {
- // if no more files to send
- dh.setDone(true);
- dh.clearFileList();
- }
- } else {
- // empty directory
- dh.setDone(true);
- dh.clearFileList();
- sendStatus(id, SSH_FX_EOF, "", "");
- }
- }
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doReadDir(buffer, id);
break;
}
case SSH_FXP_REMOVE: {
- String path = buffer.getString();
- log.debug("Received SSH_FXP_REMOVE (path={})", path);
- try {
- Path p = resolveFile(path);
- if (!Files.exists(p)) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString());
- } else if (Files.isDirectory(p)) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString());
- } else {
- Files.delete(p);
- sendStatus(id, SSH_FX_OK, "");
- }
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doRemove(buffer, id);
break;
}
case SSH_FXP_MKDIR: {
- String path = buffer.getString();
- Map<Attribute, Object> attrs = readAttrs(buffer);
-
- log.debug("Received SSH_FXP_MKDIR (path={})", path);
- // attrs
- try {
- Path p = resolveFile(path);
- if (Files.exists(p)) {
- if (Files.isDirectory(p)) {
- sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, p.toString());
- } else {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString());
- }
- } else {
- Files.createDirectory(p);
- setAttributes(p, attrs);
- sendStatus(id, SSH_FX_OK, "");
- }
- } catch (AccessDeniedException e) {
- sendStatus(id, SSH_FX_PERMISSION_DENIED, e.getMessage());
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doMakeDirectory(buffer, id);
break;
}
case SSH_FXP_RMDIR: {
- String path = buffer.getString();
- log.debug("Received SSH_FXP_RMDIR (path={})", path);
- // attrs
- try {
- Path p = resolveFile(path);
- if (Files.isDirectory(p)) {
- Files.delete(p);
- sendStatus(id, SSH_FX_OK, "");
- } else {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString());
- }
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doRemoveDirectory(buffer, id);
break;
}
case SSH_FXP_REALPATH: {
- String path = buffer.getString();
- log.debug("Received SSH_FXP_REALPATH (path={})", path);
- if (path.trim().length() == 0) {
- path = ".";
- }
- try {
- Path p = resolveFile(path).toAbsolutePath().normalize();
- sendPath(id, p, false);
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage());
- } catch (IOException e) {
- e.printStackTrace();
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doRealPath(buffer, id);
break;
}
case SSH_FXP_STAT: {
- String path = buffer.getString();
- log.debug("Received SSH_FXP_STAT (path={})", path);
- try {
- Path p = resolveFile(path);
- sendAttrs(id, p, true);
- } catch (FileNotFoundException e) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage());
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doStat(buffer, id);
break;
}
case SSH_FXP_RENAME: {
- String oldPath = buffer.getString();
- String newPath = buffer.getString();
- log.debug("Received SSH_FXP_RENAME (oldPath={}, newPath={})", oldPath, newPath);
- try {
- Path o = resolveFile(oldPath);
- Path n = resolveFile(newPath);
- if (!Files.exists(o)) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, o.toString());
- } else if (Files.exists(n)) {
- sendStatus(id, SSH_FX_FAILURE, n.toString());
- } else {
- Files.move(o, n);
- sendStatus(id, SSH_FX_OK, "");
- }
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doRename(buffer, id);
break;
}
case SSH_FXP_READLINK: {
- String path = buffer.getString();
- log.debug("Received SSH_FXP_READLINK (path={})", path);
- try {
- Path f = resolveFile(path);
- String l = Files.readSymbolicLink(f).toString();
- sendLink(id, l);
- } catch (UnsupportedOperationException e) {
- sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command " + type + " is unsupported or not implemented");
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doReadLink(buffer, id);
break;
}
case SSH_FXP_SYMLINK: {
- String linkpath = buffer.getString();
- String targetpath = buffer.getString();
- log.debug("Received SSH_FXP_SYMLINK (linkpath={}, targetpath={})", linkpath, targetpath);
- try {
- Path link = resolveFile(linkpath);
- Path target = fileSystem.getPath(targetpath);
- Files.createSymbolicLink(link, target);
- sendStatus(id, SSH_FX_OK, "");
- } catch (UnsupportedOperationException e) {
- sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command " + type + " is unsupported or not implemented");
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- }
+ doSymLink(buffer, id);
+ break;
+ }
+ case SSH_FXP_LINK: {
+ doLink(buffer, id);
+ break;
+ }
+ case SSH_FXP_BLOCK: {
+ doBlock(buffer, id);
+ break;
+ }
+ case SSH_FXP_UNBLOCK: {
+ doUnblock(buffer, id);
+ break;
+ }
+ case SSH_FXP_EXTENDED: {
+ doExtended(buffer, id);
break;
}
default: {
@@ -904,6 +773,627 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
}
+ protected void doExtended(Buffer buffer, int id) throws IOException {
+ String extension = buffer.getString();
+ switch (extension) {
+ case "text-seek":
+ doTextSeek(buffer, id);
+ break;
+ case "version-select":
+ doVersionSelect(buffer, id);
+ break;
+ default:
+ log.error("Received unsupported SSH_FXP_EXTENDED({})", extension);
+ sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command SSH_FXP_EXTENDED(" + extension + ") is unsupported or not implemented");
+ break;
+ }
+ }
+
+ protected void doTextSeek(Buffer buffer, int id) throws IOException {
+ String handle = buffer.getString();
+ long line = buffer.getLong();
+ log.debug("Received SSH_FXP_EXTENDED(text-seek) (handle={}, line={})", handle, line);
+ // TODO : implement text-seek
+ sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command SSH_FXP_EXTENDED(text-seek) is unsupported or not implemented");
+ }
+
+ protected void doVersionSelect(Buffer buffer, int id) throws IOException {
+ String ver = buffer.getString();
+ log.debug("Received SSH_FXP_EXTENDED(version-select) (version={})", version);
+ if (Integer.toString(SFTP_V3).equals(ver)) {
+ version = SFTP_V3;
+ } else if (Integer.toString(SFTP_V4).equals(ver)) {
+ version = SFTP_V4;
+ } else if (Integer.toString(SFTP_V5).equals(ver)) {
+ version = SFTP_V5;
+ } else if (Integer.toString(SFTP_V6).equals(ver)) {
+ version = SFTP_V6;
+ } else {
+ sendStatus(id, SSH_FX_FAILURE, "Unsupported version " + ver);
+ return;
+ }
+ sendStatus(id, SSH_FX_OK, "");
+ }
+
+ protected void doBlock(Buffer buffer, int id) throws IOException {
+ String handle = buffer.getString();
+ long offset = buffer.getLong();
+ long length = buffer.getLong();
+ int mask = buffer.getInt();
+ log.debug("Received SSH_FXP_BLOCK (handle={}, offset={}, length={}, mask={})", new Object[] { handle, offset, length, mask });
+ try {
+ Handle p = handles.get(handle);
+ if (!(p instanceof FileHandle)) {
+ sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
+ return;
+ }
+ FileHandle fileHandle = (FileHandle) p;
+ fileHandle.lock(offset, length, mask);
+ sendStatus(id, SSH_FX_OK, "");
+ } catch (IOException | OverlappingFileLockException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doUnblock(Buffer buffer, int id) throws IOException {
+ String handle = buffer.getString();
+ long offset = buffer.getLong();
+ long length = buffer.getLong();
+ log.debug("Received SSH_FXP_UNBLOCK (handle={}, offset={}, length={})", new Object[] { handle, offset, length });
+ try {
+ Handle p = handles.get(handle);
+ if (!(p instanceof FileHandle)) {
+ sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
+ return;
+ }
+ FileHandle fileHandle = (FileHandle) p;
+ boolean found = fileHandle.unlock(offset, length);
+ sendStatus(id, found ? SSH_FX_OK : SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK, "");
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doLink(Buffer buffer, int id) throws IOException {
+ String targetpath = buffer.getString();
+ String linkpath = buffer.getString();
+ boolean symLink = buffer.getBoolean();
+ log.debug("Received SSH_FXP_LINK (linkpath={}, targetpath={}, symlink={})", new Object[] { linkpath, targetpath, symLink });
+ try {
+ Path link = resolveFile(linkpath);
+ Path target = fileSystem.getPath(targetpath);
+ if (symLink) {
+ Files.createSymbolicLink(link, target);
+ } else {
+ Files.createLink(link, target);
+ }
+ sendStatus(id, SSH_FX_OK, "");
+ } catch (UnsupportedOperationException e) {
+ sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command SSH_FXP_SYMLINK is unsupported or not implemented");
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doSymLink(Buffer buffer, int id) throws IOException {
+ String targetpath = buffer.getString();
+ String linkpath = buffer.getString();
+ log.debug("Received SSH_FXP_SYMLINK (linkpath={}, targetpath={})", linkpath, targetpath);
+ try {
+ Path link = resolveFile(linkpath);
+ Path target = fileSystem.getPath(targetpath);
+ Files.createSymbolicLink(link, target);
+ sendStatus(id, SSH_FX_OK, "");
+ } catch (UnsupportedOperationException e) {
+ sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command SSH_FXP_SYMLINK is unsupported or not implemented");
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doReadLink(Buffer buffer, int id) throws IOException {
+ String path = buffer.getString();
+ log.debug("Received SSH_FXP_READLINK (path={})", path);
+ try {
+ Path f = resolveFile(path);
+ String l = Files.readSymbolicLink(f).toString();
+ sendLink(id, l);
+ } catch (UnsupportedOperationException e) {
+ sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command SSH_FXP_READLINK is unsupported or not implemented");
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doRename(Buffer buffer, int id) throws IOException {
+ String oldPath = buffer.getString();
+ String newPath = buffer.getString();
+ int flags = 0;
+ if (version >= SFTP_V5) {
+ flags = buffer.getInt();
+ }
+ log.debug("Received SSH_FXP_RENAME (oldPath={}, newPath={}, flags={})", new Object[] { oldPath, newPath, flags });
+ try {
+ List<CopyOption> opts = new ArrayList<>();
+ if ((flags & SSH_FXP_RENAME_ATOMIC) != 0) {
+ opts.add(StandardCopyOption.ATOMIC_MOVE);
+ }
+ if ((flags & SSH_FXP_RENAME_OVERWRITE) != 0) {
+ opts.add(StandardCopyOption.REPLACE_EXISTING);
+ }
+ Path o = resolveFile(oldPath);
+ Path n = resolveFile(newPath);
+ Files.move(o, n, opts.toArray(new CopyOption[opts.size()]));
+ sendStatus(id, SSH_FX_OK, "");
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doStat(Buffer buffer, int id) throws IOException {
+ String path = buffer.getString();
+ int flags = SSH_FILEXFER_ATTR_ALL;
+ if (version >= SFTP_V4) {
+ flags = buffer.getInt();
+ }
+ log.debug("Received SSH_FXP_STAT (path={}, flags={})", path, flags);
+ try {
+ Path p = resolveFile(path);
+ sendAttrs(id, p, flags, true);
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doRealPath(Buffer buffer, int id) throws IOException {
+ String path = buffer.getString();
+ log.debug("Received SSH_FXP_REALPATH (path={})", path);
+ if (path.trim().length() == 0) {
+ path = ".";
+ }
+ try {
+ if (version < SFTP_V6) {
+ Path p = resolveFile(path).toAbsolutePath().normalize();
+ if (!Files.exists(p)) {
+ throw new FileNotFoundException(p.toString());
+ }
+ sendPath(id, p, Collections.<String, Object>emptyMap());
+ } else {
+ // Read control byte
+ int control = 0;
+ if (buffer.available() > 0) {
+ control = buffer.getByte();
+ }
+ List<String> paths = new ArrayList<>();
+ while (buffer.available() > 0) {
+ paths.add(buffer.getString());
+ }
+ // Resolve path
+ Path p = resolveFile(path);
+ for (String p2 : paths) {
+ p = p.resolve(p2);
+ }
+ p = p.toAbsolutePath().normalize();
+ Map<String, Object> attrs = Collections.emptyMap();
+ if (control == SSH_FXP_REALPATH_STAT_IF) {
+ try {
+ attrs = getAttributes(p, false);
+ } catch (IOException e) {
+ // ignore
+ }
+ } else if (control == SSH_FXP_REALPATH_STAT_ALWAYS) {
+ attrs = getAttributes(p, false);
+ }
+ sendPath(id, p, attrs);
+ }
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doRemoveDirectory(Buffer buffer, int id) throws IOException {
+ String path = buffer.getString();
+ log.debug("Received SSH_FXP_RMDIR (path={})", path);
+ // attrs
+ try {
+ Path p = resolveFile(path);
+ if (Files.isDirectory(p)) {
+ Files.delete(p);
+ sendStatus(id, SSH_FX_OK, "");
+ } else {
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString());
+ }
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doMakeDirectory(Buffer buffer, int id) throws IOException {
+ String path = buffer.getString();
+ Map<String, Object> attrs = readAttrs(buffer);
+
+ log.debug("Received SSH_FXP_MKDIR (path={})", path);
+ // attrs
+ try {
+ Path p = resolveFile(path);
+ if (Files.exists(p)) {
+ if (Files.isDirectory(p)) {
+ sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, p.toString());
+ } else {
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString());
+ }
+ } else {
+ Files.createDirectory(p);
+ setAttributes(p, attrs);
+ sendStatus(id, SSH_FX_OK, "");
+ }
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doRemove(Buffer buffer, int id) throws IOException {
+ String path = buffer.getString();
+ log.debug("Received SSH_FXP_REMOVE (path={})", path);
+ try {
+ Path p = resolveFile(path);
+ if (!Files.exists(p)) {
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString());
+ } else if (Files.isDirectory(p, LinkOption.NOFOLLOW_LINKS)) {
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString());
+ } else {
+ Files.delete(p);
+ sendStatus(id, SSH_FX_OK, "");
+ }
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doReadDir(Buffer buffer, int id) throws IOException {
+ String handle = buffer.getString();
+ log.debug("Received SSH_FXP_READDIR (handle={})", handle);
+ try {
+ Handle p = handles.get(handle);
+ if (!(p instanceof DirectoryHandle)) {
+ sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
+ } else if (((DirectoryHandle) p).isDone()) {
+ sendStatus(id, SSH_FX_EOF, "", "");
+ } else if (!Files.exists(p.getFile())) {
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getFile().toString());
+ } else if (!Files.isDirectory(p.getFile())) {
+ sendStatus(id, SSH_FX_NOT_A_DIRECTORY, p.getFile().toString());
+ } else if (!Files.isReadable(p.getFile())) {
+ sendStatus(id, SSH_FX_PERMISSION_DENIED, p.getFile().toString());
+ } else {
+ DirectoryHandle dh = (DirectoryHandle) p;
+ if (dh.hasNext()) {
+ // There is at least one file in the directory.
+ // Send only a few files at a time to not create packets of a too
+ // large size or have a timeout to occur.
+ sendName(id, dh);
+ if (!dh.hasNext()) {
+ // if no more files to send
+ dh.setDone(true);
+ dh.clearFileList();
+ }
+ } else {
+ // empty directory
+ dh.setDone(true);
+ dh.clearFileList();
+ sendStatus(id, SSH_FX_EOF, "", "");
+ }
+ }
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doOpenDir(Buffer buffer, int id) throws IOException {
+ String path = buffer.getString();
+ log.debug("Received SSH_FXP_OPENDIR (path={})", path);
+ try {
+ Path p = resolveFile(path);
+ if (!Files.exists(p)) {
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, path);
+ } else if (!Files.isDirectory(p)) {
+ sendStatus(id, SSH_FX_NOT_A_DIRECTORY, path);
+ } else if (!Files.isReadable(p)) {
+ sendStatus(id, SSH_FX_PERMISSION_DENIED, path);
+ } else {
+ String handle = UUID.randomUUID().toString();
+ handles.put(handle, new DirectoryHandle(p));
+ sendHandle(id, handle);
+ }
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doFSetStat(Buffer buffer, int id) throws IOException {
+ String handle = buffer.getString();
+ Map<String, Object> attrs = readAttrs(buffer);
+ log.debug("Received SSH_FXP_FSETSTAT (handle={}, attrs={})", handle, attrs);
+ try {
+ Handle p = handles.get(handle);
+ if (p == null) {
+ sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
+ } else {
+ setAttributes(p.getFile(), attrs);
+ sendStatus(id, SSH_FX_OK, "");
+ }
+ } catch (IOException | UnsupportedOperationException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doSetStat(Buffer buffer, int id) throws IOException {
+ String path = buffer.getString();
+ Map<String, Object> attrs = readAttrs(buffer);
+ log.debug("Received SSH_FXP_SETSTAT (path={}, attrs={})", path, attrs);
+ try {
+ Path p = resolveFile(path);
+ setAttributes(p, attrs);
+ sendStatus(id, SSH_FX_OK, "");
+ } catch (IOException | UnsupportedOperationException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doFStat(Buffer buffer, int id) throws IOException {
+ String handle = buffer.getString();
+ int flags = SSH_FILEXFER_ATTR_ALL;
+ if (version >= SFTP_V4) {
+ flags = buffer.getInt();
+ }
+ log.debug("Received SSH_FXP_FSTAT (handle={}, flags={})", handle, flags);
+ try {
+ Handle p = handles.get(handle);
+ if (p == null) {
+ sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
+ } else {
+ sendAttrs(id, p.getFile(), flags, true);
+ }
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doLStat(Buffer buffer, int id) throws IOException {
+ String path = buffer.getString();
+ int flags = SSH_FILEXFER_ATTR_ALL;
+ if (version >= SFTP_V4) {
+ flags = buffer.getInt();
+ }
+ log.debug("Received SSH_FXP_LSTAT (path={}, flags={})", path, flags);
+ try {
+ Path p = resolveFile(path);
+ sendAttrs(id, p, flags, false);
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doWrite(Buffer buffer, int id) throws IOException {
+ String handle = buffer.getString();
+ long offset = buffer.getLong();
+ byte[] data = buffer.getBytes();
+ log.debug("Received SSH_FXP_WRITE (handle={}, offset={}, data=byte[{}])", new Object[] { handle, offset, data.length });
+ try {
+ Handle p = handles.get(handle);
+ if (!(p instanceof FileHandle)) {
+ sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
+ } else {
+ FileHandle fh = (FileHandle) p;
+ fh.write(data, offset);
+ sendStatus(id, SSH_FX_OK, "");
+ }
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doRead(Buffer buffer, int id) throws IOException {
+ String handle = buffer.getString();
+ long offset = buffer.getLong();
+ int len = buffer.getInt();
+ log.debug("Received SSH_FXP_READ (handle={}, offset={}, length={})", new Object[]{handle, offset, len});
+ try {
+ Handle p = handles.get(handle);
+ if (!(p instanceof FileHandle)) {
+ sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
+ } else {
+ FileHandle fh = (FileHandle) p;
+ byte[] b = new byte[Math.min(len, Buffer.MAX_LEN)];
+ len = fh.read(b, offset);
+ if (len >= 0) {
+ Buffer buf = new Buffer(len + 5);
+ buf.putByte((byte) SSH_FXP_DATA);
+ buf.putInt(id);
+ buf.putBytes(b, 0, len);
+ send(buf);
+ } else {
+ sendStatus(id, SSH_FX_EOF, "");
+ }
+ }
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doClose(Buffer buffer, int id) throws IOException {
+ String handle = buffer.getString();
+ log.debug("Received SSH_FXP_CLOSE (handle={})", handle);
+ try {
+ Handle h = handles.get(handle);
+ if (h == null) {
+ sendStatus(id, SSH_FX_INVALID_HANDLE, handle, "");
+ } else {
+ handles.remove(handle);
+ h.close();
+ sendStatus(id, SSH_FX_OK, "", "");
+ }
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doOpen(Buffer buffer, int id) throws IOException {
+ if (session.getFactoryManager().getProperties() != null) {
+ String maxHandlesString = session.getFactoryManager().getProperties().get(MAX_OPEN_HANDLES_PER_SESSION);
+ if (maxHandlesString != null) {
+ int maxHandleCount = Integer.parseInt(maxHandlesString);
+ if (handles.size() > maxHandleCount) {
+ sendStatus(id, SSH_FX_FAILURE, "Too many open handles");
+ return;
+ }
+ }
+ }
+
+ String path = buffer.getString();
+ int access = 0;
+ if (version >= SFTP_V5) {
+ access = buffer.getInt();
+ }
+ int pflags = buffer.getInt();
+ if (version < SFTP_V5) {
+ int flags = pflags;
+ pflags = 0;
+ switch (flags & (SSH_FXF_READ | SSH_FXF_WRITE)) {
+ case SSH_FXF_READ:
+ access |= ACE4_READ_DATA | ACE4_READ_ATTRIBUTES;
+ break;
+ case SSH_FXF_WRITE:
+ access |= ACE4_WRITE_DATA | ACE4_WRITE_ATTRIBUTES;
+ break;
+ default:
+ access |= ACE4_READ_DATA | ACE4_READ_ATTRIBUTES;
+ access |= ACE4_WRITE_DATA | ACE4_WRITE_ATTRIBUTES;
+ break;
+ }
+ if ((flags & SSH_FXF_APPEND) != 0) {
+ access |= ACE4_APPEND_DATA;
+ pflags |= SSH_FXF_APPEND_DATA | SSH_FXF_APPEND_DATA_ATOMIC;
+ }
+ if ((flags & SSH_FXF_CREAT) != 0) {
+ if ((flags & SSH_FXF_EXCL) != 0) {
+ pflags |= SSH_FXF_CREATE_NEW;
+ } else if ((flags & SSH_FXF_TRUNC) != 0) {
+ pflags |= SSH_FXF_CREATE_TRUNCATE;
+ } else {
+ pflags |= SSH_FXF_OPEN_OR_CREATE;
+ }
+ } else {
+ if ((flags & SSH_FXF_TRUNC) != 0) {
+ pflags |= SSH_FXF_TRUNCATE_EXISTING;
+ } else {
+ pflags |= SSH_FXF_OPEN_EXISTING;
+ }
+ }
+ }
+ Map<String, Object> attrs = readAttrs(buffer);
+ log.debug("Received SSH_FXP_OPEN (path={}, access={}, pflags={}, attrs={})", new Object[]{path, access, pflags, attrs});
+ try {
+ Path file = resolveFile(path);
+ String handle = UUID.randomUUID().toString();
+ handles.put(handle, new FileHandle(file, pflags, access, attrs));
+ sendHandle(id, handle);
+ } catch (IOException e) {
+ sendStatus(id, e);
+ }
+ }
+
+ protected void doInit(Buffer buffer, int id) throws IOException {
+ log.debug("Received SSH_FXP_INIT (version={})", id);
+ version = id;
+ while (buffer.available() > 0) {
+ String name = buffer.getString();
+ byte[] data = buffer.getBytes();
+ extensions.put(name, data);
+ }
+
+ int low = LOWER_SFTP_IMPL;
+ int hig = HIGHER_SFTP_IMPL;
+ String all = ALL_SFTP_IMPL;
+
+ if (session.getFactoryManager().getProperties() != null) {
+ String sftpVersion = session.getFactoryManager().getProperties().get(SFTP_VERSION);
+ if (sftpVersion != null) {
+ low = hig = Integer.parseInt(sftpVersion);
+ all = sftpVersion;
+ }
+ }
+ if (version >= low) {
+ version = Math.min(version, hig);
+ buffer.clear();
+ buffer.putByte((byte) SSH_FXP_VERSION);
+ buffer.putInt(version);
+
+ // newline
+ buffer.putString("newline");
+ buffer.putString(System.getProperty("line.separator"));
+
+ // versions
+ buffer.putString("versions");
+ buffer.putString(all);
+
+ // supported
+ buffer.putString("supported");
+ buffer.putInt(5 * 4); // length of 5 integers
+ // supported-attribute-mask
+ buffer.putInt(SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_PERMISSIONS
+ | SSH_FILEXFER_ATTR_ACCESSTIME | SSH_FILEXFER_ATTR_CREATETIME
+ | SSH_FILEXFER_ATTR_MODIFYTIME | SSH_FILEXFER_ATTR_OWNERGROUP
+ | SSH_FILEXFER_ATTR_BITS);
+ // TODO: supported-attribute-bits
+ buffer.putInt(0);
+ // supported-open-flags
+ buffer.putInt(SSH_FXF_READ | SSH_FXF_WRITE | SSH_FXF_APPEND
+ | SSH_FXF_CREAT | SSH_FXF_TRUNC | SSH_FXF_EXCL);
+ // TODO: supported-access-mask
+ buffer.putInt(0);
+ // max-read-size
+ buffer.putInt(0);
+
+ // supported2
+ buffer.putString("supported2");
+ buffer.putInt(8 * 4); // length of 7 integers + 2 shorts
+ // supported-attribute-mask
+ buffer.putInt(SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_PERMISSIONS
+ | SSH_FILEXFER_ATTR_ACCESSTIME | SSH_FILEXFER_ATTR_CREATETIME
+ | SSH_FILEXFER_ATTR_MODIFYTIME | SSH_FILEXFER_ATTR_OWNERGROUP
+ | SSH_FILEXFER_ATTR_BITS);
+ // TODO: supported-attribute-bits
+ buffer.putInt(0);
+ // supported-open-flags
+ buffer.putInt(SSH_FXF_ACCESS_DISPOSITION | SSH_FXF_APPEND_DATA);
+ // TODO: supported-access-mask
+ buffer.putInt(0);
+ // max-read-size
+ buffer.putInt(0);
+ // supported-open-block-vector
+ buffer.putShort(0);
+ // supported-block-vector
+ buffer.putShort(0);
+ // attrib-extension-count
+ buffer.putInt(0);
+ // extension-count
+ buffer.putInt(0);
+
+ /*
+ buffer.putString("acl-supported");
+ buffer.putInt(4);
+ // capabilities
+ buffer.putInt(0);
+ */
+
+ send(buffer);
+ } else {
+ // We only support version >= 3 (Version 1 and 2 are not common)
+ sendStatus(id, SSH_FX_OP_UNSUPPORTED, "SFTP server only support versions " + all);
+ }
+ }
+
protected void sendHandle(int id, String handle) throws IOException {
Buffer buffer = new Buffer();
buffer.putByte((byte) SSH_FXP_HANDLE);
@@ -912,19 +1402,15 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
send(buffer);
}
- protected void sendAttrs(int id, Path file, boolean followLinks) throws IOException {
+ protected void sendAttrs(int id, Path file, int flags, boolean followLinks) throws IOException {
Buffer buffer = new Buffer();
buffer.putByte((byte) SSH_FXP_ATTRS);
buffer.putInt(id);
- writeAttrs(buffer, file, followLinks);
+ writeAttrs(buffer, file, flags, followLinks);
send(buffer);
}
- protected void sendPath(int id, Path f) throws IOException {
- sendPath(id, f, true);
- }
-
- protected void sendPath(int id, Path f, boolean sendAttrs) throws IOException {
+ protected void sendPath(int id, Path f, Map<String, Object> attrs) throws IOException {
Buffer buffer = new Buffer();
buffer.putByte((byte) SSH_FXP_NAME);
buffer.putInt(id);
@@ -934,13 +1420,19 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
if (normalizedPath.length() == 0) {
normalizedPath = "/";
}
- buffer.putString(normalizedPath);
+ buffer.putString(normalizedPath, StandardCharsets.UTF_8);
f = resolveFile(normalizedPath);
if (f.getFileName() == null) {
f = resolveFile(".");
}
- buffer.putString(getLongName(f, sendAttrs)); // Format specified in the specs
- buffer.putInt(0);
+ if (version == SFTP_V3) {
+ buffer.putString(getLongName(f, attrs), StandardCharsets.UTF_8); // Format specified in the specs
+ buffer.putInt(0);
+ } else if (version >= SFTP_V4) {
+ writeAttrs(buffer, attrs);
+ } else {
+ throw new IllegalStateException();
+ }
send(buffer);
}
@@ -965,9 +1457,11 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
int nb = 0;
while (files.hasNext() && buffer.wpos() < MAX_PACKET_LENGTH) {
Path f = files.next();
- buffer.putString(f.getFileName().toString());
- buffer.putString(getLongName(f)); // Format specified in the specs
- writeAttrs(buffer, f, false);
+ buffer.putString(f.getFileName().toString(), StandardCharsets.UTF_8);
+ if (version == SFTP_V3) {
+ buffer.putString(getLongName(f), StandardCharsets.UTF_8); // Format specified in the specs
+ }
+ writeAttrs(buffer, f, SSH_FILEXFER_ATTR_ALL, false);
nb++;
}
int oldpos = buffer.wpos();
@@ -982,21 +1476,22 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
private String getLongName(Path f, boolean sendAttrs) throws IOException {
- Map<Attribute, Object> attributes;
+ Map<String, Object> attributes;
if (sendAttrs) {
attributes = getAttributes(f, false);
} else {
- attributes = new HashMap<>();
- attributes.put(Attribute.Owner, "owner");
- attributes.put(Attribute.Group, "group");
- attributes.put(Attribute.Size, (long) 0);
- attributes.put(Attribute.IsDirectory, false);
- attributes.put(Attribute.IsSymbolicLink, false);
- attributes.put(Attribute.IsRegularFile, false);
- attributes.put(Attribute.Permissions, EnumSet.noneOf(Permission.class));
- attributes.put(Attribute.LastModifiedTime, (long) 0);
- }
- String username = (String) attributes.get(Attribute.Owner);
+ attributes = Collections.emptyMap();
+ }
+ return getLongName(f, attributes);
+ }
+
+ private String getLongName(Path f, Map<String, Object> attributes) throws IOException {
+ String username;
+ if (attributes.containsKey("owner")) {
+ username = attributes.get("owner").toString();
+ } else {
+ username = "owner";
+ }
if (username.length() > 8) {
username = username.substring(0, 8);
} else {
@@ -1004,7 +1499,12 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
username = username + " ";
}
}
- String group = (String) attributes.get(Attribute.Group);
+ String group;
+ if (attributes.containsKey("group")) {
+ group = attributes.get("group").toString();
+ } else {
+ group = "group";
+ }
if (group.length() > 8) {
group = group.substring(0, 8);
} else {
@@ -1013,27 +1513,25 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
}
- long length = (Long) attributes.get(Attribute.Size);
+ Long length = (Long) attributes.get("size");
+ if (length == null) {
+ length = 0l;
+ }
String lengthString = String.format("%1$8s", length);
- boolean isDirectory = (Boolean) attributes.get(Attribute.IsDirectory);
- boolean isLink = (Boolean) attributes.get(Attribute.IsSymbolicLink);
- int perms = getPermissions(attributes);
+ Boolean isDirectory = (Boolean) attributes.get("isDirectory");
+ Boolean isLink = (Boolean) attributes.get("isSymbolicLink");
+ Set<PosixFilePermission> perms = (Set<PosixFilePermission>) attributes.get("permissions");
+ if (perms == null) {
+ perms = new HashSet<>();
+ }
StringBuilder sb = new StringBuilder();
- sb.append(isDirectory ? "d" : isLink ? "l" : "-");
- sb.append((perms & S_IRUSR) != 0 ? "r" : "-");
- sb.append((perms & S_IWUSR) != 0 ? "w" : "-");
- sb.append((perms & S_IXUSR) != 0 ? "x" : "-");
- sb.append((perms & S_IRGRP) != 0 ? "r" : "-");
- sb.append((perms & S_IWGRP) != 0 ? "w" : "-");
- sb.append((perms & S_IXGRP) != 0 ? "x" : "-");
- sb.append((perms & S_IROTH) != 0 ? "r" : "-");
- sb.append((perms & S_IWOTH) != 0 ? "w" : "-");
- sb.append((perms & S_IXOTH) != 0 ? "x" : "-");
+ sb.append((isDirectory != null && isDirectory) ? "d" : (isLink != null && isLink) ? "l" : "-");
+ sb.append(PosixFilePermissions.toString(perms));
sb.append(" ");
- sb.append(attributes.containsKey(Attribute.NLink)
- ? attributes.get(Attribute.NLink) : "1");
+ sb.append(attributes.containsKey("nlink")
+ ? attributes.get("nlink") : "1");
sb.append(" ");
sb.append(username);
sb.append(" ");
@@ -1041,73 +1539,50 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
sb.append(" ");
sb.append(lengthString);
sb.append(" ");
- sb.append(getUnixDate((Long) attributes.get(Attribute.LastModifiedTime)));
+ sb.append(getUnixDate((FileTime) attributes.get("lastModifiedTime")));
sb.append(" ");
sb.append(f.getFileName().toString());
return sb.toString();
}
- protected Map<Attribute, Object> getPermissions(int perms) {
- Map<Attribute, Object> attrs = new HashMap<>();
- if ((perms & S_IFMT) == S_IFREG) {
- attrs.put(Attribute.IsRegularFile, Boolean.TRUE);
- }
- if ((perms & S_IFMT) == S_IFDIR) {
- attrs.put(Attribute.IsDirectory, Boolean.TRUE);
- }
- if ((perms & S_IFMT) == S_IFLNK) {
- attrs.put(Attribute.IsSymbolicLink, Boolean.TRUE);
- }
- EnumSet<Permission> p = EnumSet.noneOf(Permission.class);
- if ((perms & S_IRUSR) != 0) {
- p.add(Permission.UserRead);
- }
- if ((perms & S_IWUSR) != 0) {
- p.add(Permission.UserWrite);
- }
- if ((perms & S_IXUSR) != 0) {
- p.add(Permission.UserExecute);
- }
- if ((perms & S_IRGRP) != 0) {
- p.add(Permission.GroupRead);
- }
- if ((perms & S_IWGRP) != 0) {
- p.add(Permission.GroupWrite);
- }
- if ((perms & S_IXGRP) != 0) {
- p.add(Permission.GroupExecute);
- }
- if ((perms & S_IROTH) != 0) {
- p.add(Permission.OthersRead);
- }
- if ((perms & S_IWOTH) != 0) {
- p.add(Permission.OthersWrite);
- }
- if ((perms & S_IXOTH) != 0) {
- p.add(Permission.OthersExecute);
- }
- attrs.put(Attribute.Permissions, p);
- return attrs;
- }
-
- protected int getPermissions(Map<Attribute, Object> attributes) {
- boolean isReg = (Boolean) attributes.get(Attribute.IsRegularFile);
- boolean isDir = (Boolean) attributes.get(Attribute.IsDirectory);
- boolean isLnk = (Boolean) attributes.get(Attribute.IsSymbolicLink);
+ protected int attributesToPermissions(Map<String, Object> attributes) {
+ boolean isReg = getBool((Boolean) attributes.get("isRegularFile"));
+ boolean isDir = getBool((Boolean) attributes.get("isDirectory"));
+ boolean isLnk = getBool((Boolean) attributes.get("isSymbolicLink"));
int pf = 0;
- EnumSet<Permission> perms = (EnumSet<Permission>) attributes.get(Attribute.Permissions);
- for (Permission p : perms) {
- switch (p) {
- case UserRead: pf |= S_IRUSR; break;
- case UserWrite: pf |= S_IWUSR; break;
- case UserExecute: pf |= S_IXUSR; break;
- case GroupRead: pf |= S_IRGRP; break;
- case GroupWrite: pf |= S_IWGRP; break;
- case GroupExecute: pf |= S_IXGRP; break;
- case OthersRead: pf |= S_IROTH; break;
- case OthersWrite: pf |= S_IWOTH; break;
- case OthersExecute: pf |= S_IXOTH; break;
+ Set<PosixFilePermission> perms = (Set<PosixFilePermission>) attributes.get("permissions");
+ if (perms != null) {
+ for (PosixFilePermission p : perms) {
+ switch (p) {
+ case OWNER_READ:
+ pf |= S_IRUSR;
+ break;
+ case OWNER_WRITE:
+ pf |= S_IWUSR;
+ break;
+ case OWNER_EXECUTE:
+ pf |= S_IXUSR;
+ break;
+ case GROUP_READ:
+ pf |= S_IRGRP;
+ break;
+ case GROUP_WRITE:
+ pf |= S_IWGRP;
+ break;
+ case GROUP_EXECUTE:
+ pf |= S_IXGRP;
+ break;
+ case OTHERS_READ:
+ pf |= S_IROTH;
+ break;
+ case OTHERS_WRITE:
+ pf |= S_IWOTH;
+ break;
+ case OTHERS_EXECUTE:
+ pf |= S_IXOTH;
+ break;
+ }
}
}
pf |= isReg ? S_IFREG : 0;
@@ -1116,41 +1591,93 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
return pf;
}
- protected void writeAttrs(Buffer buffer, Path file, boolean followLinks) throws IOException {
- if (!Files.exists(file)) {
+ protected void writeAttrs(Buffer buffer, Path file, int flags, boolean followLinks) throws IOException {
+ LinkOption[] options = followLinks ? new LinkOption[0] : new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
+ if (!Files.exists(file, options)) {
throw new FileNotFoundException(file.toString());
}
- Map<Attribute, Object> attributes = getAttributes(file, followLinks);
- boolean isReg = getBool((Boolean) attributes.get(Attribute.IsRegularFile));
- boolean isDir = getBool((Boolean) attributes.get(Attribute.IsDirectory));
- boolean isLnk = getBool((Boolean) attributes.get(Attribute.IsSymbolicLink));
- int flags = 0;
- if ((isReg || isLnk) && attributes.containsKey(Attribute.Size)) {
- flags |= SSH_FILEXFER_ATTR_SIZE;
- }
- if (attributes.containsKey(Attribute.Uid) && attributes.containsKey(Attribute.Gid)) {
- flags |= SSH_FILEXFER_ATTR_UIDGID;
- }
- if (attributes.containsKey(Attribute.Permissions)) {
- flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
- }
- if (attributes.containsKey(Attribute.LastAccessTime) && attributes.containsKey(Attribute.LastModifiedTime)) {
- flags |= SSH_FILEXFER_ATTR_ACMODTIME;
- }
- buffer.putInt(flags);
- if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
- buffer.putLong((Long) attributes.get(Attribute.Size));
- }
- if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
- buffer.putInt((Integer) attributes.get(Attribute.Uid));
- buffer.putInt((Integer) attributes.get(Attribute.Gid));
- }
- if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
- buffer.putInt(getPermissions(attributes));
- }
- if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
- buffer.putInt(((Long) attributes.get(Attribute.LastAccessTime)) / 1000);
- buffer.putInt(((Long) attributes.get(Attribute.LastModifiedTime)) / 1000);
+ Map<String, Object> attributes = getAttributes(file, flags, followLinks);
+ writeAttrs(buffer, attributes);
+ }
+
+ protected void writeAttrs(Buffer buffer, Map<String, Object> attributes) throws IOException {
+ if (version == SFTP_V3) {
+ boolean isReg = getBool((Boolean) attributes.get("isRegularFile"));
+ boolean isDir = getBool((Boolean) attributes.get("isDirectory"));
+ boolean isLnk = getBool((Boolean) attributes.get("isSymbolicLink"));
+ int flags =
+ ((isReg || isLnk) && attributes.containsKey("size") ? SSH_FILEXFER_ATTR_SIZE : 0) |
+ (attributes.containsKey("uid") && attributes.containsKey("gid") ? SSH_FILEXFER_ATTR_UIDGID : 0) |
+ (attributes.containsKey("permissions") ? SSH_FILEXFER_ATTR_PERMISSIONS : 0) |
+ (attributes.containsKey("lastModifiedTime") && attributes.containsKey("lastAccessTime") ? SSH_FILEXFER_ATTR_ACMODTIME : 0);
+ buffer.putInt(flags);
+ if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
+ buffer.putLong((Long) attributes.get("size"));
+ }
+ if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
+ buffer.putInt((Integer) attributes.get("uid"));
+ buffer.putInt((Integer) attributes.get("gid"));
+ }
+ if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+ buffer.putInt(attributesToPermissions(attributes));
+ }
+ if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
+ buffer.putInt(((FileTime) attributes.get("lastAccessTime")).to(TimeUnit.SECONDS));
+ buffer.putInt(((FileTime) attributes.get("lastModifiedTime")).to(TimeUnit.SECONDS));
+ }
+ } else if (version >= SFTP_V4) {
+ boolean isReg = getBool((Boolean) attributes.get("isRegularFile"));
+ boolean isDir = getBool((Boolean) attributes.get("isDirectory"));
+ boolean isLnk = getBool((Boolean) attributes.get("isSymbolicLink"));
+ int flags =
+ ((isReg || isLnk) && attributes.containsKey("size") ? SSH_FILEXFER_ATTR_SIZE : 0) |
+ (attributes.containsKey("owner") && attributes.containsKey("group") ? SSH_FILEXFER_ATTR_OWNERGROUP : 0) |
+ (attributes.containsKey("permissions") ? SSH_FILEXFER_ATTR_PERMISSIONS : 0) |
+ (attributes.containsKey("lastModifiedTime") ? SSH_FILEXFER_ATTR_MODIFYTIME : 0) |
+ (attributes.containsKey("creationTime") ? SSH_FILEXFER_ATTR_CREATETIME : 0) |
+ (attributes.containsKey("lastAccessTime") ? SSH_FILEXFER_ATTR_ACCESSTIME : 0);
+ buffer.putInt(flags);
+ buffer.putByte((byte) (isReg ? SSH_FILEXFER_TYPE_REGULAR :
+ isDir ? SSH_FILEXFER_TYPE_DIRECTORY :
+ isLnk ? SSH_FILEXFER_TYPE_SYMLINK :
+ SSH_FILEXFER_TYPE_UNKNOWN));
+ if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
+ buffer.putLong((Long) attributes.get("size"));
+ }
+ if ((flags & SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
+ buffer.putString(attributes.get("owner").toString(), StandardCharsets.UTF_8);
+ buffer.putString(attributes.get("group").toString(), StandardCharsets.UTF_8);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+ buffer.putInt(attributesToPermissions(attributes));
+ }
+ if ((flags & SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
+ buffer.putLong(((FileTime) attributes.get("lastAccessTime")).to(TimeUnit.SECONDS));
+ if ((flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
+ long nanos = ((FileTime) attributes.get("lastAccessTime")).to(TimeUnit.NANOSECONDS);
+ nanos = nanos % TimeUnit.SECONDS.toNanos(1);
+ buffer.putInt((int) nanos);
+ }
+ }
+ if ((flags & SSH_FILEXFER_ATTR_CREATETIME) != 0) {
+ buffer.putLong(((FileTime) attributes.get("creationTime")).to(TimeUnit.SECONDS));
+ if ((flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
+ long nanos = ((FileTime) attributes.get("creationTime")).to(TimeUnit.NANOSECONDS);
+ nanos = nanos % TimeUnit.SECONDS.toNanos(1);
+ buffer.putInt((int) nanos);
+ }
+ }
+ if ((flags & SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
+ buffer.putLong(((FileTime) attributes.get("lastModifiedTime")).to(TimeUnit.SECONDS));
+ if ((flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
+ long nanos = ((FileTime) attributes.get("lastModifiedTime")).to(TimeUnit.NANOSECONDS);
+ nanos = nanos % TimeUnit.SECONDS.toNanos(1);
+ buffer.putInt((int) nanos);
+ }
+ }
+ // TODO: acls
+ // TODO: bits
+ // TODO: extended
}
}
@@ -1158,98 +1685,51 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
return bool != null && bool;
}
- protected Map<Attribute, Object> getAttributes(Path file, boolean followLinks) throws IOException {
- String[] attrs = new String[] { "unix:*", "posix:*", "*" };
- Map<String, Object> a = null;
- for (String attr : attrs) {
- try {
- a = Files.readAttributes(
- file, attr,
- followLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS});
- break;
- } catch (UnsupportedOperationException e) {
- // Ignore
- }
- }
- if (a == null) {
- throw new IllegalStateException();
- }
- Map<Attribute, Object> map = new HashMap<>();
- map.put(Attribute.Size, a.get("size"));
- if (a.containsKey("uid")) {
- map.put(Attribute.Uid, a.get("uid"));
- }
- if (a.containsKey("owner")) {
- map.put(Attribute.Owner, ((UserPrincipal) a.get("owner")).getName());
- } else {
- map.put(Attribute.Owner, session.getUsername());
- }
- if (a.containsKey("gid")) {
- map.put(Attribute.Gid, a.get("gid"));
- }
- if (a.containsKey("group")) {
- map.put(Attribute.Group, ((GroupPrincipal) a.get("group")).getName());
- } else {
- map.put(Attribute.Group, session.getUsername());
- }
- if (a.containsKey("nlink")) {
- map.put(Attribute.NLink, a.get("nlink"));
- }
- map.put(Attribute.IsDirectory, a.get("isDirectory"));
- map.put(Attribute.IsRegularFile, a.get("isRegularFile"));
- map.put(Attribute.IsSymbolicLink, a.get("isSymbolicLink"));
- map.put(Attribute.CreationTime, ((FileTime) a.get("creationTime")).toMillis());
- map.put(Attribute.LastModifiedTime, ((FileTime) a.get("lastModifiedTime")).toMillis());
- map.put(Attribute.LastAccessTime, ((FileTime) a.get("lastAccessTime")).toMillis());
- if (a.containsKey("permissions")) {
- map.put(Attribute.Permissions, fromPerms((Set<PosixFilePermission>) a.get("permissions")));
+ protected Map<String, Object> getAttributes(Path file, boolean followLinks) throws IOException {
+ return getAttributes(file, SSH_FILEXFER_ATTR_ALL, followLinks);
+ }
+
+ protected Map<String, Object
<TRUNCATED>
[13/15] mina-sshd git commit: [SSHD-378] Switch to nio FileSystem api
for commands (scp and sftp subsystem)
Posted by gn...@apache.org.
[SSHD-378] Switch to nio FileSystem api for commands (scp and sftp subsystem)
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/345604ba
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/345604ba
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/345604ba
Branch: refs/heads/master
Commit: 345604ba5804ce5ef03242ee9e83757b536e1274
Parents: cc4d787
Author: Guillaume Nodet <gn...@apache.org>
Authored: Mon Dec 1 08:25:25 2014 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Mon Feb 23 16:21:30 2015 +0100
----------------------------------------------------------------------
.../java.nio.file.spi.FileSystemProvider | 1 +
.../sshd/client/scp/DefaultScpClient.java | 82 ++-
.../client/sftp/SftpFileSystemProvider.java | 8 +-
.../sshd/common/file/FileSystemAware.java | 6 +-
.../sshd/common/file/FileSystemFactory.java | 11 +-
.../apache/sshd/common/file/FileSystemView.java | 53 --
.../org/apache/sshd/common/file/SshFile.java | 234 ------
.../file/nativefs/NameEqualsFileFilter.java | 61 --
.../file/nativefs/NativeFileSystemFactory.java | 29 +-
.../file/nativefs/NativeFileSystemView.java | 431 -----------
.../common/file/nativefs/NativeSshFile.java | 701 ------------------
.../common/file/nativefs/NativeSshFileNio.java | 260 -------
.../sshd/common/file/root/RootedFileSystem.java | 76 ++
.../file/root/RootedFileSystemProvider.java | 310 ++++++++
.../sshd/common/file/root/RootedPath.java | 48 ++
.../virtualfs/VirtualFileSystemFactory.java | 13 +-
.../org/apache/sshd/common/scp/ScpHelper.java | 306 +++++---
.../sshd/server/channel/ChannelSession.java | 2 +-
.../apache/sshd/server/command/ScpCommand.java | 20 +-
.../apache/sshd/server/sftp/SftpSubsystem.java | 707 ++++++++++++-------
.../org/apache/sshd/SftpFileSystemTest.java | 16 +-
.../src/test/java/org/apache/sshd/SftpTest.java | 2 +-
.../file/nativefs/NativeFileSystemViewTest.java | 115 ---
.../common/file/nativefs/NativeSshFileTest.java | 50 --
.../file/virtualfs/VirtualFileSystemTest.java | 107 ---
25 files changed, 1172 insertions(+), 2477 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/filtered-resources/META-INF/services/java.nio.file.spi.FileSystemProvider
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/filtered-resources/META-INF/services/java.nio.file.spi.FileSystemProvider b/sshd-core/src/main/filtered-resources/META-INF/services/java.nio.file.spi.FileSystemProvider
index 08d4b4a..7691b7c 100644
--- a/sshd-core/src/main/filtered-resources/META-INF/services/java.nio.file.spi.FileSystemProvider
+++ b/sshd-core/src/main/filtered-resources/META-INF/services/java.nio.file.spi.FileSystemProvider
@@ -18,3 +18,4 @@
##
org.apache.sshd.client.sftp.SftpFileSystemProvider
+org.apache.sshd.common.file.root.RootedFileSystemProvider
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
index 7527102..c0b23a0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
@@ -20,10 +20,12 @@ package org.apache.sshd.client.scp;
import java.io.IOException;
import java.io.InterruptedIOException;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import org.apache.sshd.ClientSession;
@@ -31,8 +33,6 @@ import org.apache.sshd.client.ScpClient;
import org.apache.sshd.client.channel.ChannelExec;
import org.apache.sshd.common.SshException;
import org.apache.sshd.common.file.FileSystemFactory;
-import org.apache.sshd.common.file.FileSystemView;
-import org.apache.sshd.common.file.SshFile;
import org.apache.sshd.common.scp.ScpHelper;
/**
@@ -81,33 +81,41 @@ public class DefaultScpClient implements ScpClient {
sb.append(remote);
FileSystemFactory factory = clientSession.getFactoryManager().getFileSystemFactory();
- FileSystemView fs = factory.createFileSystemView(clientSession);
- SshFile target = fs.getFile(local);
- if (options.contains(Option.TargetIsDirectory)) {
- if (!target.doesExist()) {
- throw new SshException("Target directory " + target.toString() + " does not exists");
- }
- if (!target.isDirectory()) {
- throw new SshException("Target directory " + target.toString() + " is not a directory");
+ FileSystem fs = factory.createFileSystem(clientSession);
+ try {
+ Path target = fs.getPath(local);
+ if (options.contains(Option.TargetIsDirectory)) {
+ if (!Files.exists(target)) {
+ throw new SshException("Target directory " + target.toString() + " does not exists");
+ }
+ if (!Files.isDirectory(target)) {
+ throw new SshException("Target directory " + target.toString() + " is not a directory");
+ }
}
- }
- ChannelExec channel = clientSession.createExecChannel(sb.toString());
- try {
- channel.open().await();
- } catch (InterruptedException e) {
- throw (IOException) new InterruptedIOException().initCause(e);
- }
+ ChannelExec channel = clientSession.createExecChannel(sb.toString());
+ try {
+ channel.open().await();
+ } catch (InterruptedException e) {
+ throw (IOException) new InterruptedIOException().initCause(e);
+ }
- ScpHelper helper = new ScpHelper(channel.getInvertedOut(), channel.getInvertedIn(), fs);
+ ScpHelper helper = new ScpHelper(channel.getInvertedOut(), channel.getInvertedIn(), fs);
- helper.receive(target,
- options.contains(Option.Recursive),
- options.contains(Option.TargetIsDirectory),
- options.contains(Option.PreserveAttributes),
- ScpHelper.DEFAULT_RECEIVE_BUFFER_SIZE);
+ helper.receive(target,
+ options.contains(Option.Recursive),
+ options.contains(Option.TargetIsDirectory),
+ options.contains(Option.PreserveAttributes),
+ ScpHelper.DEFAULT_RECEIVE_BUFFER_SIZE);
- channel.close(false);
+ channel.close(false);
+ } finally {
+ try {
+ fs.close();
+ } catch (UnsupportedOperationException e) {
+ // Ignore
+ }
+ }
}
public void upload(String local, String remote, Option... options) throws IOException {
@@ -151,19 +159,25 @@ public class DefaultScpClient implements ScpClient {
}
FileSystemFactory factory = clientSession.getFactoryManager().getFileSystemFactory();
- FileSystemView fs = factory.createFileSystemView(clientSession);
- ScpHelper helper = new ScpHelper(channel.getInvertedOut(), channel.getInvertedIn(), fs);
-
- helper.send(Arrays.asList(local),
- options.contains(Option.Recursive),
- options.contains(Option.PreserveAttributes),
- ScpHelper.DEFAULT_SEND_BUFFER_SIZE);
-
+ FileSystem fs = factory.createFileSystem(clientSession);
+ try {
+ ScpHelper helper = new ScpHelper(channel.getInvertedOut(), channel.getInvertedIn(), fs);
+ helper.send(Arrays.asList(local),
+ options.contains(Option.Recursive),
+ options.contains(Option.PreserveAttributes),
+ ScpHelper.DEFAULT_SEND_BUFFER_SIZE);
+ } finally {
+ try {
+ fs.close();
+ } catch (UnsupportedOperationException e) {
+ // Ignore
+ }
+ }
channel.close(false);
}
private List<Option> options(Option... options) {
- List<Option> opts = new ArrayList<Option>();
+ List<Option> opts = new ArrayList<>();
if (options != null) {
opts.addAll(Arrays.asList(options));
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
index 07c0dc2..ca60647 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
@@ -240,9 +240,11 @@ public class SftpFileSystemProvider extends FileSystemProvider {
@Override
public void close() throws IOException {
- sftp.close(handle);
- sftp.close();
- pos = -1;
+ if (pos >= 0) {
+ sftp.close(handle);
+ sftp.close();
+ pos = -1;
+ }
}
};
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemAware.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemAware.java b/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemAware.java
index ae0d66b..b3a7bf4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemAware.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemAware.java
@@ -18,6 +18,8 @@
*/
package org.apache.sshd.common.file;
+import java.nio.file.FileSystem;
+
/**
* Interface that can be implemented by a command to be able to access the
* file system in which this command will be used.
@@ -26,7 +28,7 @@ public interface FileSystemAware {
/**
* Set the file system in which this shell will be executed.
*
- * @param view
+ * @param fileSystem
*/
- void setFileSystemView(FileSystemView view);
+ void setFileSystem(FileSystem fileSystem);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemFactory.java
index 6acc99c..6692c44 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemFactory.java
@@ -20,22 +20,23 @@
package org.apache.sshd.common.file;
import java.io.IOException;
+import java.nio.file.FileSystem;
import org.apache.sshd.common.Session;
/**
- * Factory for file system implementations - it returns the file system view for user.
+ * Factory for file system implementations - it returns the file system for user.
*
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/
public interface FileSystemFactory {
/**
- * Create user specific file system view.
+ * Create user specific file system.
* @param session The session created for the user
- * @return The current {@link FileSystemView} for the provided session
- * @throws IOException when the filesystem view can not be created
+ * @return The current {@link FileSystem} for the provided session
+ * @throws java.io.IOException when the filesystem can not be created
*/
- FileSystemView createFileSystemView(Session session) throws IOException;
+ FileSystem createFileSystem(Session session) throws IOException;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemView.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemView.java b/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemView.java
deleted file mode 100644
index e2de5d5..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemView.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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;
-
-/**
- * This is an abstraction over the user file system view.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public interface FileSystemView {
-
- /**
- * Get file object.
- * @param file The path to the file to get
- * @return The {@link SshFile} for the provided path
- */
- SshFile getFile(String file);
-
- /**
- * Get file object.
- * @param baseDir The reference towards which the file should be resolved
- * @param file The path to the file to get
- * @return The {@link SshFile} for the provided path
- */
- SshFile getFile(SshFile baseDir, String file);
-
- /**
- * Return a view of this file system which has a single root
- * and uses '/' as a file separator.
- * If the file system has multiple roots, they are mapped
- * to directories inside that single root.
- * @return a normalized file system view
- */
- FileSystemView getNormalizedView();
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/SshFile.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/SshFile.java b/sshd-core/src/main/java/org/apache/sshd/common/file/SshFile.java
deleted file mode 100644
index a7a2372..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/SshFile.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * 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;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * This is the file abstraction used by the server.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public interface SshFile {
-
- //
- // File attributes
- //
- enum Attribute {
- Size, // long
- Uid, // int
- Owner, // String
- Gid, // int
- Group, // String
- IsDirectory, // boolean
- IsRegularFile, // boolean
- IsSymbolicLink, // boolean
- Permissions, // EnumSet<Permission>
- CreationTime, // long
- LastModifiedTime, // long
- LastAccessTime, // long
- NLink // int
- }
-
- //
- // File permissions
- //
- enum Permission {
- UserRead,
- UserWrite,
- UserExecute,
- GroupRead,
- GroupWrite,
- GroupExecute,
- OthersRead,
- OthersWrite,
- OthersExecute
- }
-
-
- /**
- * Get the full path from the base directory of the FileSystemView.
- * @return a path where the path separator is '/' (even if the operating system
- * uses another character as path separator).
- */
- String getAbsolutePath();
-
- /**
- * Get the file name of the file
- * @return the last part of the file path (the part after the last '/').
- */
- String getName();
-
- Map<Attribute,Object> getAttributes(boolean followLinks) throws IOException;
-
- void setAttributes(Map<Attribute, Object> attributes) throws IOException;
-
- Object getAttribute(Attribute attribute, boolean followLinks) throws IOException;
-
- void setAttribute(Attribute attribute, Object value) throws IOException;
-
- String readSymbolicLink() throws IOException;
-
- void createSymbolicLink(SshFile destination) throws IOException;
-
-
- /**
- * Get the owner name of the file
- * @return the name of the owner.
- */
- String getOwner();
-
- /**
- * Is it a directory?
- * @return true if the {@link SshFile} is a directory
- */
- boolean isDirectory();
-
- /**
- * Is it a file?
- * @return true if the {@link SshFile} is a file, false if it is a directory
- */
- boolean isFile();
-
- /**
- * Does this file exists?
- * @return true if the {@link SshFile} exists
- */
- boolean doesExist();
-
- /**
- * Has read permission?
- * @return true if the {@link SshFile} is readable by the user
- */
- boolean isReadable();
-
- /**
- * Has write permission?
- * @return true if the {@link SshFile} is writable by the user
- */
- boolean isWritable();
-
- /**
- * Has exec permission?
- * @return true if the {@link SshFile} is executable by the user
- */
- boolean isExecutable();
-
- /**
- * Has delete permission?
- * @return true if the {@link SshFile} is removable by the user
- */
- boolean isRemovable();
-
- /**
- * Get the immediate parent. Returns the root directory if the current file is the root.
- * @return
- */
- SshFile getParentFile();
-
- /**
- * Get last modified time in UTC.
- * @return The timestamp of the last modified time for the {@link SshFile}
- */
- long getLastModified();
-
- /**
- * Set the last modified time stamp of a file
- * @param time The last modified time, in milliseconds since the epoch. See {@link java.io.File#setLastModified(long)}.
- */
- boolean setLastModified(long time);
-
- /**
- * Get file size.
- * @return The size of the {@link SshFile} in bytes
- */
- long getSize();
-
- /**
- * Create directory.
- * @return true if the operation was successful
- */
- boolean mkdir();
-
- /**
- * Delete file.
- * @return true if the operation was successful
- */
- boolean delete();
-
- /**
- * Create the file.
- * @return true if the file has been created and false if it already exist
- * @throws java.io.IOException if something wrong happen
- */
- boolean create() throws IOException;
-
- /**
- * Truncate the file to length 0.
- * @throws java.io.IOException if something wrong happen
- */
- void truncate() throws IOException;
-
- /**
- * Move file.
- * @param destination The target {@link SshFile} to move the current {@link SshFile} to
- * @return true if the operation was successful
- */
- boolean move(SshFile destination);
-
- /**
- * List file objects. If not a directory or does not exist, null will be
- * returned. Files must be returned in alphabetical order.
- * List must be immutable.
- * @return The {@link java.util.List} of {@link SshFile}s
- */
- List<SshFile> listSshFiles();
-
- /**
- * Create output stream for writing.
- * @param offset The number of bytes at where to start writing.
- * If the file is not random accessible,
- * any offset other than zero will throw an exception.
- * @return An {@link java.io.OutputStream} used to write to the {@link SshFile}
- * @throws java.io.IOException
- */
- OutputStream createOutputStream(long offset) throws IOException;
-
- /**
- * Create input stream for reading.
- * @param offset The number of bytes of where to start reading.
- * If the file is not random accessible,
- * any offset other than zero will throw an exception.
- * @return An {@link java.io.InputStream} used to read the {@link SshFile}
- * @throws java.io.IOException
- */
- InputStream createInputStream(long offset) throws IOException;
-
- /**
- * Handle post-handle-close functionality.
- * @throws IOException
- */
- void handleClose() throws IOException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NameEqualsFileFilter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NameEqualsFileFilter.java b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NameEqualsFileFilter.java
deleted file mode 100644
index 65693fb..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NameEqualsFileFilter.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.nativefs;
-
-import java.io.File;
-import java.io.FileFilter;
-
-/**
- * <strong>Internal class, do not use directly.</strong>
- *
- * FileFilter used for simple file name matching
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class NameEqualsFileFilter implements FileFilter {
-
- private String nameToMatch;
-
- private boolean caseInsensitive = false;
-
- /**
- * Constructor
- *
- * @param nameToMatch
- * The exact file name to match
- * @param caseInsensitive
- * Wether that match should be case insensitive
- */
- public NameEqualsFileFilter(final String nameToMatch,
- final boolean caseInsensitive) {
- this.nameToMatch = nameToMatch;
- this.caseInsensitive = caseInsensitive;
- }
-
- public boolean accept(final File file) {
-
- if (caseInsensitive) {
- return file.getName().equalsIgnoreCase(nameToMatch);
- } else {
- return file.getName().equals(nameToMatch);
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java
index 50e7f73..1561dbc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java
@@ -20,10 +20,11 @@
package org.apache.sshd.common.file.nativefs;
import java.io.File;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
import org.apache.sshd.common.Session;
import org.apache.sshd.common.file.FileSystemFactory;
-import org.apache.sshd.common.file.FileSystemView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,8 +39,6 @@ public class NativeFileSystemFactory implements FileSystemFactory {
private boolean createHome;
- private boolean caseInsensitive;
-
/**
* Should the home directories be created automatically
* @return true if the file system will create the home directory if not available
@@ -58,27 +57,9 @@ public class NativeFileSystemFactory implements FileSystemFactory {
}
/**
- * Is this file system case insensitive.
- * Enabling might cause problems when working against case-sensitive file systems, like on Linux
- * @return true if this file system is case insensitive
- */
- public boolean isCaseInsensitive() {
- return caseInsensitive;
- }
-
- /**
- * Should this file system be case insensitive.
- * Enabling might cause problems when working against case-sensitive file systems, like on Linux
- * @param caseInsensitive true if this file system should be case insensitive
- */
- public void setCaseInsensitive(boolean caseInsensitive) {
- this.caseInsensitive = caseInsensitive;
- }
-
- /**
* Create the appropriate user file system view.
*/
- public FileSystemView createFileSystemView(Session session) {
+ public FileSystem createFileSystem(Session session) {
String userName = session.getUsername();
// create home if does not exist
if (createHome) {
@@ -95,7 +76,7 @@ public class NativeFileSystemFactory implements FileSystemFactory {
}
}
- FileSystemView fsView = new NativeFileSystemView(userName, caseInsensitive);
- return fsView;
+ return FileSystems.getDefault();
}
+
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java
deleted file mode 100644
index 9337349..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- * 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.nativefs;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.sshd.common.file.FileSystemView;
-import org.apache.sshd.common.file.SshFile;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.apache.sshd.common.file.nativefs.NativeSshFile.normalizeSeparateChar;
-
-/**
- * <strong>Internal class, do not use directly.</strong>
- *
- * File system view based on native file system. Here the root directory will be
- * user virtual root (/).
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class NativeFileSystemView implements FileSystemView {
-
- public enum UnsupportedAttributePolicy {
- Ignore,
- Warn,
- ThrowException
- }
-
- private final Logger LOG = LoggerFactory.getLogger(NativeFileSystemView.class);
-
-
- private Map<String, String> roots;
-
- // the first and the last character will always be '/'
- // It is always with respect to one of the roots.
- private String current;
-
- private String userName;
-
- private char separator;
-
- private boolean caseInsensitive = false;
-
- private UnsupportedAttributePolicy unsupportedAttributePolicy = UnsupportedAttributePolicy.Warn;
-
- /**
- * Constructor - internal do not use directly, use {@link NativeFileSystemFactory} instead
- */
- public NativeFileSystemView(String userName) {
- this(userName, false);
- }
-
- /**
- * Constructor - internal do not use directly, use {@link NativeFileSystemFactory} instead
- */
- public NativeFileSystemView(String userName, boolean caseInsensitive) {
- this(userName, getAllRoots(), System.getProperty("user.dir"), File.separatorChar, caseInsensitive);
- }
-
- /**
- * Constructor - internal do not use directly, use {@link NativeFileSystemFactory} instead
- */
- public NativeFileSystemView(String userName, Map<String, String> roots, String current) {
- this(userName, roots, current, File.separatorChar, false);
- }
-
- /**
- * Constructor - internal do not use directly, use {@link NativeFileSystemFactory} instead
- *
- * @param userName the user name
- * @param roots known root mapping, key is the virtual root name, value is the physical file
- * @param current the virtual current dir
- */
- public NativeFileSystemView(String userName, Map<String, String> roots, String current, char separator, boolean caseInsensitive) {
- if (userName == null) {
- throw new IllegalArgumentException("user can not be null");
- }
- // Normalize roots
- Map<String, String> verRoots = new LinkedHashMap<String, String>();
- for (String r : roots.keySet()) {
- String virtual = appendSlash(normalizeSeparateChar(r));
- String physical = appendSlash(normalizeSeparateChar(roots.get(r)));
- verRoots.put(virtual, physical);
- }
- // add last '/' if necessary
- current = appendSlash(normalizeSeparateChar(current));
- // Verify the current dir is relative to a known root
- String root = null;
- for (String r : verRoots.keySet()) {
- if (current.startsWith(r)) {
- root = r;
- break;
- }
- }
- if (root == null) {
- throw new IllegalArgumentException("Current dir " + current + " does not start from a known root: " + new ArrayList<String>(verRoots.keySet()));
- }
-
- this.separator = separator;
- this.caseInsensitive = caseInsensitive;
- this.roots = verRoots;
- this.current = current;
- this.userName = userName;
- LOG.debug("Native filesystem view created for user \"{}\" with current dir \"{}\"", userName, this.current);
- }
-
- private String appendSlash(String path) {
- return path.endsWith("/") ? path : path + "/";
- }
-
- private static Map<String, String> getAllRoots() {
- Map<String, String> roots = new LinkedHashMap<String, String>();
- if (isWindows) {
- for (File file : File.listRoots()) {
- if (file.exists()) {
- String root = file.toString();
- String name = root.substring(0, root.length() - 1);
- roots.put(name, root);
- }
- }
- } else {
- roots.put("/", "/");
- }
- return roots;
- }
-
- public UnsupportedAttributePolicy getUnsupportedAttributePolicy() {
- return unsupportedAttributePolicy;
- }
-
- public void setUnsupportedAttributePolicy(UnsupportedAttributePolicy unsupportedAttributePolicy) {
- this.unsupportedAttributePolicy = unsupportedAttributePolicy;
- }
-
- public String getUserName() {
- return userName;
- }
-
- public char getSeparator() {
- return separator;
- }
-
- /**
- * Get file object.
- */
- public SshFile getFile(String file) {
- return getFile(current, file);
- }
-
- public SshFile getFile(SshFile baseDir, String file) {
- return getFile(baseDir.getAbsolutePath(), file);
- }
-
- protected SshFile getFile(String dir, String file) {
- dir = appendSlash(normalizeSeparateChar(dir));
- file = normalizeSeparateChar(file);
- // Compute root + non rooted absolute file
- String root = null;
- if (roots.size() > 1 && file.startsWith("/")) {
- file = file.substring(1);
- }
- for (String r : roots.keySet()) {
- if (!file.isEmpty() && r.equals(file + "/")) {
- file += "/";
- }
- if (file.startsWith(r)) {
- root = r;
- file = "/" + file.substring(r.length());
- break;
- }
- }
- if (root == null) {
- // file is relative to dir
- file = dir + file;
- for (String r : roots.keySet()) {
- if (file.startsWith(r)) {
- root = r;
- file = "/" + file.substring(r.length());
- break;
- }
- }
- }
- if (root == null) {
- throw new IllegalStateException("Could not find root dir for file(" + dir + ", " + file + ")");
- }
- // Physical root
- String physicalRoot = roots.get(root);
- // get actual file object
- String physicalName = NativeSshFile.getPhysicalName(physicalRoot, "/", file, caseInsensitive);
- File fileObj = new File(physicalName);
-
- // strip the root directory and return
- String userFileName = root + physicalName.substring(physicalRoot.length());
- return createNativeSshFile(userFileName, fileObj, userName);
- }
-
- static boolean isJava7;
- static boolean isWindows;
- static {
- // Check java 7
- boolean j7 = false;
- try {
- ClassLoader.getSystemClassLoader().loadClass("java.nio.file.Files");
- j7 = true;
- } catch (Throwable t) {
- // Ignore
- }
- isJava7 = j7;
- // Check windows
- boolean win = false;
- try {
- win = System.getProperty("os.name").toLowerCase().contains("win");
- } catch (Throwable t) {
- // Ignore
- }
- isWindows = win;
- }
-
- public NativeSshFile createNativeSshFile(String name, File file, String userName) {
- name = deNormalizeSeparateChar(name);
- if (isJava7) {
- return new NativeSshFileNio(this, name, file, userName);
- } else {
- return new NativeSshFile(this, name, file, userName);
- }
- }
-
- /**
- * Normalize separate character. Separate character should be '/' always.
- */
- public final String deNormalizeSeparateChar(final String pathName) {
- return pathName.replace('/', separator);
- }
-
- public FileSystemView getNormalizedView() {
- if (roots.size() == 1 && roots.containsKey("/") && separator == '/') {
- return this;
- }
- return new NativeFileSystemView(userName, roots, current, '/', caseInsensitive) {
- public SshFile getFile(String file) {
- return getFile(reroot(current), file);
- }
-
- public SshFile getFile(SshFile baseDir, String file) {
- return getFile(baseDir.getAbsolutePath(), file);
- }
-
- public FileSystemView getNormalizedView() {
- return this;
- }
-
- protected String reroot(String file) {
- file = appendSlash(file);
- for (String r : roots.keySet()) {
- if (file.startsWith(r)) {
- return "/" + normalizeRoot(r) + file.substring(r.length());
- }
- }
- throw new IllegalArgumentException();
- }
-
- protected SshFile getFile(String dir, String file) {
- dir = appendSlash(normalizeSeparateChar(dir));
- file = normalizeSeparateChar(file);
- // Compute root + non rooted absolute file
- if (!file.startsWith("/")) {
- file = dir + file;
- }
- // get actual file object
- String userFileName = NativeSshFile.getPhysicalName("/", "/", file, caseInsensitive);
- if (userFileName.equals("/")) {
- return new RootFile();
- }
- int idx = userFileName.indexOf("/", 1);
- if (idx < 0) {
- String root = userFileName + "/";
- String physRoot = null;
- for (String r : roots.keySet()) {
- if (normalizeRoot(r).equals(root)) {
- physRoot = roots.get(r);
- break;
- }
- }
- if (physRoot == null) {
- throw new IllegalArgumentException("Unknown root " + userFileName);
- }
- File fileObj = new File(physRoot);
- userFileName = normalizeSeparateChar(userFileName);
- return createNativeSshFile(userFileName, fileObj, userName);
- } else {
- String root = userFileName.substring(1, idx) + "/";
- String physRoot = null;
- for (String r : roots.keySet()) {
- if (normalizeRoot(r).equals(root)) {
- physRoot = roots.get(r);
- break;
- }
- }
- if (physRoot == null) {
- throw new IllegalArgumentException("Unknown root " + userFileName);
- }
- File fileObj = new File(physRoot + userFileName.substring(idx + 1));
- userFileName = normalizeSeparateChar(userFileName);
- return createNativeSshFile(userFileName, fileObj, userName);
- }
- }
- };
- }
-
- protected static String normalizeRoot(String root) {
- return root.replace(":", "");
- }
-
- class RootFile implements SshFile {
- public String getAbsolutePath() {
- return "/";
- }
- public String getName() {
- return "/";
- }
- public Map<Attribute, Object> getAttributes(boolean followLinks) throws IOException {
- return null;
- }
- public void setAttributes(Map<Attribute, Object> attributes) throws IOException {
- throw new UnsupportedOperationException();
- }
- public Object getAttribute(Attribute attribute, boolean followLinks) throws IOException {
- return null;
- }
- public void setAttribute(Attribute attribute, Object value) throws IOException {
- throw new UnsupportedOperationException();
- }
- public String readSymbolicLink() throws IOException {
- return null;
- }
- public void createSymbolicLink(SshFile destination) throws IOException {
- }
- public String getOwner() {
- return null;
- }
- public boolean isDirectory() {
- return true;
- }
- public boolean isFile() {
- return false;
- }
- public boolean doesExist() {
- return true;
- }
- public boolean isReadable() {
- return true;
- }
- public boolean isWritable() {
- return false;
- }
- public boolean isExecutable() {
- return false;
- }
- public boolean isRemovable() {
- return false;
- }
- public SshFile getParentFile() {
- return null;
- }
- public long getLastModified() {
- return 0;
- }
- public boolean setLastModified(long time) {
- return false;
- }
- public long getSize() {
- return 0;
- }
- public boolean mkdir() {
- return false;
- }
- public boolean delete() {
- return false;
- }
- public boolean create() throws IOException {
- return false;
- }
- public void truncate() throws IOException {
- }
- public boolean move(SshFile destination) {
- return false;
- }
- public List<SshFile> listSshFiles() {
- List<SshFile> list = new ArrayList<SshFile>();
- for (String root : roots.keySet()) {
- String display = normalizeRoot(root);
- display = "/" + display.substring(display.length() - 1);
- list.add(createNativeSshFile(display, new File(roots.get(root)), userName));
- }
- return list;
- }
- public OutputStream createOutputStream(long offset) throws IOException {
- return null;
- }
- public InputStream createInputStream(long offset) throws IOException {
- return null;
- }
- public void handleClose() throws IOException {
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java
deleted file mode 100644
index 8b336ef..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java
+++ /dev/null
@@ -1,701 +0,0 @@
-/*
- * 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.nativefs;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.lang.reflect.Method;
-import java.nio.channels.FileChannel;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-
-import org.apache.sshd.common.file.SshFile;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * <strong>Internal class, do not use directly.</strong>
- *
- * This class wraps native file object.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class NativeSshFile implements SshFile {
-
- protected static final Logger LOG = LoggerFactory.getLogger(NativeSshFile.class);
-
- // the file name with respect to the user root.
- // The path separator character will be '/'.
- protected String fileName;
-
- protected File file;
-
- protected String userName;
-
- protected final NativeFileSystemView nativeFileSystemView;
-
- /**
- * Constructor, internal do not use directly.
- * @param nativeFileSystemView
- */
- protected NativeSshFile(final NativeFileSystemView nativeFileSystemView, final String fileName, final File file,
- final String userName) {
- this.nativeFileSystemView = nativeFileSystemView;
- if (fileName == null) {
- throw new IllegalArgumentException("fileName can not be null");
- }
- if (file == null) {
- throw new IllegalArgumentException("file can not be null");
- }
-
- if (fileName.length() == 0) {
- throw new IllegalArgumentException("fileName can not be empty");
- }
-
- this.fileName = fileName;
- this.file = file;
- this.userName = userName;
- }
-
- public File getNativeFile() {
- return file;
- }
-
- /**
- * Get full name.
- */
- public String getAbsolutePath() {
-
- char separator = nativeFileSystemView.getSeparator();
-
- // strip the last '/' if necessary
- String fullName = fileName;
- int filelen = fullName.length();
- if (fileName.indexOf(separator) != filelen - 1 && (fullName.charAt(filelen - 1) == separator)) {
- fullName = fullName.substring(0, filelen - 1);
- }
-
- return fullName;
- }
-
- /**
- * Get short name.
- */
- public String getName() {
-
- char separator = nativeFileSystemView.getSeparator();
-
- // root - the short name will be '/'
- if (fileName.indexOf(separator) == fileName.length() - 1) {
- return fileName;
- }
-
- // strip the last '/'
- String shortName = fileName;
- int filelen = fileName.length();
- if (shortName.charAt(filelen - 1) == separator) {
- shortName = shortName.substring(0, filelen - 1);
- }
-
- // return from the last '/'
- int slashIndex = shortName.lastIndexOf(separator);
- if (slashIndex != -1) {
- shortName = shortName.substring(slashIndex + 1);
- }
- return shortName;
- }
-
- /**
- * Get owner name
- */
- public String getOwner() {
- return userName;
- }
-
- /**
- * Is it a directory?
- */
- public boolean isDirectory() {
- return file.isDirectory();
- }
-
- /**
- * Is it a file?
- */
- public boolean isFile() {
- return file.isFile();
- }
-
- /**
- * Does this file exists?
- */
- public boolean doesExist() {
- return file.exists();
- }
-
- /**
- * Get file size.
- */
- public long getSize() {
- return file.length();
- }
-
- /**
- * Get last modified time.
- */
- public long getLastModified() {
- return file.lastModified();
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean setLastModified(long time) {
- return file.setLastModified(time);
- }
-
- /**
- * Check read permission.
- */
- public boolean isReadable() {
- return file.canRead();
- }
-
- /**
- * Check file write permission.
- */
- public boolean isWritable() {
- LOG.debug("Checking if file exists");
- if (file.exists()) {
- LOG.debug("Checking can write: " + file.canWrite());
- return file.canWrite();
- }
-
- LOG.debug("Authorized");
- return true;
- }
-
- /**
- * File.canExecute() method is only available on JDK 1.6
- */
- private static final Method CAN_EXECUTE_METHOD;
- static {
- Method method = null;
- try {
- method = File.class.getMethod("canExecute");
- } catch (Throwable t) {
- }
- CAN_EXECUTE_METHOD = method;
- }
-
- /**
- * Check file exec permission.
- */
- public boolean isExecutable() {
- if (CAN_EXECUTE_METHOD != null) {
- try {
- return (Boolean) CAN_EXECUTE_METHOD.invoke(file);
- } catch (Throwable t) {
- }
- }
- // Default directories to being executable
- // as on unix systems to allow listing their contents.
- return file.isDirectory();
- }
-
- /**
- * Has delete permission.
- */
- public boolean isRemovable() {
-
- char separator = nativeFileSystemView.getSeparator();
-
- // root cannot be deleted
- if (fileName.indexOf(separator) == fileName.length() - 1) {
- return false;
- }
-
- /* Added 12/08/2008: in the case that the permission is not explicitly denied for this file
- * we will check if the parent file has write permission as most systems consider that a file can
- * be deleted when their parent directory is writable.
- */
- String fullName = getAbsolutePath();
-
- // we check FTPServer's write permission for this file.
-// if (user.authorize(new WriteRequest(fullName)) == null) {
-// return false;
-// }
-
- // In order to maintain consistency, when possible we delete the last '/' character in the String
- int indexOfSlash = fullName.lastIndexOf(separator);
- String parentFullName;
- if (indexOfSlash == 0) {
- parentFullName = "/";
- } else {
- if (fullName.indexOf(separator) == indexOfSlash) {
- parentFullName = fullName.substring(0, indexOfSlash + 1);
- } else {
- parentFullName = fullName.substring(0, indexOfSlash);
- }
- }
-
- // we check if the parent FileObject is writable.
- NativeSshFile parentObject = nativeFileSystemView.createNativeSshFile(parentFullName, file
- .getAbsoluteFile().getParentFile(), userName);
- return parentObject.isWritable();
- }
-
- public SshFile getParentFile() {
- char separator = nativeFileSystemView.getSeparator();
-
- String path = getAbsolutePath();
- int indexOfSlash = path.lastIndexOf(separator);
- String parentFullName;
- if (indexOfSlash == 0) {
- parentFullName = "/";
- } else {
- if (path.indexOf(separator) == indexOfSlash) {
- parentFullName = path.substring(0, indexOfSlash + 1);
- } else {
- parentFullName = path.substring(0, indexOfSlash);
- }
- }
-
- // we check if the parent FileObject is writable.
- return nativeFileSystemView.createNativeSshFile(parentFullName, file
- .getAbsoluteFile().getParentFile(), userName);
- }
-
- /**
- * Delete file.
- */
- public boolean delete() {
- boolean retVal = false;
- if (isRemovable()) {
- retVal = file.delete();
- }
- return retVal;
- }
-
- /**
- * Create a new file
- */
- public boolean create() throws IOException {
- return file.createNewFile();
- }
-
- /**
- * Truncate file to length 0.
- */
- public void truncate() throws IOException {
- new FileWriter(file).close();
- }
-
- /**
- * Move file object.
- */
- public boolean move(final SshFile dest) {
- boolean retVal = false;
- if (dest.isWritable() && isReadable()) {
- File destFile = ((NativeSshFile) dest).file;
-
- if (destFile.exists()) {
- // renameTo behaves differently on different platforms
- // this check verifies that if the destination already exists,
- // we fail
- retVal = false;
- } else {
- retVal = file.renameTo(destFile);
- }
- }
- return retVal;
- }
-
- /**
- * Create directory.
- */
- public boolean mkdir() {
- boolean retVal = false;
- if (isWritable()) {
- retVal = file.mkdir();
- }
- return retVal;
- }
-
- /**
- * List files. If not a directory or does not exist, null will be returned.
- */
- public List<SshFile> listSshFiles() {
-
- // is a directory
- if (!file.isDirectory()) {
- return null;
- }
-
- // directory - return all the files
- File[] files = file.listFiles();
- if (files == null) {
- return null;
- }
-
- // make sure the files are returned in order
- Arrays.sort(files, new Comparator<File>() {
- public int compare(File f1, File f2) {
- return f1.getName().compareTo(f2.getName());
- }
- });
-
- char separator = nativeFileSystemView.getSeparator();
-
- // get the virtual name of the base directory
- String virtualFileStr = getAbsolutePath();
- if (virtualFileStr.charAt(virtualFileStr.length() - 1) != separator) {
- virtualFileStr += separator;
- }
-
- // now return all the files under the directory
- SshFile[] virtualFiles = new SshFile[files.length];
- for (int i = 0; i < files.length; ++i) {
- File fileObj = files[i];
- String fileName = virtualFileStr + fileObj.getName();
- virtualFiles[i] = nativeFileSystemView.createNativeSshFile(fileName, fileObj, userName);
- }
-
- return Collections.unmodifiableList(Arrays.asList(virtualFiles));
- }
-
- /**
- * Create output stream for writing.
- */
- public OutputStream createOutputStream(final long offset)
- throws IOException {
-
- // permission check
- if (!isWritable()) {
- throw new IOException("No write permission : " + file.getName());
- }
-
- // move to the appropriate offset and create output stream
- final boolean canRead = file.canRead();
- if (!canRead) {
- file.setReadable(true, true);
- }
- final RandomAccessFile raf = new RandomAccessFile(file, "rw");
- try {
- raf.seek(offset);
-
- // The IBM jre needs to have both the stream and the random access file
- // objects closed to actually close the file
- return new FileOutputStream(raf.getFD()) {
- public void close() throws IOException {
- super.close();
- raf.close();
- if (!canRead) {
- file.setReadable(false, true);
- }
- }
- };
- } catch (IOException e) {
- raf.close();
- throw e;
- }
- }
-
- /**
- * Create input stream for reading.
- */
- public InputStream createInputStream(final long offset) throws IOException {
-
- // permission check
- if (!isReadable()) {
- throw new IOException("No read permission : " + file.getName());
- }
-
- // move to the appropriate offset and create input stream
- final FileInputStream fis = new FileInputStream(file);
- try {
- fis.getChannel().position(offset);
- return fis;
- } catch (IOException e) {
- fis.close();
- throw e;
- }
- }
-
- public void handleClose() {
- // Noop
- }
-
- /**
- * Normalize separate character. Separate character should be '/' always.
- */
- public final static String normalizeSeparateChar(final String pathName) {
- String normalizedPathName = pathName.replace('\\', '/');
- return normalizedPathName;
- }
-
- /**
- * Get the physical canonical file name. It works like
- * File.getCanonicalPath().
- *
- * @param rootDir
- * The root directory.
- * @param currDir
- * The current directory. It will always be with respect to the
- * root directory.
- * @param fileName
- * The input file name.
- * @return The return string will always begin with the root directory. It
- * will never be null.
- */
- public final static String getPhysicalName(final String rootDir,
- final String currDir, final String fileName,
- final boolean caseInsensitive) {
-
- // get the starting directory
- String normalizedRootDir = normalizeSeparateChar(rootDir);
- if (normalizedRootDir.charAt(normalizedRootDir.length() - 1) != '/') {
- normalizedRootDir += '/';
- }
-
- String normalizedFileName = normalizeSeparateChar(fileName);
- String resArg;
- String normalizedCurrDir = currDir;
- if (normalizedFileName.charAt(0) != '/') {
- if (normalizedCurrDir == null) {
- normalizedCurrDir = "/";
- }
- if (normalizedCurrDir.length() == 0) {
- normalizedCurrDir = "/";
- }
-
- normalizedCurrDir = normalizeSeparateChar(normalizedCurrDir);
-
- if (normalizedCurrDir.charAt(0) != '/') {
- normalizedCurrDir = '/' + normalizedCurrDir;
- }
- if (normalizedCurrDir.charAt(normalizedCurrDir.length() - 1) != '/') {
- normalizedCurrDir += '/';
- }
-
- resArg = normalizedRootDir + normalizedCurrDir.substring(1);
- } else {
- resArg = normalizedRootDir;
- }
-
- // strip last '/'
- if (resArg.charAt(resArg.length() - 1) == '/') {
- resArg = resArg.substring(0, resArg.length() - 1);
- }
-
- // replace ., ~ and ..
- // in this loop resArg will never end with '/'
- StringTokenizer st = new StringTokenizer(normalizedFileName, "/");
- while (st.hasMoreTokens()) {
- String tok = st.nextToken();
-
- // . => current directory
- if (tok.equals(".")) {
- continue;
- }
-
- // .. => parent directory (if not root)
- if (tok.equals("..")) {
- if (resArg.startsWith(normalizedRootDir)) {
- int slashIndex = resArg.lastIndexOf('/');
- if (slashIndex != -1) {
- resArg = resArg.substring(0, slashIndex);
- }
- }
- continue;
- }
-
- // ~ => home directory (in this case the root directory)
- if (tok.equals("~")) {
- resArg = normalizedRootDir.substring(0, normalizedRootDir
- .length() - 1);
- continue;
- }
-
- if (caseInsensitive) {
- File[] matches = new File(resArg)
- .listFiles(new NameEqualsFileFilter(tok, true));
-
- if (matches != null && matches.length > 0) {
- tok = matches[0].getName();
- }
- }
-
- resArg = resArg + '/' + tok;
- }
-
- // add last slash if necessary
- if ((resArg.length()) + 1 == normalizedRootDir.length()) {
- resArg += '/';
- }
-
- // final check
- if (!resArg.regionMatches(0, normalizedRootDir, 0, normalizedRootDir
- .length())) {
- resArg = normalizedRootDir;
- }
-
- return resArg;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj != null && obj instanceof NativeSshFile) {
- File thisCanonicalFile;
- File otherCanonicalFile;
- try {
- thisCanonicalFile = this.file.getCanonicalFile();
- otherCanonicalFile = ((NativeSshFile) obj).file
- .getCanonicalFile();
- } catch (IOException e) {
- throw new RuntimeException("Failed to get the canonical path", e);
- }
-
- return thisCanonicalFile.equals(otherCanonicalFile);
- }
- return false;
- }
-
- /**
- * Returns the according physical file. Needed for logging, monitoring, event handling, etc.
- *
- * @return The according physical file.
- */
- public File getPhysicalFile() {
- return file;
- }
-
- @Override
- public String toString() {
- return fileName;
- }
-
- public Map<Attribute, Object> getAttributes(boolean followLinks) throws IOException {
- Map<Attribute, Object> map = new HashMap<Attribute, Object>();
- map.put(Attribute.Size, getSize());
- map.put(Attribute.IsDirectory, isDirectory());
- map.put(Attribute.IsRegularFile, isFile());
- map.put(Attribute.IsSymbolicLink, false);
- map.put(Attribute.LastModifiedTime, getLastModified());
- map.put(Attribute.LastAccessTime, getLastModified());
- map.put(Attribute.Owner, userName);
- map.put(Attribute.Group, userName);
- EnumSet<Permission> p = EnumSet.noneOf(Permission.class);
- if (isReadable()) {
- p.add(Permission.UserRead);
- p.add(Permission.GroupRead);
- p.add(Permission.OthersRead);
- }
- if (isWritable()) {
- p.add(Permission.UserWrite);
- p.add(Permission.GroupWrite);
- p.add(Permission.OthersWrite);
- }
- if (isExecutable()) {
- p.add(Permission.UserExecute);
- p.add(Permission.GroupExecute);
- p.add(Permission.OthersExecute);
- }
- map.put(Attribute.Permissions, p);
- return map;
- }
-
- public void setAttributes(Map<Attribute, Object> attributes) throws IOException {
- Set<Attribute> unsupported = new HashSet<Attribute>();
- for (Attribute attribute : attributes.keySet()) {
- Object value = attributes.get(attribute);
- switch (attribute) {
- case Size: {
- long newSize = (Long) value;
- FileChannel outChan = new FileOutputStream(file, true).getChannel();
- outChan.truncate(newSize);
- outChan.close();
- continue;
- }
- case LastModifiedTime:
- setLastModified((Long) value);
- break;
- default:
- unsupported.add(attribute);
- break;
- }
- }
- handleUnsupportedAttributes(unsupported);
- }
-
- protected void handleUnsupportedAttributes(Collection<Attribute> attributes) {
- if (!attributes.isEmpty()) {
- StringBuilder sb = new StringBuilder();
- for (Attribute attr : attributes) {
- if (sb.length() > 0) {
- sb.append(", ");
- }
- sb.append(attr.name());
- }
- switch (nativeFileSystemView.getUnsupportedAttributePolicy()) {
- case Ignore:
- break;
- case Warn:
- LOG.warn("Unsupported attributes: " + sb.toString());
- break;
- case ThrowException:
- throw new UnsupportedOperationException("Unsupported attributes: " + sb.toString());
- }
- }
- }
-
- public Object getAttribute(Attribute attribute, boolean followLinks) throws IOException {
- return getAttributes(followLinks).get(attribute);
- }
-
- public void setAttribute(Attribute attribute, Object value) throws IOException {
- Map<Attribute, Object> map = new HashMap<Attribute, Object>();
- map.put(attribute, value);
- setAttributes(map);
- }
-
- public String readSymbolicLink() throws IOException {
- throw new UnsupportedOperationException();
- }
-
- public void createSymbolicLink(SshFile destination) throws IOException {
- throw new UnsupportedOperationException();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFileNio.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFileNio.java b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFileNio.java
deleted file mode 100644
index b0596d1..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFileNio.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * 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.nativefs;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.channels.SeekableByteChannel;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
-import java.nio.file.attribute.FileTime;
-import java.nio.file.attribute.GroupPrincipal;
-import java.nio.file.attribute.PosixFilePermission;
-import java.nio.file.attribute.UserPrincipal;
-import java.nio.file.attribute.UserPrincipalLookupService;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.sshd.common.file.SshFile;
-
-/**
- * <strong>Internal class, do not use directly.</strong>
- *
- * This class wraps native file object.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class NativeSshFileNio extends NativeSshFile {
-
- @Override
- public boolean doesExist() {
- return Files.exists(file.toPath(), LinkOption.NOFOLLOW_LINKS);
- }
-
- /**
- * Constructor, internal do not use directly.
- * @param nativeFileSystemView
- */
- public NativeSshFileNio(NativeFileSystemView nativeFileSystemView, String fileName, File file, String userName) {
- super(nativeFileSystemView, fileName, file, userName);
- }
-
- public Map<Attribute, Object> getAttributes(boolean followLinks) throws IOException {
- String[] attrs = new String[] { "unix:*", "posix:*", "*" };
- Map<String, Object> a = null;
- for (String attr : attrs) {
- try {
- a = Files.readAttributes(
- file.toPath(), attr,
- followLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS});
- break;
- } catch (UnsupportedOperationException e) {
- // Ignore
- }
- }
- if (a == null) {
- throw new IllegalStateException();
- }
- Map<Attribute, Object> map = new HashMap<Attribute, Object>();
- map.put(Attribute.Size, a.get("size"));
- if (a.containsKey("uid")) {
- map.put(Attribute.Uid, a.get("uid"));
- }
- if (a.containsKey("owner")) {
- map.put(Attribute.Owner, ((UserPrincipal) a.get("owner")).getName());
- } else {
- map.put(Attribute.Owner, userName);
- }
- if (a.containsKey("gid")) {
- map.put(Attribute.Gid, a.get("gid"));
- }
- if (a.containsKey("group")) {
- map.put(Attribute.Group, ((GroupPrincipal) a.get("group")).getName());
- } else {
- map.put(Attribute.Group, userName);
- }
- if (a.containsKey("nlink")) {
- map.put(Attribute.NLink, a.get("nlink"));
- }
- map.put(Attribute.IsDirectory, a.get("isDirectory"));
- map.put(Attribute.IsRegularFile, a.get("isRegularFile"));
- map.put(Attribute.IsSymbolicLink, a.get("isSymbolicLink"));
- map.put(Attribute.CreationTime, ((FileTime) a.get("creationTime")).toMillis());
- map.put(Attribute.LastModifiedTime, ((FileTime) a.get("lastModifiedTime")).toMillis());
- map.put(Attribute.LastAccessTime, ((FileTime) a.get("lastAccessTime")).toMillis());
- if (a.containsKey("permissions")) {
- map.put(Attribute.Permissions, fromPerms((Set<PosixFilePermission>) a.get("permissions")));
- } else {
- EnumSet<Permission> p = EnumSet.noneOf(Permission.class);
- if (isReadable()) {
- p.add(Permission.UserRead);
- p.add(Permission.GroupRead);
- p.add(Permission.OthersRead);
- }
- if (isWritable()) {
- p.add(Permission.UserWrite);
- p.add(Permission.GroupWrite);
- p.add(Permission.OthersWrite);
- }
- if (isExecutable()) {
- p.add(Permission.UserExecute);
- p.add(Permission.GroupExecute);
- p.add(Permission.OthersExecute);
- }
- map.put(Attribute.Permissions, p);
- }
- return map;
- }
-
- public void setAttributes(Map<Attribute, Object> attributes) throws IOException {
- Set<Attribute> unsupported = new HashSet<Attribute>();
- for (Attribute attribute : attributes.keySet()) {
- String name = null;
- Object value = attributes.get(attribute);
- switch (attribute) {
- case Size: {
- long newSize = (Long) value;
- FileChannel outChan = new FileOutputStream(file, true).getChannel();
- outChan.truncate(newSize);
- outChan.close();
- continue;
- }
- case Uid: name = "unix:uid"; break;
- case Gid: name = "unix:gid"; break;
- case Owner: name = "posix:owner"; value = toUser((String) value); break;
- case Group: name = "posix:group"; value = toGroup((String) value); break;
- case Permissions: name = "posix:permissions"; value = toPerms((EnumSet<Permission>) value); break;
- case CreationTime: name = "basic:creationTime"; value = FileTime.fromMillis((Long) value); break;
- case LastModifiedTime: name = "basic:lastModifiedTime"; value = FileTime.fromMillis((Long) value); break;
- case LastAccessTime: name = "basic:lastAccessTime"; value = FileTime.fromMillis((Long) value); break;
- }
- if (name != null && value != null) {
- try {
- Files.setAttribute(file.toPath(), name, value, LinkOption.NOFOLLOW_LINKS);
- } catch (UnsupportedOperationException e) {
- unsupported.add(attribute);
- }
- }
- }
- handleUnsupportedAttributes(unsupported);
- }
-
- public String readSymbolicLink() throws IOException {
- Path path = file.toPath();
- Path link = Files.readSymbolicLink(path);
- return link.toString();
- }
-
- public void createSymbolicLink(SshFile destination) throws IOException {
- Path link = file.toPath();
- Path target = Paths.get(destination.toString());
- Files.createSymbolicLink(link, target);
- }
-
- private EnumSet<Permission> fromPerms(Set<PosixFilePermission> perms) {
- EnumSet<Permission> p = EnumSet.noneOf(Permission.class);
- for (PosixFilePermission perm : perms) {
- switch (perm) {
- case OWNER_READ: p.add(Permission.UserRead); break;
- case OWNER_WRITE: p.add(Permission.UserWrite); break;
- case OWNER_EXECUTE: p.add(Permission.UserExecute); break;
- case GROUP_READ: p.add(Permission.GroupRead); break;
- case GROUP_WRITE: p.add(Permission.GroupWrite); break;
- case GROUP_EXECUTE: p.add(Permission.GroupExecute); break;
- case OTHERS_READ: p.add(Permission.OthersRead); break;
- case OTHERS_WRITE: p.add(Permission.OthersWrite); break;
- case OTHERS_EXECUTE: p.add(Permission.OthersExecute); break;
- }
- }
- return p;
- }
-
- private GroupPrincipal toGroup(String name) throws IOException {
- UserPrincipalLookupService lookupService = file.toPath().getFileSystem().getUserPrincipalLookupService();
- return lookupService.lookupPrincipalByGroupName(name);
- }
-
- private UserPrincipal toUser(String name) throws IOException {
- UserPrincipalLookupService lookupService = file.toPath().getFileSystem().getUserPrincipalLookupService();
- return lookupService.lookupPrincipalByName(name);
- }
-
- private Set<PosixFilePermission> toPerms(EnumSet<Permission> perms) {
- Set<PosixFilePermission> set = new HashSet<PosixFilePermission>();
- for (Permission p : perms) {
- switch (p) {
- case UserRead: set.add(PosixFilePermission.OWNER_READ); break;
- case UserWrite: set.add(PosixFilePermission.OWNER_WRITE); break;
- case UserExecute: set.add(PosixFilePermission.OWNER_EXECUTE); break;
- case GroupRead: set.add(PosixFilePermission.GROUP_READ); break;
- case GroupWrite: set.add(PosixFilePermission.GROUP_WRITE); break;
- case GroupExecute: set.add(PosixFilePermission.GROUP_EXECUTE); break;
- case OthersRead: set.add(PosixFilePermission.OTHERS_READ); break;
- case OthersWrite: set.add(PosixFilePermission.OTHERS_WRITE); break;
- case OthersExecute: set.add(PosixFilePermission.OTHERS_EXECUTE); break;
- }
- }
- return set;
- }
-
- @Override
- public OutputStream createOutputStream(long offset) throws IOException {
- Path path = file.toPath();
- final SeekableByteChannel sbc = Files.newByteChannel(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
- if (offset > 0) {
- sbc.position(offset);
- }
- return new OutputStream() {
- @Override
- public void write(int b) throws IOException {
- write(new byte[] { (byte) b }, 0, 1);
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- if (b == null) {
- throw new NullPointerException();
- } else if ((off < 0) || (off > b.length) || (len < 0) ||
- ((off + len) > b.length) || ((off + len) < 0)) {
- throw new IndexOutOfBoundsException();
- } else if (len == 0) {
- return;
- }
- sbc.write(ByteBuffer.wrap(b, off, len));
- }
-
- @Override
- public void close() throws IOException {
- sbc.close();
- }
- };
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystem.java b/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystem.java
new file mode 100644
index 0000000..293d054
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystem.java
@@ -0,0 +1,76 @@
+/*
+ * 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.file.Path;
+import java.nio.file.WatchService;
+import java.nio.file.attribute.UserPrincipalLookupService;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.sshd.common.file.util.BaseFileSystem;
+import org.apache.sshd.common.file.util.ImmutableList;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RootedFileSystem extends BaseFileSystem<RootedPath> {
+
+ private Path root;
+
+ public RootedFileSystem(RootedFileSystemProvider fileSystemProvider, Path root, Map<String, ?> env) {
+ super(fileSystemProvider);
+ this.root = root;
+ }
+
+ public Path getRoot() {
+ return root;
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+
+ @Override
+ public boolean isOpen() {
+ return getRoot().getFileSystem().isOpen();
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return getRoot().getFileSystem().isReadOnly();
+ }
+
+ @Override
+ public Set<String> supportedFileAttributeViews() {
+ return root.getFileSystem().supportedFileAttributeViews();
+ }
+
+ @Override
+ public UserPrincipalLookupService getUserPrincipalLookupService() {
+ return getRoot().getFileSystem().getUserPrincipalLookupService();
+ }
+
+ @Override
+ protected RootedPath create(String root, ImmutableList<String> names) {
+ return new RootedPath(this, root, names);
+ }
+
+}
[07/15] mina-sshd git commit: [SSHD-377] Create a nio FileSystem
implementation for sftp
Posted by gn...@apache.org.
[SSHD-377] Create a nio FileSystem implementation for sftp
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/cc4d7877
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/cc4d7877
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/cc4d7877
Branch: refs/heads/master
Commit: cc4d787733388f192a2f776ae4fcde2a471f858d
Parents: e9ee318
Author: Guillaume Nodet <gn...@apache.org>
Authored: Fri Nov 28 15:12:06 2014 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Mon Feb 23 16:21:30 2015 +0100
----------------------------------------------------------------------
.../java.nio.file.spi.FileSystemProvider | 20 +
.../java/org/apache/sshd/ClientSession.java | 3 +
.../java/org/apache/sshd/client/SftpClient.java | 5 +-
.../sshd/client/session/ClientSessionImpl.java | 7 +
.../sshd/client/sftp/DefaultSftpClient.java | 4 +
.../apache/sshd/client/sftp/SftpFileSystem.java | 314 +++++++++
.../client/sftp/SftpFileSystemProvider.java | 665 +++++++++++++++++++
.../org/apache/sshd/client/sftp/SftpPath.java | 49 ++
.../common/file/nativefs/NativeSshFileNio.java | 4 +-
.../sshd/common/file/util/BaseFileSystem.java | 239 +++++++
.../apache/sshd/common/file/util/BasePath.java | 367 ++++++++++
.../sshd/common/file/util/ImmutableList.java | 61 ++
.../apache/sshd/server/sftp/SftpSubsystem.java | 5 +-
.../org/apache/sshd/SftpFileSystemTest.java | 112 ++++
.../sshd/common/file/util/BasePathTest.java | 560 ++++++++++++++++
15 files changed, 2411 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/main/filtered-resources/META-INF/services/java.nio.file.spi.FileSystemProvider
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/filtered-resources/META-INF/services/java.nio.file.spi.FileSystemProvider b/sshd-core/src/main/filtered-resources/META-INF/services/java.nio.file.spi.FileSystemProvider
new file mode 100644
index 0000000..08d4b4a
--- /dev/null
+++ b/sshd-core/src/main/filtered-resources/META-INF/services/java.nio.file.spi.FileSystemProvider
@@ -0,0 +1,20 @@
+##
+## 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.
+##
+
+org.apache.sshd.client.sftp.SftpFileSystemProvider
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/main/java/org/apache/sshd/ClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/ClientSession.java b/sshd-core/src/main/java/org/apache/sshd/ClientSession.java
index bee81f7..867dadb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/ClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/ClientSession.java
@@ -19,6 +19,7 @@
package org.apache.sshd;
import java.io.IOException;
+import java.nio.file.FileSystem;
import java.security.KeyPair;
import java.util.Map;
@@ -160,6 +161,8 @@ public interface ClientSession extends Session {
*/
SftpClient createSftpClient() throws IOException;
+ FileSystem createSftpFileSystem() throws IOException;
+
/**
* Start forwarding the given local address on the client to the given address on the server.
*/
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/main/java/org/apache/sshd/client/SftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SftpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SftpClient.java
index 35b4260..18a9ba4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/SftpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SftpClient.java
@@ -21,12 +21,13 @@ package org.apache.sshd.client;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.ByteBuffer;
import java.util.EnumSet;
/**
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/
-public interface SftpClient {
+public interface SftpClient extends AutoCloseable {
//
// Permission flags
@@ -135,6 +136,8 @@ public interface SftpClient {
*/
void close() throws IOException;
+ boolean isClosing();
+
//
// Low level API
//
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
index 7d9a8c9..a1872b8 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
@@ -20,6 +20,7 @@ package org.apache.sshd.client.session;
import java.io.IOException;
import java.net.SocketAddress;
+import java.nio.file.FileSystem;
import java.security.KeyPair;
import java.util.ArrayList;
import java.util.Arrays;
@@ -47,6 +48,8 @@ import org.apache.sshd.client.future.AuthFuture;
import org.apache.sshd.client.future.DefaultAuthFuture;
import org.apache.sshd.client.scp.DefaultScpClient;
import org.apache.sshd.client.sftp.DefaultSftpClient;
+import org.apache.sshd.client.sftp.SftpFileSystem;
+import org.apache.sshd.client.sftp.SftpFileSystemProvider;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.Service;
import org.apache.sshd.common.ServiceFactory;
@@ -278,6 +281,10 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
return new DefaultSftpClient(this);
}
+ public FileSystem createSftpFileSystem() throws IOException {
+ return new SftpFileSystem(new SftpFileSystemProvider((org.apache.sshd.SshClient) factoryManager), this);
+ }
+
public SshdSocketAddress startLocalPortForwarding(SshdSocketAddress local, SshdSocketAddress remote) throws IOException {
return getConnectionService().getTcpipForwarder().startLocalPortForwarding(local, remote);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java
index cbed805..526d92d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java
@@ -132,6 +132,10 @@ public class DefaultSftpClient implements SftpClient {
init();
}
+ public boolean isClosing() {
+ return closing;
+ }
+
public void close() throws IOException {
this.channel.close(false);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystem.java b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystem.java
new file mode 100644
index 0000000..d6de9df
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystem.java
@@ -0,0 +1,314 @@
+/*
+ * 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.client.sftp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.attribute.GroupPrincipal;
+import java.nio.file.attribute.UserPrincipal;
+import java.nio.file.attribute.UserPrincipalLookupService;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.client.SftpClient;
+import org.apache.sshd.common.file.util.BaseFileSystem;
+import org.apache.sshd.common.file.util.ImmutableList;
+
+public class SftpFileSystem extends BaseFileSystem<SftpPath> {
+
+ private final ClientSession session;
+ private final Queue<SftpClient> pool;
+ private final ThreadLocal<Wrapper> wrappers = new ThreadLocal<>();
+ private SftpPath defaultDir;
+
+ public SftpFileSystem(SftpFileSystemProvider provider, ClientSession session) throws IOException {
+ super(provider);
+ this.session = session;
+ this.pool = new LinkedBlockingQueue<>(8);
+ try (SftpClient client = getClient()) {
+ defaultDir = getPath(client.canonicalPath("."));
+ }
+ }
+
+ @Override
+ protected SftpPath create(String root, ImmutableList<String> names) {
+ return new SftpPath(this, root, names);
+ }
+
+ public ClientSession getSession() {
+ return session;
+ }
+
+ public SftpClient getClient() throws IOException {
+ Wrapper wrapper = wrappers.get();
+ if (wrapper == null) {
+ while (wrapper == null) {
+ SftpClient client = pool.poll();
+ if (client == null) {
+ client = session.createSftpClient();
+ }
+ if (!client.isClosing()) {
+ wrapper = new Wrapper(client);
+ }
+ }
+ wrappers.set(wrapper);
+ } else {
+ wrapper.increment();
+ }
+ return wrapper;
+ }
+
+ @Override
+ public void close() throws IOException {
+ session.close(true);
+ }
+
+ @Override
+ public boolean isOpen() {
+ return !session.isClosing();
+ }
+
+ @Override
+ public Set<String> supportedFileAttributeViews() {
+ Set<String> set = new HashSet<>();
+ set.addAll(Arrays.asList("basic", "posix", "owner"));
+ return Collections.unmodifiableSet(set);
+ }
+
+ @Override
+ public UserPrincipalLookupService getUserPrincipalLookupService() {
+ return new DefaultUserPrincipalLookupService();
+ }
+
+ public SftpPath getDefaultDir() {
+ return defaultDir;
+ }
+
+ private class Wrapper implements SftpClient {
+
+ private final SftpClient delegate;
+ private final AtomicInteger count = new AtomicInteger(1);
+
+ private Wrapper(SftpClient delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public boolean isClosing() {
+ return false;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (count.decrementAndGet() == 0) {
+ if (!pool.offer(delegate)) {
+ delegate.close();
+ }
+ wrappers.set(null);
+ }
+ }
+
+ public void increment() {
+ count.incrementAndGet();
+ }
+
+ @Override
+ public Handle open(String path, EnumSet<OpenMode> options) throws IOException {
+ return delegate.open(path, options);
+ }
+
+ @Override
+ public void close(Handle handle) throws IOException {
+ delegate.close(handle);
+ }
+
+ @Override
+ public void remove(String path) throws IOException {
+ delegate.remove(path);
+ }
+
+ @Override
+ public void rename(String oldPath, String newPath) throws IOException {
+ delegate.rename(oldPath, newPath);
+ }
+
+ @Override
+ public int read(Handle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException {
+ return delegate.read(handle, fileOffset, dst, dstoff, len);
+ }
+
+ @Override
+ public void write(Handle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException {
+ delegate.write(handle, fileOffset, src, srcoff, len);
+ }
+
+ @Override
+ public void mkdir(String path) throws IOException {
+ delegate.mkdir(path);
+ }
+
+ @Override
+ public void rmdir(String path) throws IOException {
+ delegate.rmdir(path);
+ }
+
+ @Override
+ public Handle openDir(String path) throws IOException {
+ return delegate.openDir(path);
+ }
+
+ @Override
+ public DirEntry[] readDir(Handle handle) throws IOException {
+ return delegate.readDir(handle);
+ }
+
+ @Override
+ public String canonicalPath(String canonical) throws IOException {
+ return delegate.canonicalPath(canonical);
+ }
+
+ @Override
+ public Attributes stat(String path) throws IOException {
+ return delegate.stat(path);
+ }
+
+ @Override
+ public Attributes lstat(String path) throws IOException {
+ return delegate.lstat(path);
+ }
+
+ @Override
+ public Attributes stat(Handle handle) throws IOException {
+ return delegate.stat(handle);
+ }
+
+ @Override
+ public void setStat(String path, Attributes attributes) throws IOException {
+ delegate.setStat(path, attributes);
+ }
+
+ @Override
+ public void setStat(Handle handle, Attributes attributes) throws IOException {
+ delegate.setStat(handle, attributes);
+ }
+
+ @Override
+ public String readLink(String path) throws IOException {
+ return delegate.readLink(path);
+ }
+
+ @Override
+ public void symLink(String linkPath, String targetPath) throws IOException {
+ delegate.symLink(linkPath, targetPath);
+ }
+
+ @Override
+ public Iterable<DirEntry> readDir(String path) throws IOException {
+ return delegate.readDir(path);
+ }
+
+ @Override
+ public InputStream read(String path) throws IOException {
+ return delegate.read(path);
+ }
+
+ @Override
+ public InputStream read(String path, EnumSet<OpenMode> mode) throws IOException {
+ return delegate.read(path, mode);
+ }
+
+ @Override
+ public OutputStream write(String path) throws IOException {
+ return delegate.write(path);
+ }
+
+ @Override
+ public OutputStream write(String path, EnumSet<OpenMode> mode) throws IOException {
+ return delegate.write(path, mode);
+ }
+
+ }
+
+ protected static class DefaultUserPrincipalLookupService extends UserPrincipalLookupService {
+
+ @Override
+ public UserPrincipal lookupPrincipalByName(String name) throws IOException {
+ return new DefaultUserPrincipal(name);
+ }
+
+ @Override
+ public GroupPrincipal lookupPrincipalByGroupName(String group) throws IOException {
+ return new DefaultGroupPrincipal(group);
+ }
+
+ }
+
+ protected static class DefaultUserPrincipal implements UserPrincipal {
+
+ private final String name;
+
+ public DefaultUserPrincipal(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("name is null");
+ }
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DefaultUserPrincipal that = (DefaultUserPrincipal) o;
+ return name.equals(that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ }
+
+ protected static class DefaultGroupPrincipal extends DefaultUserPrincipal implements GroupPrincipal {
+
+ public DefaultGroupPrincipal(String name) {
+ super(name);
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
new file mode 100644
index 0000000..07c0dc2
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
@@ -0,0 +1,665 @@
+/*
+ * 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.client.sftp;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.AccessDeniedException;
+import java.nio.file.AccessMode;
+import java.nio.file.CopyOption;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystemAlreadyExistsException;
+import java.nio.file.FileSystemNotFoundException;
+import java.nio.file.LinkOption;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.ProviderMismatchException;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.GroupPrincipal;
+import java.nio.file.attribute.PosixFileAttributeView;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.UserPrincipal;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshBuilder;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.client.SftpClient;
+import org.apache.sshd.client.SftpException;
+
+public class SftpFileSystemProvider extends FileSystemProvider {
+
+ public static final int SSH_FX_NO_SUCH_FILE = 2;
+ public static final int SSH_FX_FILE_ALREADY_EXISTS = 11;
+
+ final SshClient client;
+ final Map<String, SftpFileSystem> fileSystems = new HashMap<String, SftpFileSystem>();
+
+ public SftpFileSystemProvider() {
+ this(null);
+ }
+
+ public SftpFileSystemProvider(SshClient client) {
+ if (client == null) {
+ // TODO: make this configurable using system properties
+ client = SshBuilder.client().build();
+ }
+ this.client = client;
+ this.client.start();
+ }
+
+ @Override
+ public String getScheme() {
+ return "sftp";
+ }
+
+ @Override
+ public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
+ synchronized (fileSystems) {
+ String authority = uri.getAuthority();
+ SftpFileSystem fileSystem = fileSystems.get(authority);
+ if (fileSystem != null) {
+ throw new FileSystemAlreadyExistsException(authority);
+ }
+ String host = uri.getHost();
+ String userInfo = uri.getUserInfo();
+ if (host == null) {
+ throw new IllegalArgumentException("Host not provided");
+ }
+ if (userInfo == null) {
+ throw new IllegalArgumentException("UserInfo not provided");
+ }
+ String[] ui = userInfo.split(":");
+ ClientSession session;
+ try {
+ session = client.connect(ui[0], host, uri.getPort() > 0 ? uri.getPort() : 22)
+ .await().getSession();
+ } catch (InterruptedException e) {
+ throw new InterruptedIOException();
+ }
+ session.addPasswordIdentity(ui[1]);
+ session.auth().verify();
+ fileSystem = new SftpFileSystem(this, session);
+ fileSystems.put(authority, fileSystem);
+ return fileSystem;
+ }
+ }
+
+ @Override
+ public FileSystem getFileSystem(URI uri) {
+ synchronized (fileSystems) {
+ String authority = uri.getAuthority();
+ SftpFileSystem fileSystem = fileSystems.get(authority);
+ if (fileSystem == null) {
+ throw new FileSystemNotFoundException(authority);
+ }
+ return fileSystem;
+ }
+ }
+
+ @Override
+ public Path getPath(URI uri) {
+ return getFileSystem(uri).getPath(uri.getPath());
+ }
+
+ @Override
+ public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
+ final SftpPath p = toSftpPath(path);
+ final EnumSet<SftpClient.OpenMode> modes = EnumSet.noneOf(SftpClient.OpenMode.class);
+ for (OpenOption option : options) {
+ if (option == StandardOpenOption.READ) {
+ modes.add(SftpClient.OpenMode.Read);
+ } else if (option == StandardOpenOption.APPEND) {
+ modes.add(SftpClient.OpenMode.Append);
+ } else if (option == StandardOpenOption.CREATE) {
+ modes.add(SftpClient.OpenMode.Create);
+ } else if (option == StandardOpenOption.TRUNCATE_EXISTING) {
+ modes.add(SftpClient.OpenMode.Truncate);
+ } else if (option == StandardOpenOption.WRITE) {
+ modes.add(SftpClient.OpenMode.Write);
+ } else if (option == StandardOpenOption.CREATE_NEW) {
+ modes.add(SftpClient.OpenMode.Create);
+ modes.add(SftpClient.OpenMode.Exclusive);
+ } else {
+ throw new IllegalArgumentException("Unsupported open option " + option);
+ }
+ }
+ if (modes.isEmpty()) {
+ modes.add(SftpClient.OpenMode.Read);
+ }
+ return new SeekableByteChannel() {
+ final SftpClient sftp = p.getFileSystem().getClient();
+ final SftpClient.Handle handle = sftp.open(p.toString(), modes);
+ long pos = 0;
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ if (dst.hasArray()) {
+ int read = sftp.read(handle, pos, dst.array(), dst.arrayOffset() + dst.position(), dst.remaining());
+ if (read > 0) {
+ dst.position(dst.position() + read);
+ pos += read;
+ }
+ return read;
+ } else {
+ int remaining = Math.min(8192, dst.remaining());
+ byte[] buf = new byte[remaining];
+ int read = sftp.read(handle, pos, buf, 0, remaining);
+ if (read > 0) {
+ dst.put(buf, 0, read);
+ pos += read;
+ }
+ return read;
+ }
+ }
+
+ @Override
+ public int write(ByteBuffer src) throws IOException {
+ if (src.hasArray()) {
+ int rem = src.remaining();
+ sftp.write(handle, pos, src.array(), src.arrayOffset() + src.position(), rem);
+ src.position(src.position() + rem);
+ pos += rem;
+ return rem;
+ } else {
+ byte[] buf = new byte[Math.min(8192, src.remaining())];
+ src.get(buf);
+ sftp.write(handle, pos, buf, 0, buf.length);
+ pos += buf.length;
+ return buf.length;
+ }
+ }
+
+ @Override
+ public long position() throws IOException {
+ if (pos < 0) {
+ throw new ClosedChannelException();
+ }
+ return pos;
+ }
+
+ @Override
+ public SeekableByteChannel position(long newPosition) throws IOException {
+ if (newPosition < 0) {
+ throw new IllegalArgumentException();
+ }
+ pos = newPosition;
+ return this;
+ }
+
+ @Override
+ public long size() throws IOException {
+ return sftp.stat(handle).size;
+ }
+
+ @Override
+ public SeekableByteChannel truncate(long size) throws IOException {
+ sftp.setStat(handle, new SftpClient.Attributes().size(size));
+ return this;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return pos >= 0;
+ }
+
+ @Override
+ public void close() throws IOException {
+ sftp.close(handle);
+ sftp.close();
+ pos = -1;
+ }
+ };
+ }
+
+ @Override
+ public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException {
+ final SftpPath p = toSftpPath(dir);
+ return new DirectoryStream<Path>() {
+ final SftpClient sftp = p.getFileSystem().getClient();
+ final Iterable<SftpClient.DirEntry> iter = sftp.readDir(p.toString());
+ @Override
+ public Iterator<Path> iterator() {
+ return new Iterator<Path>() {
+ final Iterator<SftpClient.DirEntry> it = iter.iterator();
+ @Override
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+
+ @Override
+ public Path next() {
+ SftpClient.DirEntry entry = it.next();
+ return p.resolve(entry.filename);
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override
+ public void close() throws IOException {
+ sftp.close();
+ }
+ };
+ }
+
+ @Override
+ public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
+ SftpPath p = toSftpPath(dir);
+ try (SftpClient sftp = p.getFileSystem().getClient()) {
+ // TODO: handle attributes
+ try {
+ sftp.mkdir(dir.toString());
+ } catch (SftpException e) {
+ if (e.getStatus() == SSH_FX_FILE_ALREADY_EXISTS) {
+ throw new FileAlreadyExistsException(p.toString());
+ }
+ throw e;
+ }
+ }
+ }
+
+ @Override
+ public void delete(Path path) throws IOException {
+ SftpPath p = toSftpPath(path);
+ checkAccess(p, AccessMode.WRITE);
+ try (SftpClient sftp = p.getFileSystem().getClient()) {
+ BasicFileAttributes attributes = readAttributes(path, BasicFileAttributes.class);
+ if (attributes.isDirectory()) {
+ sftp.rmdir(path.toString());
+ } else {
+ sftp.remove(path.toString());
+ }
+ }
+ }
+
+ @Override
+ public void copy(Path source, Path target, CopyOption... options) throws IOException {
+ // TODO
+ }
+
+ @Override
+ public void move(Path source, Path target, CopyOption... options) throws IOException {
+ // TODO
+
+ }
+
+ @Override
+ public boolean isSameFile(Path path1, Path path2) throws IOException {
+ SftpPath p1 = toSftpPath(path1);
+ SftpPath p2 = toSftpPath(path2);
+ checkAccess(p1);
+ checkAccess(p2);
+ return p1.equals(p2);
+ }
+
+ @Override
+ public boolean isHidden(Path path) throws IOException {
+ return false;
+ }
+
+ @Override
+ public FileStore getFileStore(Path path) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs) throws IOException {
+ SftpPath l = toSftpPath(link);
+ SftpPath t = toSftpPath(target);
+ if (l.getFileSystem() != t.getFileSystem()) {
+ throw new ProviderMismatchException();
+ }
+ try (SftpClient client = l.getFileSystem().getClient()) {
+ client.symLink(l.toString(), t.toString());
+ }
+ }
+
+ @Override
+ public Path readSymbolicLink(Path link) throws IOException {
+ SftpPath l = toSftpPath(link);
+ try (SftpClient client = l.getFileSystem().getClient()) {
+ return l.getFileSystem().getPath(client.readLink(l.toString()));
+ }
+ }
+
+ @Override
+ public void checkAccess(Path path, AccessMode... modes) throws IOException {
+ SftpPath p = toSftpPath(path);
+ boolean w = false;
+ boolean x = false;
+ for (AccessMode mode : modes) {
+ switch (mode) {
+ case READ:
+ break;
+ case WRITE:
+ w = true;
+ break;
+ case EXECUTE:
+ x = true;
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+ BasicFileAttributes attrs = getFileAttributeView(p, BasicFileAttributeView.class).readAttributes();
+ if (attrs == null && !(p.isAbsolute() && p.getNameCount() == 0)) {
+ throw new NoSuchFileException(toString());
+ }
+ if (x || w && p.getFileSystem().isReadOnly()) {
+ throw new AccessDeniedException(toString());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <V extends FileAttributeView> V getFileAttributeView(final Path path, Class<V> type, final LinkOption... options) {
+ if (type.isAssignableFrom(PosixFileAttributeView.class)) {
+ return (V) new PosixFileAttributeView() {
+ @Override
+ public String name() {
+ return "view";
+ }
+
+ @Override
+ public PosixFileAttributes readAttributes() throws IOException {
+ SftpPath p = toSftpPath(path);
+ final SftpClient.Attributes attributes;
+ try (SftpClient client = p.getFileSystem().getClient()) {
+ try {
+ if (followLinks(options)) {
+ attributes = client.stat(p.toString());
+ } else {
+ attributes = client.lstat(p.toString());
+ }
+ } catch (SftpException e) {
+ if (e.getStatus() == SSH_FX_NO_SUCH_FILE) {
+ throw new NoSuchFileException(p.toString());
+ }
+ throw e;
+ }
+ }
+ return new PosixFileAttributes() {
+ @Override
+ public UserPrincipal owner() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public GroupPrincipal group() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public Set<PosixFilePermission> permissions() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public FileTime lastModifiedTime() {
+ return FileTime.from(attributes.mtime, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public FileTime lastAccessTime() {
+ return FileTime.from(attributes.atime, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public FileTime creationTime() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public boolean isRegularFile() {
+ return attributes.isRegularFile();
+ }
+
+ @Override
+ public boolean isDirectory() {
+ return attributes.isDirectory();
+ }
+
+ @Override
+ public boolean isSymbolicLink() {
+ return attributes.isSymbolicLink();
+ }
+
+ @Override
+ public boolean isOther() {
+ return attributes.isOther();
+ }
+
+ @Override
+ public long size() {
+ return attributes.size;
+ }
+
+ @Override
+ public Object fileKey() {
+ // TODO
+ return null;
+ }
+ };
+ }
+
+ @Override
+ public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime) throws IOException {
+ if (lastModifiedTime != null) {
+ setAttribute(path, "lastModifiedTime", lastModifiedTime, options);
+ }
+ if (lastAccessTime != null) {
+ setAttribute(path, "lastAccessTime", lastAccessTime, options);
+ }
+ if (createTime != null) {
+ setAttribute(path, "createTime", createTime, options);
+ }
+ }
+
+ @Override
+ public void setPermissions(Set<PosixFilePermission> perms) throws IOException {
+ setAttribute(path, "permissions", perms, options);
+ }
+
+ @Override
+ public void setGroup(GroupPrincipal group) throws IOException {
+ setAttribute(path, "group", group, options);
+ }
+
+ @Override
+ public UserPrincipal getOwner() throws IOException {
+ return readAttributes().owner();
+ }
+
+ @Override
+ public void setOwner(UserPrincipal owner) throws IOException {
+ setAttribute(path, "owner", owner, options);
+ }
+ };
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options) throws IOException {
+ if (type.isAssignableFrom(PosixFileAttributes.class)) {
+ return (A) getFileAttributeView(path, PosixFileAttributeView.class, options).readAttributes();
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
+ String view;
+ String attrs;
+ int i = attributes.indexOf(':');
+ if (i == -1) {
+ view = "basic";
+ attrs = attributes;
+ } else {
+ view = attributes.substring(0, i++);
+ attrs = attributes.substring(i);
+ }
+ SftpPath p = toSftpPath(path);
+ if (!p.getFileSystem().supportedFileAttributeViews().contains(view)) {
+ throw new UnsupportedOperationException();
+ }
+ PosixFileAttributes v = readAttributes(path, PosixFileAttributes.class, options);
+ if ("*".equals(attrs)) {
+ attrs = "lastModifiedTime,lastAccessTime,creationTime,size,isRegularFile,isDirectory,isSymbolicLink,isOther,fileKey,owner,permissions,group";
+ }
+ Map<String, Object> map = new HashMap<>();
+ for (String attr : attrs.split(",")) {
+ switch (attr) {
+ case "lastModifiedTime":
+ map.put(attr, v.lastModifiedTime());
+ break;
+ case "lastAccessTime":
+ map.put(attr, v.lastAccessTime());
+ break;
+ case "creationTime":
+ map.put(attr, v.creationTime());
+ break;
+ case "size":
+ map.put(attr, v.size());
+ break;
+ case "isRegularFile":
+ map.put(attr, v.isRegularFile());
+ break;
+ case "isDirectory":
+ map.put(attr, v.isDirectory());
+ break;
+ case "isSymbolicLink":
+ map.put(attr, v.isSymbolicLink());
+ break;
+ case "isOther":
+ map.put(attr, v.isOther());
+ break;
+ case "fileKey":
+ map.put(attr, v.fileKey());
+ break;
+ case "owner":
+ map.put(attr, v.owner());
+ break;
+ case "permissions":
+ map.put(attr, v.permissions());
+ break;
+ case "group":
+ map.put(attr, v.group());
+ break;
+ }
+ }
+ return map;
+ }
+
+ @Override
+ public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
+ String view;
+ String attr;
+ int i = attribute.indexOf(':');
+ if (i == -1) {
+ view = "basic";
+ attr = attribute;
+ } else {
+ view = attribute.substring(0, i++);
+ attr = attribute.substring(i);
+ }
+ SftpPath p = toSftpPath(path);
+ if (!p.getFileSystem().supportedFileAttributeViews().contains(view)) {
+ throw new UnsupportedOperationException();
+ }
+ SftpClient.Attributes attributes = new SftpClient.Attributes();
+ switch (attr) {
+ case "lastModifiedTime":
+ attributes.mtime = (int) ((FileTime) value).to(TimeUnit.SECONDS);
+ break;
+ case "lastAccessTime":
+ attributes.atime = (int) ((FileTime) value).to(TimeUnit.SECONDS);
+ break;
+ case "size":
+ attributes.size = (long) value;
+ break;
+ case "owner":
+ case "permissions":
+ case "group":
+ // TODO: handle those
+ throw new IllegalArgumentException(attr);
+ case "creationTime":
+ case "isRegularFile":
+ case "isDirectory":
+ case "isSymbolicLink":
+ case "isOther":
+ case "fileKey":
+ throw new IllegalArgumentException(attr);
+ }
+ try (SftpClient client = p.getFileSystem().getClient()) {
+ client.setStat(p.toString(), attributes);
+ }
+ }
+
+ private SftpPath toSftpPath(Path path) {
+ if (path == null) {
+ throw new NullPointerException();
+ }
+ if (!(path instanceof SftpPath)) {
+ throw new ProviderMismatchException();
+ }
+ return (SftpPath) path;
+ }
+
+ static boolean followLinks(LinkOption... paramVarArgs)
+ {
+ boolean bool = true;
+ for (LinkOption localLinkOption : paramVarArgs) {
+ if (localLinkOption == LinkOption.NOFOLLOW_LINKS) {
+ bool = false;
+ }
+ }
+ return bool;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpPath.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpPath.java b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpPath.java
new file mode 100644
index 0000000..8e70c62
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpPath.java
@@ -0,0 +1,49 @@
+/*
+ * 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.client.sftp;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.LinkOption;
+
+import org.apache.sshd.common.file.util.BasePath;
+import org.apache.sshd.common.file.util.ImmutableList;
+
+public class SftpPath extends BasePath<SftpPath, SftpFileSystem> {
+
+ public SftpPath(SftpFileSystem fileSystem, String root, ImmutableList<String> names) {
+ super(fileSystem, root, names);
+ }
+
+ public URI toUri() {
+ // TODO
+ return null;
+ }
+
+ public SftpPath toRealPath(LinkOption... options) throws IOException {
+// try (SftpClient client = fileSystem.getClient()) {
+// client.realP
+// }
+ // TODO: handle links
+ SftpPath absolute = toAbsolutePath();
+ fileSystem.provider().checkAccess(absolute);
+ return absolute;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFileNio.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFileNio.java b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFileNio.java
index 9753ed8..b0596d1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFileNio.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFileNio.java
@@ -174,8 +174,8 @@ public class NativeSshFileNio extends NativeSshFile {
public void createSymbolicLink(SshFile destination) throws IOException {
Path link = file.toPath();
- Path target = Paths.get(destination.getAbsolutePath());
- Files.createSymbolicLink(target, link);
+ Path target = Paths.get(destination.toString());
+ Files.createSymbolicLink(link, target);
}
private EnumSet<Permission> fromPerms(Set<PosixFilePermission> perms) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/main/java/org/apache/sshd/common/file/util/BaseFileSystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/util/BaseFileSystem.java b/sshd-core/src/main/java/org/apache/sshd/common/file/util/BaseFileSystem.java
new file mode 100644
index 0000000..68869ca
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/util/BaseFileSystem.java
@@ -0,0 +1,239 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.WatchService;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.regex.Pattern;
+
+public abstract class BaseFileSystem<T extends Path> extends FileSystem {
+
+ private final FileSystemProvider fileSystemProvider;
+
+ public BaseFileSystem(FileSystemProvider fileSystemProvider) {
+ this.fileSystemProvider = fileSystemProvider;
+ }
+
+ public T getDefaultDir() {
+ return getPath("/");
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return false;
+ }
+
+ @Override
+ public FileSystemProvider provider() {
+ return fileSystemProvider;
+ }
+
+ @Override
+ public String getSeparator() {
+ return "/";
+ }
+
+ @Override
+ public Iterable<Path> getRootDirectories() {
+ return Collections.<Path>singleton(create("/"));
+ }
+
+ @Override
+ public Iterable<FileStore> getFileStores() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public T getPath(String first, String... more) {
+ StringBuilder sb = new StringBuilder();
+ appendDedupSep(sb, first);
+ for (String segment : more) {
+ if (sb.length() > 0 && sb.charAt(sb.length() - 1) != '/') {
+ sb.append('/');
+ }
+ appendDedupSep(sb, segment);
+ }
+ if (sb.length() > 1 && sb.charAt(sb.length() - 1) == '/') {
+ sb.setLength(sb.length() - 1);
+ }
+ String path = sb.toString();
+ String root = null;
+ if (path.startsWith("/")) {
+ root = "/";
+ path = path.substring(1);
+ }
+ String[] names;
+ if (path.length() > 0) {
+ names = path.split("/");
+ } else {
+ names = new String[0];
+ }
+ return create(root, names);
+ }
+
+ private void appendDedupSep(StringBuilder sb, String s) {
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ if (ch != '/' || sb.length() == 0 || sb.charAt(sb.length() - 1) != '/') {
+ sb.append(ch);
+ }
+ }
+ }
+
+ @Override
+ public PathMatcher getPathMatcher(String syntaxAndPattern) {
+ int colonIndex = syntaxAndPattern.indexOf(':');
+ if (colonIndex <= 0 || colonIndex == syntaxAndPattern.length() - 1) {
+ throw new IllegalArgumentException("syntaxAndPattern must have form \"syntax:pattern\" but was \"" + syntaxAndPattern + "\"");
+ }
+
+ String syntax = syntaxAndPattern.substring(0, colonIndex);
+ String pattern = syntaxAndPattern.substring(colonIndex + 1);
+ String expr;
+ switch (syntax) {
+ case "glob":
+ expr = globToRegex(pattern);
+ break;
+ case "regex":
+ expr = pattern;
+ break;
+ default:
+ throw new UnsupportedOperationException("Unsupported syntax \'" + syntax + "\'");
+ }
+ final Pattern regex = Pattern.compile(expr);
+ return new PathMatcher() {
+ @Override
+ public boolean matches(Path path) {
+ return regex.matcher(path.toString()).matches();
+ }
+ };
+ }
+
+ private String globToRegex(String pattern) {
+ StringBuilder sb = new StringBuilder(pattern.length());
+ int inGroup = 0;
+ int inClass = 0;
+ int firstIndexInClass = -1;
+ char[] arr = pattern.toCharArray();
+ for (int i = 0; i < arr.length; i++) {
+ char ch = arr[i];
+ switch (ch) {
+ case '\\':
+ if (++i >= arr.length) {
+ sb.append('\\');
+ } else {
+ char next = arr[i];
+ switch (next) {
+ case ',':
+ // escape not needed
+ break;
+ case 'Q':
+ case 'E':
+ // extra escape needed
+ sb.append('\\');
+ default:
+ sb.append('\\');
+ }
+ sb.append(next);
+ }
+ break;
+ case '*':
+ if (inClass == 0)
+ sb.append(".*");
+ else
+ sb.append('*');
+ break;
+ case '?':
+ if (inClass == 0)
+ sb.append('.');
+ else
+ sb.append('?');
+ break;
+ case '[':
+ inClass++;
+ firstIndexInClass = i+1;
+ sb.append('[');
+ break;
+ case ']':
+ inClass--;
+ sb.append(']');
+ break;
+ case '.':
+ case '(':
+ case ')':
+ case '+':
+ case '|':
+ case '^':
+ case '$':
+ case '@':
+ case '%':
+ if (inClass == 0 || (firstIndexInClass == i && ch == '^'))
+ sb.append('\\');
+ sb.append(ch);
+ break;
+ case '!':
+ if (firstIndexInClass == i)
+ sb.append('^');
+ else
+ sb.append('!');
+ break;
+ case '{':
+ inGroup++;
+ sb.append('(');
+ break;
+ case '}':
+ inGroup--;
+ sb.append(')');
+ break;
+ case ',':
+ if (inGroup > 0)
+ sb.append('|');
+ else
+ sb.append(',');
+ break;
+ default:
+ sb.append(ch);
+ }
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public WatchService newWatchService() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ protected T create(String root, String... names) {
+ return create(root, new ImmutableList<>(names));
+ }
+
+ protected T create(String root, Collection<String> names) {
+ return create(root, new ImmutableList<>(names.toArray(new String[names.size()])));
+ }
+
+ protected abstract T create(String root, ImmutableList<String> names);
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/main/java/org/apache/sshd/common/file/util/BasePath.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/util/BasePath.java b/sshd-core/src/main/java/org/apache/sshd/common/file/util/BasePath.java
new file mode 100644
index 0000000..a5fde82
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/util/BasePath.java
@@ -0,0 +1,367 @@
+/*
+ * 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.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.ProviderMismatchException;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.AbstractList;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+public abstract class BasePath<T extends BasePath<T, FS>, FS extends BaseFileSystem<T>> implements Path {
+
+ protected final FS fileSystem;
+ protected final String root;
+ protected final ImmutableList<String> names;
+
+ public BasePath(FS fileSystem, String root, ImmutableList<String> names) {
+ this.fileSystem = fileSystem;
+ this.root = root;
+ this.names = names;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected T asT() {
+ return (T) this;
+ }
+ protected T create(String root, String... names) {
+ return create(root, new ImmutableList<>(names));
+ }
+
+ protected T create(String root, Collection<String> names) {
+ return create(root, new ImmutableList<>(names.toArray(new String[names.size()])));
+ }
+
+ protected T create(String root, ImmutableList<String> names) {
+ return fileSystem.create(root, names);
+ }
+
+ public FS getFileSystem() {
+ return fileSystem;
+ }
+
+ public boolean isAbsolute() {
+ return root != null;
+ }
+
+ public T getRoot() {
+ if (isAbsolute()) {
+ return create(root);
+ }
+ return null;
+ }
+
+ public T getFileName() {
+ if (!names.isEmpty()) {
+ return create(null, names.get(names.size() - 1));
+ }
+ return null;
+ }
+
+ public T getParent() {
+ if (names.isEmpty() || names.size() == 1 && root == null) {
+ return null;
+ }
+ return create(root, names.subList(0, names.size() - 1));
+ }
+
+ public int getNameCount() {
+ return names.size();
+ }
+
+ public T getName(int index) {
+ if (index < 0 || index >= names.size()) {
+ throw new IllegalArgumentException();
+ }
+ return create(null, names.subList(index, index + 1));
+ }
+
+ public T subpath(int beginIndex, int endIndex) {
+ if ((beginIndex < 0) || (beginIndex >= names.size()) || (endIndex > names.size()) || (beginIndex >= endIndex)) {
+ throw new IllegalArgumentException();
+ }
+ return create(null, names.subList(beginIndex, endIndex));
+ }
+
+ private static boolean startsWith(List<?> list, List<?> other) {
+ return list.size() >= other.size() && list.subList(0, other.size()).equals(other);
+ }
+
+ public boolean startsWith(Path other) {
+ T p1 = asT();
+ T p2 = checkPath(other);
+ return p1.getFileSystem().equals(p2.getFileSystem())
+ && Objects.equals(p1.root, p2.root)
+ && startsWith(p1.names, p2.names);
+ }
+
+ public boolean startsWith(String other) {
+ return startsWith(getFileSystem().getPath(other));
+ }
+
+ private static boolean endsWith(List<?> list, List<?> other) {
+ return other.size() <= list.size() && list.subList(list.size() - other.size(), list.size()).equals(other);
+ }
+
+ public boolean endsWith(Path other) {
+ T p1 = asT();
+ T p2 = checkPath(other);
+ if (p2.isAbsolute()) {
+ return p1.compareTo(p2) == 0;
+ }
+ return endsWith(p1.names, p2.names);
+ }
+
+ public boolean endsWith(String other) {
+ return endsWith(getFileSystem().getPath(other));
+ }
+
+ private boolean isNormal() {
+ if (getNameCount() == 0 || getNameCount() == 1 && !isAbsolute()) {
+ return true;
+ }
+ boolean foundNonParentName = isAbsolute(); // if there's a root, the path doesn't start with ..
+ boolean normal = true;
+ for (String name : names) {
+ if (name.equals("..")) {
+ if (foundNonParentName) {
+ normal = false;
+ break;
+ }
+ } else {
+ if (name.equals(".")) {
+ normal = false;
+ break;
+ }
+ foundNonParentName = true;
+ }
+ }
+ return normal;
+ }
+
+ public T normalize() {
+ if (isNormal()) {
+ return asT();
+ }
+
+ Deque<String> newNames = new ArrayDeque<>();
+ for (String name : names) {
+ if (name.equals("..")) {
+ String lastName = newNames.peekLast();
+ if (lastName != null && !lastName.equals("..")) {
+ newNames.removeLast();
+ } else if (!isAbsolute()) {
+ // if there's a root and we have an extra ".." that would go up above the root, ignore it
+ newNames.add(name);
+ }
+ } else if (!name.equals(".")) {
+ newNames.add(name);
+ }
+ }
+
+ return newNames.equals(names) ? asT() : create(root, newNames);
+ }
+
+ public T resolve(Path other) {
+ T p1 = asT();
+ T p2 = checkPath(other);
+ if (p2.isAbsolute()) {
+ return p2;
+ }
+ if (p2.names.isEmpty()) {
+ return p1;
+ }
+ String[] names = new String[p1.names.size() + p2.names.size()];
+ int index = 0;
+ for (String p : p1.names) {
+ names[index++] = p;
+ }
+ for (String p : p2.names) {
+ names[index++] = p;
+ }
+ return create(p1.root, names);
+ }
+
+ public T resolve(String other) {
+ return resolve(getFileSystem().getPath(other));
+ }
+
+ public Path resolveSibling(Path other) {
+ if (other == null) {
+ throw new NullPointerException();
+ }
+ T parent = getParent();
+ return parent == null ? other : parent.resolve(other);
+ }
+
+ public Path resolveSibling(String other) {
+ return resolveSibling(getFileSystem().getPath(other));
+ }
+
+ public T relativize(Path other) {
+ T p1 = asT();
+ T p2 = checkPath(other);
+ if (!Objects.equals(p1.getRoot(), p2.getRoot())) {
+ throw new IllegalArgumentException("Paths have different roots: " + this + ", " + other);
+ }
+ if (p2.equals(p1)) {
+ return create(null);
+ }
+ if (p1.root == null && p1.names.isEmpty()) {
+ return p2;
+ }
+ // Common subsequence
+ int sharedSubsequenceLength = 0;
+ for (int i = 0; i < Math.min(p1.names.size(), p2.names.size()); i++) {
+ if (p1.names.get(i).equals(p2.names.get(i))) {
+ sharedSubsequenceLength++;
+ } else {
+ break;
+ }
+ }
+ int extraNamesInThis = Math.max(0, p1.names.size() - sharedSubsequenceLength);
+ List<String> extraNamesInOther = (p2.names.size() <= sharedSubsequenceLength)
+ ? Collections.<String>emptyList()
+ : p2.names.subList(sharedSubsequenceLength, p2.names.size());
+ List<String> parts = new ArrayList<>(extraNamesInThis + extraNamesInOther.size());
+ // add .. for each extra name in this path
+ parts.addAll(Collections.nCopies(extraNamesInThis, ".."));
+ // add each extra name in the other path
+ parts.addAll(extraNamesInOther);
+ return create(null, parts);
+ }
+
+ public T toAbsolutePath() {
+ if (isAbsolute()) {
+ return asT();
+ }
+ return fileSystem.getDefaultDir().resolve(this);
+ }
+
+ public File toFile() {
+ throw new UnsupportedOperationException();
+ }
+
+ public WatchKey register(WatchService watcher, WatchEvent.Kind<?>[] events, WatchEvent.Modifier... modifiers) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Iterator<Path> iterator() {
+ return new AbstractList<Path>() {
+ @Override
+ public Path get(int index) {
+ return getName(index);
+ }
+
+ @Override
+ public int size() {
+ return getNameCount();
+ }
+ }.iterator();
+ }
+
+ public int compareTo(Path paramPath) {
+ T p1 = asT();
+ T p2 = checkPath(paramPath);
+ int c = compare(p1.root, p2.root);
+ if (c != 0) {
+ return c;
+ }
+ for (int i = 0; i < Math.min(p1.names.size(), p2.names.size()); i++) {
+ String n1 = p1.names.get(i);
+ String n2 = p2.names.get(i);
+ c = compare(n1, n2);
+ if (c != 0) {
+ return c;
+ }
+ }
+ return p1.names.size() - p2.names.size();
+ }
+
+ private int compare(String s1, String s2) {
+ if (s1 == null) {
+ return s2 == null ? 0 : -1;
+ } else {
+ return s2 == null ? +1 : s1.compareTo(s2);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private T checkPath(Path paramPath) {
+ if (paramPath == null) {
+ throw new NullPointerException();
+ }
+ if (paramPath.getClass() != getClass()) {
+ throw new ProviderMismatchException();
+ }
+ T t = (T) paramPath;
+ if (t.fileSystem.provider() != this.fileSystem.provider()) {
+ throw new ProviderMismatchException();
+ }
+ return t;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = getFileSystem().hashCode();
+ // use hash codes from toString() form of names
+ hash = 31 * hash + (root == null ? 0 : root.hashCode());
+ for (String name : names) {
+ hash = 31 * hash + name.hashCode();
+ }
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Path
+ && compareTo((Path) obj) == 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (root != null) {
+ sb.append(root);
+ }
+ for (String name : names) {
+ if (sb.length() > 0 && sb.charAt(sb.length() - 1) != '/') {
+ sb.append(fileSystem.getSeparator());
+ }
+ sb.append(name);
+ }
+ return sb.toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/main/java/org/apache/sshd/common/file/util/ImmutableList.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/util/ImmutableList.java b/sshd-core/src/main/java/org/apache/sshd/common/file/util/ImmutableList.java
new file mode 100644
index 0000000..88d9f5c
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/util/ImmutableList.java
@@ -0,0 +1,61 @@
+/*
+ * 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.util;
+
+import java.util.AbstractList;
+
+/**
+ * Simple immutable array list
+ * @param <T>
+ */
+public class ImmutableList<T> extends AbstractList<T> {
+
+ final T[] data;
+ final int from;
+ final int to;
+
+ public ImmutableList(T[] data) {
+ this(data, 0, data.length);
+ }
+
+ public ImmutableList(T[] data, int from, int to) {
+ this.data = data;
+ this.from = from;
+ this.to = to;
+ }
+
+ @Override
+ public T get(int index) {
+ return data[from + index];
+ }
+
+ @Override
+ public int size() {
+ return to - from;
+ }
+
+ @Override
+ public ImmutableList<T> subList(int fromIndex, int toIndex) {
+ if (fromIndex == from && toIndex == to) {
+ return this;
+ }
+ return new ImmutableList<>(data, from + fromIndex, from + toIndex);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
index 3ce1c78..643a1b7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
@@ -165,6 +165,8 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
public static final int SSH_FX_CONNECTION_LOST = 7;
public static final int SSH_FX_OP_UNSUPPORTED = 8;
+ public static final int SSH_FX_FILE_ALREADY_EXISTS = 11; // Not in v3, but we need it
+
public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
public static final int SSH_FILEXFER_ATTR_UIDGID = 0x00000002;
public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
@@ -768,7 +770,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
SshFile p = resolveFile(path);
if (p.doesExist()) {
if (p.isDirectory()) {
- sendStatus(id, SSH_FX_FAILURE, p.getAbsolutePath());
+ sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, p.getAbsolutePath());
} else {
sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath());
}
@@ -885,6 +887,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
log.debug("Received SSH_FXP_SYMLINK (linkpath={}, targetpath={})", linkpath, targetpath);
try {
SshFile link = resolveFile(linkpath);
+ // TODO: resolving the file is wrong, we should keep it relative
SshFile target = resolveFile(targetpath);
link.createSymbolicLink(target);
sendStatus(id, SSH_FX_OK, "");
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/cc4d7877/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java b/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
new file mode 100644
index 0000000..42d1dcc
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.net.URI;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Map;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.command.ScpCommandFactory;
+import org.apache.sshd.server.sftp.SftpSubsystem;
+import org.apache.sshd.util.BaseTest;
+import org.apache.sshd.util.BogusPasswordAuthenticator;
+import org.apache.sshd.util.EchoShellFactory;
+import org.apache.sshd.util.Utils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class SftpFileSystemTest extends BaseTest {
+
+ private SshServer sshd;
+ private int port;
+
+ @Before
+ public void setUp() throws Exception {
+ sshd = SshServer.setUpDefaultServer();
+ sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
+ sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
+ sshd.setCommandFactory(new ScpCommandFactory());
+ sshd.setShellFactory(new EchoShellFactory());
+ sshd.setPasswordAuthenticator(new BogusPasswordAuthenticator());
+ sshd.start();
+ port = sshd.getPort();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ sshd.stop(true);
+ }
+
+ @Test
+ public void testFileSystem() throws Exception {
+ Utils.deleteRecursive(new File("target/sftp"));
+
+ FileSystem fs = FileSystems.newFileSystem(URI.create("sftp://x:x@localhost:" + port + "/"), null);
+ Path root = fs.getRootDirectories().iterator().next();
+ try (DirectoryStream<Path> ds = Files.newDirectoryStream(root)) {
+ for (Path child : ds) {
+ System.out.println(child);
+ }
+ }
+ Path file = fs.getPath("target/sftp/client/test.txt");
+ Files.createDirectories(file.getParent());
+ Files.write(file, "Hello world\n".getBytes());
+ String buf = new String(Files.readAllBytes(file));
+ assertEquals("Hello world\n", buf);
+
+ Map<String, Object> attrs = Files.readAttributes(file, "*");
+ System.out.println(attrs);
+
+ // TODO: symbolic links only work for absolute files
+// Path link = fs.getPath("target/sftp/client/test2.txt");
+// Files.createSymbolicLink(link, link.relativize(file));
+// assertTrue(Files.isSymbolicLink(link));
+// assertEquals("test.txt", Files.readSymbolicLink(link).toString());
+
+ Path link = fs.getPath("target/sftp/client/test2.txt");
+ Files.createSymbolicLink(link, file);
+ assertTrue(Files.isSymbolicLink(link));
+ assertEquals(file.toAbsolutePath().toString(), Files.readSymbolicLink(link).toString());
+
+ attrs = Files.readAttributes(file, "*", LinkOption.NOFOLLOW_LINKS);
+ System.out.println(attrs);
+
+ buf = new String(Files.readAllBytes(file));
+ assertEquals("Hello world\n", buf);
+
+ Files.delete(file);
+
+ fs.close();
+ }
+
+}
[08/15] mina-sshd git commit: [SSHD-408] Implement sftp v4, v5 and v6
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/22581fb8/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java b/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
index 750ecc9..8ad1e0b 100644
--- a/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
@@ -19,14 +19,23 @@
package org.apache.sshd;
import java.io.File;
+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.file.DirectoryStream;
+import java.nio.file.FileAlreadyExistsException;
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.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFilePermissions;
import java.util.Arrays;
import java.util.Map;
@@ -44,6 +53,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
public class SftpFileSystemTest extends BaseTest {
@@ -71,19 +81,40 @@ public class SftpFileSystemTest extends BaseTest {
public void testFileSystem() throws Exception {
Utils.deleteRecursive(new File("target/sftp"));
- FileSystem fs = FileSystems.newFileSystem(URI.create("sftp://x:x@localhost:" + port + "/"), null);
+ String uri = "sftp://x:x@localhost:" + port + "/";
+
+ FileSystem fs = FileSystems.newFileSystem(URI.create(uri), null);
Path root = fs.getRootDirectories().iterator().next();
try (DirectoryStream<Path> ds = Files.newDirectoryStream(root)) {
for (Path child : ds) {
System.out.println(child);
}
}
+ Path current = fs.getPath(".").toRealPath();
Path file = fs.getPath("target/sftp/client/test.txt");
Files.createDirectories(file.getParent());
Files.write(file, "Hello world\n".getBytes());
String buf = new String(Files.readAllBytes(file));
assertEquals("Hello world\n", buf);
+ Path file2 = fs.getPath("target/sftp/client/test2.txt");
+ Path file3 = fs.getPath("target/sftp/client/test3.txt");
+ try {
+ Files.move(file2, file3);
+ fail("Expected an IOException");
+ } catch (NoSuchFileException e) {
+ // expected
+ }
+ Files.write(file2, "h".getBytes());
+ try {
+ Files.move(file, file2);
+ fail("Expected an IOException");
+ } catch (FileAlreadyExistsException e) {
+ // expected
+ }
+ Files.move(file, file2, StandardCopyOption.REPLACE_EXISTING);
+ Files.move(file2, file);
+
Map<String, Object> attrs = Files.readAttributes(file, "*");
System.out.println(attrs);
@@ -105,12 +136,47 @@ public class SftpFileSystemTest extends BaseTest {
buf = new String(Files.readAllBytes(file));
assertEquals("Hello world\n", buf);
+ try (FileChannel channel = FileChannel.open(file)) {
+ try (FileLock lock = channel.lock()) {
+ System.out.println("Locked " + lock.toString());
+
+ try (FileChannel channel2 = FileChannel.open(file)) {
+ try (FileLock lock2 = channel2.lock()) {
+ System.out.println("Locked " + lock2.toString());
+ fail("Expected an exception");
+ } catch (OverlappingFileLockException e) {
+ // expected
+ }
+ }
+
+ }
+ }
+
Files.delete(file);
fs.close();
}
@Test
+ public void testAttributes() throws Exception {
+ Utils.deleteRecursive(new File("target/sftp"));
+
+ FileSystem fs = FileSystems.newFileSystem(URI.create("sftp://x:x@localhost:" + port + "/"), null);
+ Path file = fs.getPath("target/sftp/client/test.txt");
+ Files.createDirectories(file.getParent());
+ Files.write(file, "Hello world\n".getBytes());
+
+ Map<String, Object> attrs = Files.readAttributes(file, "posix:*");
+
+ Files.setAttribute(file, "basic:size", 2l);
+ Files.setAttribute(file, "posix:permissions", PosixFilePermissions.fromString("rwxr-----"));
+ Files.setAttribute(file, "basic:lastModifiedTime", FileTime.fromMillis(100000l));
+ Files.setAttribute(file, "posix:group", file.getFileSystem().getUserPrincipalLookupService().lookupPrincipalByGroupName("everyone"));
+
+ fs.close();
+ }
+
+ @Test
public void testRootFileSystem() throws Exception {
Path rootNative = Paths.get("target/root").toAbsolutePath();
Utils.deleteRecursive(rootNative.toFile());
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/22581fb8/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SftpTest.java b/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
index b44f6af..7926544 100644
--- a/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
@@ -31,7 +31,9 @@ import java.util.Vector;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.SftpException;
import org.apache.sshd.client.SftpClient;
+import org.apache.sshd.client.sftp.DefaultSftpClient;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.util.Buffer;
import org.apache.sshd.common.util.OsUtils;
@@ -50,6 +52,7 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
@@ -431,8 +434,52 @@ public class SftpTest extends BaseTest {
URI base = new File(System.getProperty("user.dir")).getAbsoluteFile().toURI();
String path = new File(base.relativize(url).getPath()).getParent() + "/";
path = path.replace('\\', '/');
- String real = c.realpath(path + "/foobar");
+ String real = c.realpath(path);
System.out.println(real);
+ try {
+ real = c.realpath(path + "/foobar");
+ System.out.println(real);
+ fail("Expected SftpException");
+ } catch (SftpException e) {
+ // ok
+ }
+ }
+
+ @Test
+ public void testRename() throws Exception {
+ SshClient client = SshClient.setUpDefaultClient();
+ client.start();
+ ClientSession session = client.connect("x", "localhost", port).await().getSession();
+ session.addPasswordIdentity("x");
+ session.auth().verify();
+
+ Utils.deleteRecursive(new File("target/sftp"));
+ new File("target/sftp").mkdirs();
+ new File("target/sftp/client").delete();
+
+ SftpClient sftp = session.createSftpClient();
+ try (OutputStream os = sftp.write("target/sftp/test.txt")) {
+ os.write("Hello world!\n".getBytes());
+ }
+
+ try {
+ sftp.rename("target/sftp/test2.txt", "target/sftp/test3.txt");
+ fail("Expected an SftpException");
+ } catch (org.apache.sshd.client.SftpException e) {
+ assertEquals(DefaultSftpClient.SSH_FX_NO_SUCH_FILE, e.getStatus());
+ }
+
+ try (OutputStream os = sftp.write("target/sftp/test2.txt")) {
+ os.write("H".getBytes());
+ }
+
+ try {
+ sftp.rename("target/sftp/test.txt", "target/sftp/test2.txt");
+ fail("Expected an SftpException");
+ } catch (org.apache.sshd.client.SftpException e) {
+ assertEquals(DefaultSftpClient.SSH_FX_FILE_ALREADY_EXISTS, e.getStatus());
+ }
+ sftp.rename("target/sftp/test.txt", "target/sftp/test2.txt", SftpClient.CopyMode.Overwrite);
}
@Test
@@ -458,7 +505,7 @@ public class SftpTest extends BaseTest {
assertTrue(target.exists());
assertEquals("0123456789", readFile(unixPath));
- c.symlink(linkUnixPath, unixPath);
+ c.symlink(unixPath, linkUnixPath);
assertTrue(link.exists());
assertEquals("0123456789", readFile(linkUnixPath));
[05/15] mina-sshd git commit: [SSHD-379] Require JDK 1.7
Posted by gn...@apache.org.
[SSHD-379] Require JDK 1.7
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/e9ee318a
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/e9ee318a
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/e9ee318a
Branch: refs/heads/master
Commit: e9ee318a74860d32edb26ed0f4ce6ae9fa9e0df2
Parents: 80d8a51
Author: Guillaume Nodet <gn...@apache.org>
Authored: Mon Dec 1 16:48:59 2014 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Mon Feb 23 16:21:30 2015 +0100
----------------------------------------------------------------------
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9ee318a/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 063f47e..0fe9dcf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -216,8 +216,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
- <source>1.5</source>
- <target>1.5</target>
+ <source>1.7</source>
+ <target>1.7</target>
</configuration>
</plugin>
<plugin>
[15/15] mina-sshd git commit: [SSHD-410] Do not seek on files when
unnecessary
Posted by gn...@apache.org.
[SSHD-410] Do not seek on files when unnecessary
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/55d45e95
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/55d45e95
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/55d45e95
Branch: refs/heads/master
Commit: 55d45e955f23ecfbb2382713cd77a04734c8785f
Parents: 90414db
Author: Guillaume Nodet <gn...@apache.org>
Authored: Sun Feb 22 23:56:22 2015 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Mon Feb 23 16:21:30 2015 +0100
----------------------------------------------------------------------
.../apache/sshd/server/sftp/SftpSubsystem.java | 27 +++++++++++++++++---
1 file changed, 23 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/55d45e95/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
index f515445..28fba31 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
@@ -448,6 +448,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
protected class FileHandle extends Handle {
final FileChannel channel;
+ long pos;
final List<FileLock> locks = new ArrayList<>();
public FileHandle(Path file, int flags, int access, Map<String, Object> attrs) throws IOException {
@@ -504,16 +505,34 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
setAttributes(file, attrs);
}
this.channel = channel;
+ this.pos = 0;
}
public int read(byte[] data, long offset) throws IOException {
- channel.position(offset);
- return channel.read(ByteBuffer.wrap(data));
+ return read(data, 0, data.length, offset);
+ }
+
+ public int read(byte[] data, int doff, int length, long offset) throws IOException {
+ if (pos != offset) {
+ channel.position(offset);
+ pos = offset;
+ }
+ int read = channel.read(ByteBuffer.wrap(data, doff, length));
+ pos += read;
+ return read;
}
public void write(byte[] data, long offset) throws IOException {
- channel.position(offset);
- channel.write(ByteBuffer.wrap(data));
+ write(data, 0, data.length, offset);
+ }
+
+ public void write(byte[] data, int doff, int length, long offset) throws IOException {
+ if (pos != offset) {
+ channel.position(offset);
+ pos = offset;
+ }
+ channel.write(ByteBuffer.wrap(data, doff, length));
+ pos += length;
}
@Override
[04/15] mina-sshd git commit: [SSHD-411] SSH_FX_OP_UNSUPPORTED should
not be logged as error
Posted by gn...@apache.org.
[SSHD-411] SSH_FX_OP_UNSUPPORTED should not be logged as error
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/90414db9
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/90414db9
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/90414db9
Branch: refs/heads/master
Commit: 90414db93c21e47ab153aa40bdbdf3cff5d706d7
Parents: 22581fb
Author: Guillaume Nodet <gn...@apache.org>
Authored: Sat Feb 21 21:55:12 2015 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Mon Feb 23 16:21:30 2015 +0100
----------------------------------------------------------------------
.../src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/90414db9/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
index 700e61d..f515445 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
@@ -766,7 +766,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
break;
}
default: {
- log.error("Received: {}", type);
+ log.warn("Received: {}", type);
sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command " + type + " is unsupported or not implemented");
break;
}
@@ -783,7 +783,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
doVersionSelect(buffer, id);
break;
default:
- log.error("Received unsupported SSH_FXP_EXTENDED({})", extension);
+ log.info("Received unsupported SSH_FXP_EXTENDED({})", extension);
sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command SSH_FXP_EXTENDED(" + extension + ") is unsupported or not implemented");
break;
}
[02/15] mina-sshd git commit: Upgrade to 1.0.0-SNAPSHOT and remove
unsupported modules
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpSetstatRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpSetstatRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpSetstatRequest.java
deleted file mode 100644
index f72c869..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpSetstatRequest.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_SETSTAT' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpSetstatRequest extends BaseRequest {
-
- /**
- * Creates a SshFxpSetstatRequest instance.
- *
- * @param id The request id.
- */
- public SshFxpSetstatRequest(final int id) {
- super(id);
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_SETSTAT;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[]";
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpStatRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpStatRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpStatRequest.java
deleted file mode 100644
index f78018d..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpStatRequest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_STAT' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpStatRequest extends BaseRequest {
-
- private final String path;
- private final int flags;
-
- /**
- * Create a SshFxpStatRequest instance.
- *
- * @param id The request id.
- * @param path The requested file path.
- */
- public SshFxpStatRequest(final int id, final String path, final int flags) {
- super(id);
- this.path = path;
- this.flags = flags;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_STAT;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- return getName() + "[path=" + path + "]";
- }
-
- /**
- * Returns the path.
- *
- * @return The path.
- */
- public String getPath() {
- return path;
- }
-
- public int getFlags() {
- return flags;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpWriteRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpWriteRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpWriteRequest.java
deleted file mode 100644
index 22e3b8b..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/SshFxpWriteRequest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
-* 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.sftp.request;
-
-import java.util.Arrays;
-
-import org.apache.sshd.sftp.Handle;
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for 'SSH_FXP_WRITE' request.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SshFxpWriteRequest extends BaseRequest {
- private final String handleId;
- private final long offset;
- private final Handle handle;
- private final byte[] data;
-
- /**
- * Creates a SshFxpWriteRequest instance.
- *
- * @param id The request id.
- * @param handleId The according file handle id.
- * @param offset The write offset.
- * @param data The write data.
- * @param handle The according file handle.
- */
- public SshFxpWriteRequest(
- final int id, final String handleId, final long offset, final byte[] data, final Handle handle) {
- super(id);
- this.handleId = handleId;
- this.offset = offset;
- this.data = Arrays.copyOf(data, data.length);
- this.handle = handle;
- }
-
- /**
- * Returns the write data.
- *
- * @return The write data.
- */
- public byte[] getData() {
- return Arrays.copyOf(data, data.length);
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return SftpConstants.Type.SSH_FXP_WRITE;
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- String ps;
- if (handle != null && handle.getFile() != null) {
- ps = handle.getFile().getAbsolutePath();
- } else {
- ps = "";
- }
- return getName() + "[handle=" + handleId + ", file=" + ps + ", offset=" + offset + ", length=" + data.length + "]";
- }
-
- /**
- * Returns the according handle.
- *
- * @return The according handle.
- */
- public Handle getHandle() {
- return handle;
- }
-
- /**
- * Returns the handle id.
- *
- * @return The handle id.
- */
- public String getHandleId() {
- return handleId;
- }
-
- /**
- * Returns the offset.
- *
- * @return The offset.
- */
- public long getOffset() {
- return offset;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/UnsupportedRequest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/UnsupportedRequest.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/UnsupportedRequest.java
deleted file mode 100644
index 7abcda3..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/request/UnsupportedRequest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-* 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.sftp.request;
-
-import org.apache.sshd.sftp.subsystem.SftpConstants;
-
-/**
- * Data container for unknown or unsupported requests.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class UnsupportedRequest extends BaseRequest {
-
- private final int type;
-
- /**
- * Create UnsupportedRequest instance.
- *
- * @param id The request id.
- * @param type The type code.
- */
- public UnsupportedRequest(final int id, final int type) {
- super(id);
- this.type = type;
- }
-
- /**
- * {@inheritDoc}
- */
- public String getName() {
- return "Unsupported request: " + type;
- }
-
- /**
- * {@inheritDoc}
- */
- public SftpConstants.Type getMessage() {
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/BaseHandle.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/BaseHandle.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/BaseHandle.java
deleted file mode 100644
index e4df30b..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/BaseHandle.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.sftp.subsystem;
-
-import org.apache.sshd.common.file.SshFile;
-import org.apache.sshd.sftp.Handle;
-
-import java.io.IOException;
-
-public class BaseHandle implements Handle {
-
- private final String id;
- private final SshFile file;
-
- public BaseHandle(String id, SshFile file) {
- this.id = id;
- this.file = file;
- }
-
- public String getId() {
- return id;
- }
-
- public SshFile getFile() {
- return file;
- }
-
- public void close() throws IOException {
- file.handleClose();
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/DefaultSftpletContainer.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/DefaultSftpletContainer.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/DefaultSftpletContainer.java
deleted file mode 100644
index 56734ee..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/DefaultSftpletContainer.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
-* 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.sftp.subsystem;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.sshd.common.Session;
-import org.apache.sshd.server.session.ServerSession;
-import org.apache.sshd.sftp.Reply;
-import org.apache.sshd.sftp.Request;
-import org.apache.sshd.sftp.SftpSession;
-import org.apache.sshd.sftp.Sftplet;
-import org.apache.sshd.sftp.request.BaseRequest;
-
-
-/**
- * The default Sftplet implementation.
- * It's just calling other added Sftplet implementations.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class DefaultSftpletContainer implements Sftplet {
- private List<Sftplet> sftpLetList = new ArrayList<Sftplet>();
-
- /**
- * {@inheritDoc}
- */
- public void onConnect(final SftpSession session) {
- for (Sftplet sftpLet : sftpLetList) {
- sftpLet.onConnect(session);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public void onDisconnect(final SftpSession session) {
- for (Sftplet sftpLet : sftpLetList) {
- sftpLet.onDisconnect(session);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public Reply beforeCommand(final SftpSession session, final Request sftpRequest) {
- Reply reply = null;
- for (Sftplet sftpLet : sftpLetList) {
- reply = sftpLet.beforeCommand(session, sftpRequest);
- }
- return reply;
- }
-
- /**
- * {@inheritDoc}
- */
- public Reply afterCommand(final SftpSession session, final Request sftpRequest, final Reply sftpReply)
- throws IOException {
- Reply reply = sftpReply;
- for (Sftplet sftpLet : sftpLetList) {
- reply = sftpLet.afterCommand(session, sftpRequest, reply);
- }
- return reply;
- }
-
- /**
- * {@inheritDoc}
- */
- public void add(final Sftplet sftpLet) {
- this.sftpLetList.add(sftpLet);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/DirectoryHandle.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/DirectoryHandle.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/DirectoryHandle.java
deleted file mode 100644
index 06891cd..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/DirectoryHandle.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.sftp.subsystem;
-
-import org.apache.sshd.common.file.SshFile;
-
-import java.util.Iterator;
-import java.util.List;
-
-public class DirectoryHandle extends BaseHandle implements Iterator<SshFile> {
-
- boolean done;
- // the directory should be read once at "open directory"
- List<SshFile> fileList = null;
- int fileIndex;
-
- public DirectoryHandle(String id, SshFile file) {
- super(id, file);
- fileList = file.listSshFiles();
- fileIndex = 0;
- }
-
- public boolean isDone() {
- return done;
- }
-
- public void setDone(boolean done) {
- this.done = done;
- }
-
- public boolean hasNext() {
- return fileIndex < fileList.size();
- }
-
- public SshFile next() {
- SshFile f = fileList.get(fileIndex);
- fileIndex++;
- return f;
- }
-
- public void remove() {
- throw new UnsupportedOperationException();
- }
-
- public void clearFileList() {
- // allow the garbage collector to do the job
- fileList = null;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/FileHandle.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/FileHandle.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/FileHandle.java
deleted file mode 100644
index 5ebd14c..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/FileHandle.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.sftp.subsystem;
-
-import org.apache.sshd.common.util.IoUtils;
-import org.apache.sshd.common.file.SshFile;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class FileHandle extends BaseHandle {
-
- int flags;
- OutputStream output;
- long outputPos;
- InputStream input;
- long inputPos;
- long length;
-
- public FileHandle(String id, SshFile sshFile, int flags) {
- super(id, sshFile);
- this.flags = flags;
- }
-
- public int getFlags() {
- return flags;
- }
-
- public int read(byte[] data, long offset) throws IOException {
- if (input != null && offset >= length) {
- return -1;
- }
- if (input != null && offset != inputPos) {
- IoUtils.closeQuietly(input);
- input = null;
- }
- if (input == null) {
- input = getFile().createInputStream(offset);
- length = getFile().getSize();
- inputPos = offset;
- }
- if (offset >= length) {
- return -1;
- }
- int read = input.read(data);
- inputPos += read;
- return read;
- }
-
- public void write(byte[] data, long offset) throws IOException {
- if (output != null && offset != outputPos) {
- IoUtils.closeQuietly(output);
- output = null;
- }
- if (output == null) {
- output = getFile().createOutputStream(offset);
- }
- output.write(data);
- outputPos += data.length;
- }
-
- @Override
- public void close() throws IOException {
- IoUtils.closeQuietly(output, input);
- output = null;
- input = null;
- super.close();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/Serializer.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/Serializer.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/Serializer.java
deleted file mode 100644
index 2ad70b5..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/Serializer.java
+++ /dev/null
@@ -1,535 +0,0 @@
-/*
- * 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.sftp.subsystem;
-
-import org.apache.sshd.common.util.Buffer;
-import org.apache.sshd.common.file.SshFile;
-import org.apache.sshd.sftp.Handle;
-import org.apache.sshd.sftp.Reply;
-import org.apache.sshd.sftp.Request;
-import org.apache.sshd.sftp.SftpSession;
-import org.apache.sshd.sftp.reply.*;
-import org.apache.sshd.sftp.request.*;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import static org.apache.sshd.sftp.subsystem.SftpConstants.*;
-import static org.apache.sshd.sftp.subsystem.SftpConstants.SSH_FXP_STATUS;
-
-public class Serializer {
-
- private final SftpSession session;
-
- public Serializer(SftpSession session) {
- this.session = session;
- }
-
- protected Request readRequest(final Buffer buffer) {
- Request request;
-
- int length = buffer.getInt();
- byte type = buffer.getByte();
- int id = buffer.getInt();
-
- switch (type) {
- case SSH_FXP_INIT: {
- if (length != 5) {
- throw new IllegalArgumentException();
- }
- request = new SshFxpInitRequest(id);
- break;
- }
- case SSH_FXP_OPEN: {
- String path = buffer.getString();
- int acc = (session.getVersion() > 4) ? buffer.getInt() : 0;
- int flags = buffer.getInt();
- request = new SshFxpOpenRequest(id, path, acc, flags);
- break;
- }
- case SSH_FXP_CLOSE: {
- String handleId = buffer.getString();
- Handle handle = session.getHandle(handleId);
- request = new SshFxpCloseRequest(id, handleId, handle);
- break;
- }
- case SSH_FXP_READ: {
- String handleId = buffer.getString();
- long offset = buffer.getLong();
- int len = buffer.getInt();
- Handle handle = session.getHandle(handleId);
- request = new SshFxpReadRequest(id, handleId, offset, len, handle);
- break;
- }
- case SSH_FXP_WRITE: {
- String handleId = buffer.getString();
- long offset = buffer.getLong();
- byte[] data = buffer.getBytes();
- Handle handle = session.getHandle(handleId);
- request = new SshFxpWriteRequest(id, handleId, offset, data, handle);
- break;
- }
- case SSH_FXP_LSTAT: {
- String path = buffer.getString();
- int flags = 0;
- if (session.getVersion() > 5) {
- flags = buffer.getInt();
- }
- request = new SshFxpLstatRequest(id, path, flags);
- break;
- }
- case SSH_FXP_FSTAT: {
- String handle = buffer.getString();
- int flags = 0;
- if (session.getVersion() > 5) {
- flags = buffer.getInt();
- }
- Handle p = session.getHandle(handle);
- request = new SshFxpFstatRequest(id, handle, p);
- break;
- }
- case SSH_FXP_SETSTAT: {
- request = new SshFxpSetstatRequest(id);
- break;
- }
- case SSH_FXP_FSETSTAT: {
- request = new SshFxpFsetstatRequest(id);
- break;
- }
- case SSH_FXP_OPENDIR: {
- String path = buffer.getString();
- request = new SshFxpOpendirRequest(id, path);
- break;
- }
- case SSH_FXP_READDIR: {
- String handle = buffer.getString();
- Handle p = session.getHandle(handle);
- request = new SshFxpReaddirRequest(id, handle, p);
- break;
- }
- case SSH_FXP_REMOVE: {
- String path = buffer.getString();
- request = new SshFxpRemoveRequest(id, path);
- break;
- }
- case SSH_FXP_MKDIR: {
- String path = buffer.getString();
- request = new SshFxpMkdirRequest(id, path);
- break;
- }
- case SSH_FXP_RMDIR: {
- String path = buffer.getString();
- request = new SshFxpRmdirRequest(id, path);
- break;
- }
- case SSH_FXP_REALPATH: {
- String path = buffer.getString();
- byte options = SSH_FXP_REALPATH_NO_CHECK;
- List<String> compose = new ArrayList<String>();
- if (session.getVersion() >= 6 && buffer.available() > 0) {
- options = buffer.getByte();
- }
- while (session.getVersion() >= 6 && buffer.available() > 0) {
- compose.add(buffer.getString());
- }
- request = new SshFxpRealpathRequest(id, path, options, compose);
- break;
- }
- case SSH_FXP_STAT: {
- String path = buffer.getString();
- int flags = 0;
- if (session.getVersion() > 5) {
- flags = buffer.getInt();
- }
- request = new SshFxpStatRequest(id, path, flags);
- break;
- }
- case SSH_FXP_RENAME: {
- final String oldPath = buffer.getString();
- final String newPath = buffer.getString();
- request = new SshFxpRenameRequest(id, oldPath, newPath);
- break;
- }
- case SSH_FXP_READLINK: {
- // TODO: implement
- request = new UnsupportedRequest(id, Type.SSH_FXP_READLINK.toByte());
- break;
- }
- case SSH_FXP_LINK: {
- // TODO: implement
- request = new UnsupportedRequest(id, Type.SSH_FXP_LINK.toByte());
- break;
- }
- case SSH_FXP_BLOCK: {
- // TODO: implement
- request = new UnsupportedRequest(id, Type.SSH_FXP_BLOCK.toByte());
- break;
- }
- case SSH_FXP_UNBLOCK: {
- // TODO: implement
- request = new UnsupportedRequest(id, Type.SSH_FXP_UNBLOCK.toByte());
- break;
- }
- default: {
- request = new UnsupportedRequest(id, type);
- break;
- }
- }
- return request;
- }
-
- /*
- public Reply readReply(final Buffer buffer) {
- Reply reply;
-
- int length = buffer.getInt();
- byte type = buffer.getByte();
- int id = buffer.getInt();
-
- switch (type) {
- //
- // Replies
- //
- case SSH_FXP_VERSION: {
- // TODO
- break;
- }
- case SSH_FXP_STATUS: {
- // TODO
- break;
- }
- case SSH_FXP_HANDLE: {
- // TODO
- break;
- }
- case SSH_FXP_DATA: {
- // TODO
- break;
- }
- case SSH_FXP_NAME: {
- // TODO
- break;
- }
- case SSH_FXP_ATTRS: {
- // TODO
- break;
- }
- default:
- throw new IllegalStateException("Unsupported reply type: " + type);
- }
- return reply;
- }
-
- public Buffer writeRequest(final Request request) throws IOException {
- if (request == null) {
- throw new IllegalStateException("Can not serialize a null request");
- } else if (request instanceof ...) {
- // TODO
- } else {
- throw new IllegalStateException("Unsupported request: " + request.toString());
- }
- }
- */
-
- public Buffer writeReply(final Reply reply) throws IOException {
- if (reply == null) {
- throw new IllegalStateException("Can not serialize a null reply");
- } else if (reply instanceof SshFxpAttrsReply) {
- return writeAttrsReply((SshFxpAttrsReply) reply);
- } else if (reply instanceof SshFxpDataReply) {
- return writeDataReply((SshFxpDataReply) reply);
- } else if (reply instanceof SshFxpHandleReply) {
- return writeHandleReply((SshFxpHandleReply) reply);
- } else if (reply instanceof SshFxpNameReply) {
- return writeNameReply((SshFxpNameReply) reply);
- } else if (reply instanceof SshFxpStatusReply) {
- return writeStatus((SshFxpStatusReply) reply);
- } else if (reply instanceof SshFxpVersionReply) {
- return writeVersionReply((SshFxpVersionReply) reply);
- } else {
- throw new IllegalStateException("Unsupported reply: " + reply.toString());
- }
- }
-
- private Buffer writeAttrsReply(SshFxpAttrsReply reply) throws IOException {
- int id = reply.getId();
- FileAttributes attrs = reply.getAttributes();
-
- Buffer buffer = new Buffer();
- buffer.putByte((byte) SSH_FXP_ATTRS);
- buffer.putInt(id);
- writeAttrs(buffer, attrs);
- return buffer;
- }
-
- private Buffer writeVersionReply(SshFxpVersionReply reply) {
- int version = reply.getVersion();
-
- Buffer buffer = new Buffer();
- buffer.putByte((byte) SSH_FXP_VERSION);
- buffer.putInt(version);
- return buffer;
- }
-
- private Buffer writeNameReply(SshFxpNameReply reply) {
- int id = reply.getId();
- Collection<SshFxpNameReply.ReplyFile> files = reply.getFiles();
- Buffer buffer = new Buffer();
- buffer.putByte((byte) SSH_FXP_NAME);
- buffer.putInt(id);
- buffer.putInt(files.size());
- for (SshFxpNameReply.ReplyFile f : files) {
- buffer.putString(f.getFileName());
- if (session.getVersion() <= 3) {
- buffer.putString(f.getLongName()); // Format specified in the specs
- }
- writeAttrs(buffer, f.getAttrs());
- }
- if (session.getVersion() >= 6 && reply.isEol()) {
- buffer.putBoolean(true);
- }
- return buffer;
- }
-
- private Buffer writeHandleReply(SshFxpHandleReply reply) throws IOException {
- int id = reply.getId();
- String handle = reply.getHandle().getId();
-
- Buffer buffer = new Buffer();
- buffer.putByte((byte) SSH_FXP_HANDLE);
- buffer.putInt(id);
- buffer.putString(handle);
- return buffer;
- }
-
- private Buffer writeDataReply(SshFxpDataReply reply) {
- long id = reply.getId();
- byte[] data = reply.getData();
- int offset = reply.getOffset();
- int length = reply.getLength();
- boolean eof = reply.isEof();
-
- Buffer buffer = new Buffer(length + 5);
- buffer.putByte((byte) SSH_FXP_DATA);
- buffer.putInt(id);
- buffer.putBytes(data, offset, length);
- if (session.getVersion() >= 6 && eof) {
- buffer.putBoolean(eof);
- }
- return buffer;
- }
-
- private Buffer writeStatus(SshFxpStatusReply reply) throws IOException {
- int id = reply.getId();
- int substatus = reply.getSubstatus();
- String msg = reply.getMsg();
- String lang = reply.getLang();
-
- Buffer buffer = new Buffer();
- buffer.putByte((byte) SSH_FXP_STATUS);
- buffer.putInt(id);
- buffer.putInt(mapToVersion(substatus));
- buffer.putString(msg);
- buffer.putString(lang);
- return buffer;
- }
-
- protected static int mapV4ToV3(int code) {
- switch (code) {
- case SSH_FX_INVALID_HANDLE:
- return SSH_FX_FAILURE;
- case SSH_FX_NO_SUCH_PATH:
- return SSH_FX_NO_SUCH_FILE;
- case SSH_FX_FILE_ALREADY_EXISTS:
- return SSH_FX_FAILURE;
- case SSH_FX_WRITE_PROTECT:
- return SSH_FX_PERMISSION_DENIED;
- case SSH_FX_NO_MEDIA:
- return SSH_FX_FAILURE;
- default:
- return code;
- }
- }
-
- protected static int mapV5ToV4(int code) {
- switch (code) {
- case SSH_FX_NO_SPACE_ON_FILESYSTEM:
- return SSH_FX_FAILURE;
- case SSH_FX_QUOTA_EXCEEDED:
- return SSH_FX_FAILURE;
- case SSH_FX_UNKNOWN_PRINCIPAL:
- return SSH_FX_FAILURE;
- case SSH_FX_LOCK_CONFLICT:
- return SSH_FX_FAILURE;
- default:
- return code;
- }
- }
-
- protected static int mapV6ToV5(int code) {
- switch (code) {
- case SSH_FX_DIR_NOT_EMPTY:
- return SSH_FX_FAILURE;
- case SSH_FX_NOT_A_DIRECTORY:
- return SSH_FX_NO_SUCH_FILE;
- case SSH_FX_INVALID_FILENAME:
- return SSH_FX_NO_SUCH_FILE;
- case SSH_FX_LINK_LOOP:
- return SSH_FX_FAILURE;
- case SSH_FX_CANNOT_DELETE:
- return SSH_FX_PERMISSION_DENIED;
- case SSH_FX_INVALID_PARAMETER:
- return SSH_FX_FAILURE;
- case SSH_FX_FILE_IS_A_DIRECTORY:
- return SSH_FX_NO_SUCH_FILE;
- case SSH_FX_BYTE_RANGE_LOCK_CONFLICT:
- return SSH_FX_FAILURE;
- case SSH_FX_BYTE_RANGE_LOCK_REFUSED:
- return SSH_FX_FAILURE;
- case SSH_FX_DELETE_PENDING:
- return SSH_FX_FAILURE;
- case SSH_FX_FILE_CORRUPT:
- return SSH_FX_FAILURE;
- case SSH_FX_OWNER_INVALID:
- return SSH_FX_PERMISSION_DENIED;
- case SSH_FX_GROUP_INVALID:
- return SSH_FX_PERMISSION_DENIED;
- case SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK:
- return SSH_FX_FAILURE;
- default:
- return code;
- }
- }
-
- protected static int mapToVersion(int code, int version) {
- int mappedCode = code;
- if (version < 6) {
- mappedCode = mapV6ToV5(mappedCode);
- }
- if (version < 5) {
- mappedCode = mapV5ToV4(mappedCode);
- }
- if (version < 4) {
- mappedCode = mapV4ToV3(mappedCode);
- }
- return mappedCode;
- }
-
- protected int mapToVersion(int code) {
- return mapToVersion(code, session.getVersion());
- }
-
- protected void writeAttrs(Buffer buffer, FileAttributes attrs) {
- int version = session.getVersion();
- int flags = attrs.getFlags();
- buffer.putInt(flags);
- if (session.getVersion() >= 4) {
- buffer.putByte(attrs.getType());
- }
- if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
- buffer.putLong(attrs.getSize());
- }
- if (version >= 6 && (flags & SSH_FILEXFER_ATTR_ALLOCATION_SIZE) != 0) {
- buffer.putLong(attrs.getAllocationSize());
- }
- if ((flags & SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
- buffer.putString(attrs.getOwner());
- buffer.putString(attrs.getGroup());
- }
- if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
- int perms = attrs.getPermissions();
- if (session.getVersion() < 4) {
- if (attrs.getType() == SSH_FILEXFER_TYPE_REGULAR) {
- perms |= 0100000;
- } else if (attrs.getType() == SSH_FILEXFER_TYPE_DIRECTORY) {
- perms |= 0040000;
- }
- }
- buffer.putInt(perms);
- }
- if (version <= 3 && (flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
- buffer.putInt(attrs.getAccessTime());
- buffer.putInt(attrs.getAccessTime());
- }
- }
-
- protected void writeAttrs(Buffer buffer, SshFile file, int flags) {
- if (session.getVersion() >= 4) {
- long size = file.getSize();
- String username = session.getSession().getUsername();
- long lastModif = file.getLastModified();
- int p = 0;
- if (file.isReadable()) {
- p |= S_IRUSR;
- }
- if (file.isWritable()) {
- p |= S_IWUSR;
- }
- if (file.isExecutable()) {
- p |= S_IXUSR;
- }
- if (file.isFile()) {
- buffer.putInt(SSH_FILEXFER_ATTR_PERMISSIONS);
- buffer.putByte((byte) SSH_FILEXFER_TYPE_REGULAR);
- buffer.putInt(p);
- } else if (file.isDirectory()) {
- buffer.putInt(SSH_FILEXFER_ATTR_PERMISSIONS);
- buffer.putByte((byte) SSH_FILEXFER_TYPE_DIRECTORY);
- buffer.putInt(p);
- } else {
- buffer.putInt(0);
- buffer.putByte((byte) SSH_FILEXFER_TYPE_UNKNOWN);
- }
- } else {
- int p = 0;
- if (file.isFile()) {
- p |= 0100000;
- }
- if (file.isDirectory()) {
- p |= 0040000;
- }
- if (file.isReadable()) {
- p |= 0000400;
- }
- if (file.isWritable()) {
- p |= 0000200;
- }
- if (file.isExecutable()) {
- p |= 0000100;
- }
- if (file.isFile()) {
- buffer.putInt(SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
- buffer.putLong(file.getSize());
- buffer.putInt(p);
- buffer.putInt(file.getLastModified() / 1000);
- buffer.putInt(file.getLastModified() / 1000);
- } else if (file.isDirectory()) {
- buffer.putInt(SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
- buffer.putInt(p);
- buffer.putInt(file.getLastModified() / 1000);
- buffer.putInt(file.getLastModified() / 1000);
- } else {
- buffer.putInt(0);
- }
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/SftpConstants.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/SftpConstants.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/SftpConstants.java
deleted file mode 100644
index 0b4ae4a..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/SftpConstants.java
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * 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.sftp.subsystem;
-
-public final class SftpConstants {
-
- private SftpConstants() { }
-
- //
- // Packet types
- //
-
- public enum Type {
-
- SSH_FXP_INIT(1),
- SSH_FXP_VERSION(2),
- SSH_FXP_OPEN(3),
- SSH_FXP_CLOSE(4),
- SSH_FXP_READ(5),
- SSH_FXP_WRITE(6),
- SSH_FXP_LSTAT(7),
- SSH_FXP_FSTAT(8),
- SSH_FXP_SETSTAT(9),
- SSH_FXP_FSETSTAT(10),
- SSH_FXP_OPENDIR(11),
- SSH_FXP_READDIR(12),
- SSH_FXP_REMOVE(13),
- SSH_FXP_MKDIR(14),
- SSH_FXP_RMDIR(15),
- SSH_FXP_REALPATH(16),
- SSH_FXP_STAT(17),
- SSH_FXP_RENAME(18),
- SSH_FXP_READLINK(19),
- SSH_FXP_LINK(21),
- SSH_FXP_BLOCK(22),
- SSH_FXP_UNBLOCK(23),
-
- SSH_FXP_STATUS(101),
- SSH_FXP_HANDLE(102),
- SSH_FXP_DATA(103),
- SSH_FXP_NAME(104),
- SSH_FXP_ATTRS(105),
-
- SSH_FXP_EXTENDED(200),
- SSH_FXP_EXTENDED_REPLY(201);
-
- private byte b;
- private Type(int b) {
- this.b = (byte) b;
- }
-
- public byte toByte() {
- return b;
- }
-
- /*
- static Type[] commands;
- static {
- commands = new Type[256];
- for (Type c : Type.values()) {
- if (commands[c.toByte()] == null) {
- commands[c.toByte()] = c;
- }
- }
- }
- public static Type fromByte(byte b) {
- return commands[b];
- }
- */
- }
- public static final int SSH_FXP_INIT = 1;
- public static final int SSH_FXP_VERSION = 2;
- public static final int SSH_FXP_OPEN = 3;
- public static final int SSH_FXP_CLOSE = 4;
- public static final int SSH_FXP_READ = 5;
- public static final int SSH_FXP_WRITE = 6;
- public static final int SSH_FXP_LSTAT = 7;
- public static final int SSH_FXP_FSTAT = 8;
- public static final int SSH_FXP_SETSTAT = 9;
- public static final int SSH_FXP_FSETSTAT = 10;
- public static final int SSH_FXP_OPENDIR = 11;
- public static final int SSH_FXP_READDIR = 12;
- public static final int SSH_FXP_REMOVE = 13;
- public static final int SSH_FXP_MKDIR = 14;
- public static final int SSH_FXP_RMDIR = 15;
- public static final int SSH_FXP_REALPATH = 16;
- public static final int SSH_FXP_STAT = 17;
- public static final int SSH_FXP_RENAME = 18;
- public static final int SSH_FXP_READLINK = 19;
- public static final int SSH_FXP_LINK = 21;
- public static final int SSH_FXP_BLOCK = 22;
- public static final int SSH_FXP_UNBLOCK = 23;
-
- public static final int SSH_FXP_STATUS = 101;
- public static final int SSH_FXP_HANDLE = 102;
- public static final int SSH_FXP_DATA = 103;
- public static final int SSH_FXP_NAME = 104;
- public static final int SSH_FXP_ATTRS = 105;
-
- public static final int SSH_FXP_EXTENDED = 200;
- public static final int SSH_FXP_EXTENDED_REPLY = 201;
-
-
- //
- // Error codes
- //
- public enum ErrorCode {
-
- SSH_FX_OK(0),
- SSH_FX_EOF(1),
- SSH_FX_NO_SUCH_FILE(2),
- SSH_FX_PERMISSION_DENIED(3),
- SSH_FX_FAILURE(4),
- SSH_FX_BAD_MESSAGE(5),
- SSH_FX_NO_CONNECTION(6),
- SSH_FX_CONNECTION_LOST(7),
- SSH_FX_OP_UNSUPPORTED(8),
- SSH_FX_INVALID_HANDLE(9),
- SSH_FX_NO_SUCH_PATH(10),
- SSH_FX_FILE_ALREADY_EXISTS(11),
- SSH_FX_WRITE_PROTECT(12),
- SSH_FX_NO_MEDIA(13),
- SSH_FX_NO_SPACE_ON_FILESYSTEM(14),
- SSH_FX_QUOTA_EXCEEDED(15),
- SSH_FX_UNKNOWN_PRINCIPAL(16),
- SSH_FX_LOCK_CONFLICT(17),
- SSH_FX_DIR_NOT_EMPTY(18),
- SSH_FX_NOT_A_DIRECTORY(19),
- SSH_FX_INVALID_FILENAME(20),
- SSH_FX_LINK_LOOP(21),
- SSH_FX_CANNOT_DELETE(22),
- SSH_FX_INVALID_PARAMETER(23),
- SSH_FX_FILE_IS_A_DIRECTORY(24),
- SSH_FX_BYTE_RANGE_LOCK_CONFLICT(25),
- SSH_FX_BYTE_RANGE_LOCK_REFUSED(26),
- SSH_FX_DELETE_PENDING(27),
- SSH_FX_FILE_CORRUPT(28),
- SSH_FX_OWNER_INVALID(29),
- SSH_FX_GROUP_INVALID(30),
- SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK(31);
-
- private byte b;
- private ErrorCode(int b) {
- this.b = (byte) b;
- }
-
- public byte toByte() {
- return b;
- }
-
- static ErrorCode[] codes;
- static {
- codes = new ErrorCode[256];
- for (ErrorCode c : ErrorCode.values()) {
- if (codes[c.toByte()] == null) {
- codes[c.toByte()] = c;
- }
- }
- }
- public static ErrorCode fromByte(byte b) {
- return codes[b];
- }
- }
-
- public static final int SSH_FX_OK = 0;
- public static final int SSH_FX_EOF = 1;
- public static final int SSH_FX_NO_SUCH_FILE = 2;
- public static final int SSH_FX_PERMISSION_DENIED = 3;
- public static final int SSH_FX_FAILURE = 4;
- public static final int SSH_FX_BAD_MESSAGE = 5;
- public static final int SSH_FX_NO_CONNECTION = 6;
- public static final int SSH_FX_CONNECTION_LOST = 7;
- public static final int SSH_FX_OP_UNSUPPORTED = 8;
- public static final int SSH_FX_INVALID_HANDLE = 9;
- public static final int SSH_FX_NO_SUCH_PATH = 10;
- public static final int SSH_FX_FILE_ALREADY_EXISTS = 11;
- public static final int SSH_FX_WRITE_PROTECT = 12;
- public static final int SSH_FX_NO_MEDIA = 13;
- public static final int SSH_FX_NO_SPACE_ON_FILESYSTEM = 14;
- public static final int SSH_FX_QUOTA_EXCEEDED = 15;
- public static final int SSH_FX_UNKNOWN_PRINCIPAL = 16;
- public static final int SSH_FX_LOCK_CONFLICT = 17;
- public static final int SSH_FX_DIR_NOT_EMPTY = 18;
- public static final int SSH_FX_NOT_A_DIRECTORY = 19;
- public static final int SSH_FX_INVALID_FILENAME = 20;
- public static final int SSH_FX_LINK_LOOP = 21;
- public static final int SSH_FX_CANNOT_DELETE = 22;
- public static final int SSH_FX_INVALID_PARAMETER = 23;
- public static final int SSH_FX_FILE_IS_A_DIRECTORY = 24;
- public static final int SSH_FX_BYTE_RANGE_LOCK_CONFLICT = 25;
- public static final int SSH_FX_BYTE_RANGE_LOCK_REFUSED = 26;
- public static final int SSH_FX_DELETE_PENDING = 27;
- public static final int SSH_FX_FILE_CORRUPT = 28;
- public static final int SSH_FX_OWNER_INVALID = 29;
- public static final int SSH_FX_GROUP_INVALID = 30;
- public static final int SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31;
-
- public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
- public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
- public static final int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008; //v3 naming convention
- public static final int SSH_FILEXFER_ATTR_ACCESSTIME = 0x00000008;
- public static final int SSH_FILEXFER_ATTR_CREATETIME = 0x00000010;
- public static final int SSH_FILEXFER_ATTR_MODIFYTIME = 0x00000020;
- public static final int SSH_FILEXFER_ATTR_ACL = 0x00000040;
- public static final int SSH_FILEXFER_ATTR_OWNERGROUP = 0x00000080;
- public static final int SSH_FILEXFER_ATTR_SUBSECOND_TIMES = 0x00000100;
- public static final int SSH_FILEXFER_ATTR_BITS = 0x00000200;
- public static final int SSH_FILEXFER_ATTR_ALLOCATION_SIZE = 0x00000400;
- public static final int SSH_FILEXFER_ATTR_TEXT_HINT = 0x00000800;
- public static final int SSH_FILEXFER_ATTR_MIME_TYPE = 0x00001000;
- public static final int SSH_FILEXFER_ATTR_LINK_COUNT = 0x00002000;
- public static final int SSH_FILEXFER_ATTR_UNTRANSLATED_NAME = 0x00004000;
- public static final int SSH_FILEXFER_ATTR_CTIME = 0x00008000;
- public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
-
- public static final int SSH_FILEXFER_TYPE_REGULAR = 1;
- public static final int SSH_FILEXFER_TYPE_DIRECTORY = 2;
- public static final int SSH_FILEXFER_TYPE_SYMLINK = 3;
- public static final int SSH_FILEXFER_TYPE_SPECIAL = 4;
- public static final int SSH_FILEXFER_TYPE_UNKNOWN = 5;
- public static final int SSH_FILEXFER_TYPE_SOCKET = 6;
- public static final int SSH_FILEXFER_TYPE_CHAR_DEVICE = 7;
- public static final int SSH_FILEXFER_TYPE_BLOCK_DEVICE = 8;
- public static final int SSH_FILEXFER_TYPE_FIFO = 9;
-
-
- public static final int SSH_FXF_ACCESS_DISPOSITION = 0x00000007;
- public static final int SSH_FXF_CREATE_NEW = 0x00000000;
- public static final int SSH_FXF_CREATE_TRUNCATE = 0x00000001;
- public static final int SSH_FXF_OPEN_EXISTING = 0x00000002;
- public static final int SSH_FXF_OPEN_OR_CREATE = 0x00000003;
- public static final int SSH_FXF_TRUNCATE_EXISTING = 0x00000004;
- public static final int SSH_FXF_APPEND_DATA = 0x00000008;
- public static final int SSH_FXF_APPEND_DATA_ATOMIC = 0x00000010;
- public static final int SSH_FXF_TEXT_MODE = 0x00000020;
- public static final int SSH_FXF_BLOCK_READ = 0x00000040;
- public static final int SSH_FXF_BLOCK_WRITE = 0x00000080;
- public static final int SSH_FXF_BLOCK_DELETE = 0x00000100;
- public static final int SSH_FXF_BLOCK_ADVISORY = 0x00000200;
- public static final int SSH_FXF_NOFOLLOW = 0x00000400;
- public static final int SSH_FXF_DELETE_ON_CLOSE = 0x00000800;
- public static final int SSH_FXF_ACCESS_AUDIT_ALARM_INFO = 0x00001000;
- public static final int SSH_FXF_ACCESS_BACKUP = 0x00002000;
- public static final int SSH_FXF_BACKUP_STREAM = 0x00004000;
- public static final int SSH_FXF_OVERRIDE_OWNER = 0x00008000;
-
- public static final int SSH_FXF_READ = 0x00000001;
- public static final int SSH_FXF_WRITE = 0x00000002;
- public static final int SSH_FXF_APPEND = 0x00000004;
- public static final int SSH_FXF_CREAT = 0x00000008;
- public static final int SSH_FXF_TRUNC = 0x00000010;
- public static final int SSH_FXF_EXCL = 0x00000020;
- public static final int SSH_FXF_TEXT = 0x00000040;
-
- public static final int SSH_FXP_REALPATH_NO_CHECK = 0x00000001;
- public static final int SSH_FXP_REALPATH_STAT_IF = 0x00000002;
- public static final int SSH_FXP_REALPATH_STAT_ALWAYS = 0x00000003;
-
- public static final int ACE4_READ_DATA = 0x00000001;
- public static final int ACE4_LIST_DIRECTORY = 0x00000001;
- public static final int ACE4_WRITE_DATA = 0x00000002;
- public static final int ACE4_ADD_FILE = 0x00000002;
- public static final int ACE4_APPEND_DATA = 0x00000004;
- public static final int ACE4_ADD_SUBDIRECTORY = 0x00000004;
- public static final int ACE4_READ_NAMED_ATTRS = 0x00000008;
- public static final int ACE4_WRITE_NAMED_ATTRS = 0x00000010;
- public static final int ACE4_EXECUTE = 0x00000020;
- public static final int ACE4_DELETE_CHILD = 0x00000040;
- public static final int ACE4_READ_ATTRIBUTES = 0x00000080;
- public static final int ACE4_WRITE_ATTRIBUTES = 0x00000100;
- public static final int ACE4_DELETE = 0x00010000;
- public static final int ACE4_READ_ACL = 0x00020000;
- public static final int ACE4_WRITE_ACL = 0x00040000;
- public static final int ACE4_WRITE_OWNER = 0x00080000;
-
- public static final int S_IRUSR = 0000400;
- public static final int S_IWUSR = 0000200;
- public static final int S_IXUSR = 0000100;
- public static final int S_IRGRP = 0000040;
- public static final int S_IWGRP = 0000020;
- public static final int S_IXGRP = 0000010;
- public static final int S_IROTH = 0000004;
- public static final int S_IWOTH = 0000002;
- public static final int S_IXOTH = 0000001;
- public static final int S_ISUID = 0004000;
- public static final int S_ISGID = 0002000;
- public static final int S_ISVTX = 0001000;
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/80d8a51a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/SftpSubsystem.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/SftpSubsystem.java
deleted file mode 100644
index 31e3f74..0000000
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/SftpSubsystem.java
+++ /dev/null
@@ -1,786 +0,0 @@
-/*
- * 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.sftp.subsystem;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.Session;
-import org.apache.sshd.common.file.FileSystemAware;
-import org.apache.sshd.common.file.FileSystemView;
-import org.apache.sshd.common.file.SshFile;
-import org.apache.sshd.common.util.Buffer;
-import org.apache.sshd.common.util.SelectorUtils;
-import org.apache.sshd.common.util.ThreadUtils;
-import org.apache.sshd.server.*;
-import org.apache.sshd.server.channel.ChannelDataReceiver;
-import org.apache.sshd.server.channel.ChannelSession;
-import org.apache.sshd.server.session.ServerSession;
-import org.apache.sshd.sftp.*;
-import org.apache.sshd.sftp.reply.*;
-import org.apache.sshd.sftp.request.*;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.*;
-import java.util.*;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import static org.apache.sshd.sftp.subsystem.SftpConstants.*;
-
-/**
- * SFTP subsystem
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SftpSubsystem implements Command, SessionAware, FileSystemAware, SftpSession, ChannelDataReceiver, ChannelSessionAware {
-
- protected static final Logger LOG = LoggerFactory.getLogger(SftpSubsystem.class);
-
- public static class Factory implements NamedFactory<Command> {
-
- public Factory() {
- }
-
- public Command create() {
- return new SftpSubsystem();
- }
-
- public String getName() {
- return "sftp";
- }
- }
-
- public static final int LOWER_SFTP_IMPL = 3; // Working implementation from v3
- public static final int HIGHER_SFTP_IMPL = 6; // .. up to
- public static final String ALL_SFTP_IMPL = "3,4,5,6";
- public static final int MAX_PACKET_LENGTH = 1024 * 16;
-
- /**
- * Properties key for the maximum of available open handles per session.
- */
- public static final String MAX_OPEN_HANDLES_PER_SESSION = "max-open-handles-per-session";
-
-
- private ExitCallback callback;
- private InputStream in;
- private OutputStream out;
- private OutputStream err;
- private Environment env;
- private ServerSession session;
- private ChannelSession channel;
- private boolean closed = false;
-
- private FileSystemView root;
-
- private int version;
- private Map<String, Handle> handles = new HashMap<String, Handle>();
-
- private Sftplet sftpLet = new DefaultSftpletContainer();
- private Serializer serializer = new Serializer(this);
-
- private final ExecutorService executor;
-
- public SftpSubsystem() {
- executor = ThreadUtils.newSingleThreadExecutor("sftp[" + Integer.toHexString(hashCode()) + "]");
- }
-
- public void setSftpLet(final Sftplet sftpLet) {
- this.sftpLet = sftpLet;
- }
-
- public int getVersion() {
- return version;
- }
-
- public Session getSession() {
- return session;
- }
-
- public Handle getHandle(String id) {
- return handles.get(id);
- }
-
- public Handle createFileHandle(SshFile file, int flags) {
- String id = UUID.randomUUID().toString();
- Handle handle = new FileHandle(id, file, flags);
- handles.put(id, handle);
- return handle;
- }
-
- public Handle createDirectoryHandle(SshFile file) {
- String id = UUID.randomUUID().toString();
- Handle handle = new DirectoryHandle(id, file);
- handles.put(id, handle);
- return handle;
- }
-
- public void setChannelSession(ChannelSession channel) {
- this.channel = channel;
- channel.setDataReceiver(this);
- }
-
- public void setSession(ServerSession session) {
- this.session = session;
- sftpLet.onConnect(this);
- }
-
- public void setFileSystemView(FileSystemView view) {
- this.root = view;
- }
-
- public void setExitCallback(ExitCallback callback) {
- this.callback = callback;
- }
-
- public void setInputStream(InputStream in) {
- this.in = in;
- }
-
- public void setOutputStream(OutputStream out) {
- this.out = out;
- }
-
- public void setErrorStream(OutputStream err) {
- this.err = err;
- }
-
- public void start(Environment env) throws IOException {
- this.env = env;
- }
-
- private Buffer buffer = new Buffer();
-
- public int data(ChannelSession channel, byte[] buf, int start, int len) throws IOException {
- Buffer incoming = new Buffer(buf, start, len);
- // If we already have partial data, we need to append it to the buffer and use it
- if (buffer.available() > 0) {
- buffer.putBuffer(incoming);
- incoming = buffer;
- }
- // Process commands
- int rpos = incoming.rpos();
- while (receive(incoming));
- int read = incoming.rpos() - rpos;
- // Compact and add remaining data
- buffer.compact();
- if (buffer != incoming && incoming.available() > 0) {
- buffer.putBuffer(incoming);
- }
- return read;
- }
-
- protected boolean receive(Buffer incoming) throws IOException {
- int rpos = incoming.rpos();
- int wpos = incoming.wpos();
- if (wpos - rpos > 4) {
- int length = incoming.getInt();
- if (length < 5) {
- throw new IOException("Illegal sftp packet length: " + length);
- }
- if (wpos - rpos >= length + 4) {
- incoming.rpos(rpos);
- incoming.wpos(rpos + 4 + length);
- process(incoming);
- incoming.rpos(rpos + 4 + length);
- incoming.wpos(wpos);
- return true;
- }
- }
- incoming.rpos(rpos);
- return false;
- }
-
- public void close() throws IOException {
- executor.shutdownNow();
- if (handles != null) {
- for (Map.Entry<String, Handle> entry : handles.entrySet()) {
- Handle handle = entry.getValue();
- try {
- handle.close();
- } catch (IOException ioe) {
- LOG.error("Could not close open handle: " + entry.getKey(), ioe);
- }
- }
- }
- callback.onExit(0);
- sftpLet.onDisconnect(this);
- }
-
- public void process(Buffer buffer) throws IOException {
- final Request request = serializer.readRequest(buffer);
- if (LOG.isDebugEnabled()) {
- LOG.debug("Received sftp request: " + request);
- }
- executor.execute(new Runnable() {
- public void run() {
- try {
- Reply reply = sftpLet.beforeCommand(SftpSubsystem.this, request);
- if (reply == null) {
- reply = doProcess(request);
- }
- reply = sftpLet.afterCommand(SftpSubsystem.this, request, reply);
- if (reply != null) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Sending sftp reply: " + reply);
- }
- Buffer buffer = serializer.writeReply(reply);
- send(buffer);
- }
- } catch (Throwable t) {
- // TODO do something
- t.printStackTrace();
- }
- }
- });
- }
-
- protected Reply doProcess(Request request) throws IOException {
- try {
- if (request instanceof SshFxpInitRequest) {
- return doProcessInit((SshFxpInitRequest) request);
- } else if (request instanceof SshFxpOpenRequest) {
- return doProcessOpen((SshFxpOpenRequest) request);
- } else if (request instanceof SshFxpCloseRequest) {
- return doProcessClose((SshFxpCloseRequest) request);
- } else if (request instanceof SshFxpReadRequest) {
- return doProcessRead((SshFxpReadRequest) request);
- } else if (request instanceof SshFxpWriteRequest) {
- return doProcessWrite((SshFxpWriteRequest) request);
- } else if ((request instanceof SshFxpLstatRequest)
- || (request instanceof SshFxpStatRequest)) {
- return doProcessStat(request);
- } else if (request instanceof SshFxpFstatRequest) {
- return doProcessFstat((SshFxpFstatRequest) request);
- } else if (request instanceof SshFxpOpendirRequest) {
- return doProcessOpendir((SshFxpOpendirRequest) request);
- } else if (request instanceof SshFxpReaddirRequest) {
- return doProcessReaddir((SshFxpReaddirRequest) request);
- } else if (request instanceof SshFxpRemoveRequest) {
- return doProcessRemove((SshFxpRemoveRequest) request);
- } else if (request instanceof SshFxpMkdirRequest) {
- return doProcessMkdir((SshFxpMkdirRequest) request);
- } else if (request instanceof SshFxpRmdirRequest) {
- return doProcessRmdir((SshFxpRmdirRequest) request);
- } else if (request instanceof SshFxpRealpathRequest) {
- return doProcessRealpath((SshFxpRealpathRequest) request);
- } else if (request instanceof SshFxpRenameRequest) {
- return doProcessRename((SshFxpRenameRequest) request);
- } else if ((request instanceof SshFxpSetstatRequest)
- || (request instanceof SshFxpFsetstatRequest)) {
- return doProcessSetstat(request);
-
- } else {
- LOG.error("Received: {}", request);
- int id = request.getId();
- return new SshFxpStatusReply(id, SSH_FX_OP_UNSUPPORTED, "Command " + request + " is unsupported or not implemented");
- }
- } catch (IOException e) {
- int id = request.getId();
- return new SshFxpStatusReply(id, SSH_FX_FAILURE, e.getMessage());
- }
- }
-
- private Reply doProcessSetstat(Request request) throws IOException {
- // This is required for WinSCP / Cyberduck to upload properly
- // Blindly reply "OK"
- // TODO implement it
- int id = request.getId();
- return new SshFxpStatusReply(id, SSH_FX_OK, "");
- }
-
- private Reply doProcessRename(SshFxpRenameRequest request) throws IOException {
- int id = request.getId();
- String oldPath = request.getOldPath();
- String newPath = request.getNewPath();
- SshFile o = resolveFile(oldPath);
- SshFile n = resolveFile(newPath);
- if (!o.doesExist()) {
- return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, o.getAbsolutePath());
- } else if (n.doesExist()) {
- return new SshFxpStatusReply(id, SSH_FX_FILE_ALREADY_EXISTS, n.getAbsolutePath());
- } else if (!o.move(n)) {
- return new SshFxpStatusReply(id, SSH_FX_FAILURE, "Failed to rename file");
- } else {
- return new SshFxpStatusReply(id, SSH_FX_OK, "");
- }
- }
-
- private Reply doProcessRealpath(SshFxpRealpathRequest request) throws IOException {
- int id = request.getId();
- String path = request.getPath();
- if (path.trim().length() == 0) {
- path = ".";
- }
- SshFile p = resolveFile(path);
- for (String s : request.getCompose()) {
- p = this.root.getFile(p, s);
- }
- String normalizedPath = SelectorUtils.normalizePath(p.getAbsolutePath(), "/");
- if (normalizedPath.length() == 0) {
- normalizedPath = "/";
- }
- p = resolveFile(normalizedPath);
- if (p.getName().length() == 0) {
- p = resolveFile(".");
- }
- boolean exists = (request.getOptions() != SSH_FXP_REALPATH_NO_CHECK) && p.doesExist();
- if (!exists && request.getOptions() == SSH_FXP_REALPATH_STAT_ALWAYS) {
- return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath());
- } else if (exists && (request.getOptions() == SSH_FXP_REALPATH_STAT_IF || request.getOptions() == SSH_FXP_REALPATH_STAT_ALWAYS)) {
- SshFxpNameReply reply = new SshFxpNameReply(id);
- int flags = SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_SIZE;
- reply.addFile(p, normalizedPath, getLongName(p), new FileAttributes(p, flags));
- return reply;
- } else {
- SshFxpNameReply reply = new SshFxpNameReply(id);
- reply.addFile(p, normalizedPath, getLongName(p), new FileAttributes());
- return reply;
- }
- }
-
- private Reply doProcessRmdir(SshFxpRmdirRequest request) throws IOException {
- int id = request.getId();
- String path = request.getPath();
- // attrs
- SshFile p = resolveFile(path);
- if (p.isDirectory()) {
- if (p.doesExist()) {
- if (p.listSshFiles().size() == 0) {
- if (p.delete()) {
- return new SshFxpStatusReply(id, SSH_FX_OK, "");
- } else {
- return new SshFxpStatusReply(id, SSH_FX_FAILURE, "Unable to delete directory " + path);
- }
- } else {
- return new SshFxpStatusReply(id, SSH_FX_DIR_NOT_EMPTY, path);
- }
- } else {
- return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_PATH, path);
- }
- } else {
- return new SshFxpStatusReply(id, SSH_FX_NOT_A_DIRECTORY, p.getAbsolutePath());
- }
- }
-
- private Reply doProcessMkdir(SshFxpMkdirRequest request) throws IOException {
- int id = request.getId();
- String path = request.getPath();
- // attrs
- SshFile p = resolveFile(path);
- if (p.doesExist()) {
- if (p.isDirectory()) {
- return new SshFxpStatusReply(id, SSH_FX_FILE_ALREADY_EXISTS, p.getAbsolutePath());
- } else {
- return new SshFxpStatusReply(id, SSH_FX_NOT_A_DIRECTORY, p.getAbsolutePath());
- }
- } else if (!p.isWritable()) {
- return new SshFxpStatusReply(id, SSH_FX_PERMISSION_DENIED, p.getAbsolutePath());
- } else if (!p.mkdir()) {
- return new SshFxpStatusReply(id, SSH_FX_FAILURE, "Error creating dir " + path);
- } else {
- return new SshFxpStatusReply(id, SSH_FX_OK, "");
- }
- }
-
- private Reply doProcessRemove(SshFxpRemoveRequest request) throws IOException {
- int id = request.getId();
- String path = request.getPath();
- SshFile p = resolveFile(path);
- if (!p.doesExist()) {
- return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath());
- } else if (p.isDirectory()) {
- return new SshFxpStatusReply(id, SSH_FX_FILE_IS_A_DIRECTORY, p.getAbsolutePath());
- } else if (!p.delete()) {
- return new SshFxpStatusReply(id, SSH_FX_FAILURE, "Failed to delete file");
- } else {
- return new SshFxpStatusReply(id, SSH_FX_OK, "");
- }
- }
-
- private Reply doProcessReaddir(SshFxpReaddirRequest request) throws IOException {
- int id = request.getId();
- String handle = request.getHandleId();
- Handle p = getHandle(handle);
- if (!(p instanceof DirectoryHandle)) {
- return new SshFxpStatusReply(id, SSH_FX_INVALID_HANDLE, handle);
- } else if (((DirectoryHandle) p).isDone()) {
- return new SshFxpStatusReply(id, SSH_FX_EOF, "", "");
- } else if (!p.getFile().doesExist()) {
- return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, p.getFile().getAbsolutePath());
- } else if (!p.getFile().isDirectory()) {
- return new SshFxpStatusReply(id, SSH_FX_NOT_A_DIRECTORY, p.getFile().getAbsolutePath());
- } else if (!p.getFile().isReadable()) {
- return new SshFxpStatusReply(id, SSH_FX_PERMISSION_DENIED, p.getFile().getAbsolutePath());
- } else {
- DirectoryHandle dh = (DirectoryHandle) p;
- if (dh.hasNext()) {
- // There is at least one file in the directory.
- // Send only a few files at a time to not create packets of a too
- // large size or have a timeout to occur.
- Reply reply = sendName(id, dh);
- if (!dh.hasNext()) {
- // if no more files to send
- dh.setDone(true);
- dh.clearFileList();
- }
- return reply;
- } else {
- // empty directory
- dh.setDone(true);
- dh.clearFileList();
- return new SshFxpStatusReply(id, SSH_FX_EOF, "", "");
- }
- }
- }
-
- private Reply doProcessOpendir(SshFxpOpendirRequest request) throws IOException {
- int id = request.getId();
- String path = request.getPath();
- SshFile p = resolveFile(path);
- if (!p.doesExist()) {
- return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, path);
- } else if (!p.isDirectory()) {
- return new SshFxpStatusReply(id, SSH_FX_NOT_A_DIRECTORY, path);
- } else if (!p.isReadable()) {
- return new SshFxpStatusReply(id, SSH_FX_PERMISSION_DENIED, path);
- } else {
- Handle handle = createDirectoryHandle(p);
- return new SshFxpHandleReply(id, handle);
- }
- }
-
- private Reply doProcessFstat(SshFxpFstatRequest request) throws IOException {
- int id = request.getId();
- String handle = request.getHandleId();
- Handle p = getHandle(handle);
- if (p == null) {
- return new SshFxpStatusReply(id, SSH_FX_INVALID_HANDLE, handle);
- } else {
- int flags = SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_SIZE;
- return new SshFxpAttrsReply(id, new FileAttributes(p.getFile(), flags));
- }
- }
-
- private Reply doProcessStat(Request sftpRequest) throws IOException {
- int id = sftpRequest.getId();
- String path;
- if (sftpRequest instanceof SshFxpLstatRequest) {
- SshFxpLstatRequest sshFxpLstatRequest = (SshFxpLstatRequest) sftpRequest;
- path = sshFxpLstatRequest.getPath();
- } else {
- SshFxpStatRequest sshFxpStatRequest = (SshFxpStatRequest) sftpRequest;
- path = sshFxpStatRequest.getPath();
- }
- SshFile p = resolveFile(path);
- if (!p.doesExist()) {
- return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath());
- }
- int flags = SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_SIZE;
- return new SshFxpAttrsReply(id, new FileAttributes(p, flags));
- }
-
- private Reply doProcessWrite(SshFxpWriteRequest request) throws IOException {
- int id = request.getId();
- String handle = request.getHandleId();
- long offset = request.getOffset();
- byte[] data = request.getData();
- Handle p = getHandle(handle);
- if (!(p instanceof FileHandle)) {
- return new SshFxpStatusReply(id, SSH_FX_INVALID_HANDLE, handle);
- } else {
- FileHandle fh = (FileHandle) p;
- fh.write(data, offset);
- SshFile sshFile = fh.getFile();
-
- sshFile.setLastModified(new Date().getTime());
-
- return new SshFxpStatusReply(id, SSH_FX_OK, "");
- }
- }
-
- private Reply doProcessRead(SshFxpReadRequest request) throws IOException {
- int id = request.getId();
- String handle = request.getHandleId();
- long offset = request.getOffset();
- int len = request.getLength();
- Handle p = getHandle(handle);
- if (!(p instanceof FileHandle)) {
- return new SshFxpStatusReply(id, SSH_FX_INVALID_HANDLE, handle);
- } else {
- FileHandle fh = (FileHandle) p;
- byte[] b = new byte[len];
- len = fh.read(b, offset);
- if (len >= 0) {
- return new SshFxpDataReply(id, b, 0, len, len < b.length);
- } else {
- return new SshFxpStatusReply(id, SSH_FX_EOF, "");
- }
- }
- }
-
- private Reply doProcessClose(SshFxpCloseRequest sftpRequest) throws IOException {
- int id = sftpRequest.getId();
- SshFxpCloseRequest sshFxpCloseRequest = sftpRequest;
- String handle = sshFxpCloseRequest.getHandleId();
- Handle h = getHandle(handle);
- if (h == null) {
- return new SshFxpStatusReply(id, SSH_FX_INVALID_HANDLE, handle, "");
- } else {
- handles.remove(handle);
- h.close();
- return new SshFxpStatusReply(id, SSH_FX_OK, "", "");
- }
- }
-
- private Reply doProcessOpen(SshFxpOpenRequest request) throws IOException {
- int id = request.getId();
- if (session.getFactoryManager().getProperties() != null) {
- String maxHandlesString = session.getFactoryManager().getProperties().get(MAX_OPEN_HANDLES_PER_SESSION);
- if (maxHandlesString != null) {
- int maxHandleCount = Integer.parseInt(maxHandlesString);
- if (handles.size() > maxHandleCount) {
- return new SshFxpStatusReply(id, SSH_FX_FAILURE, "Too many open handles");
- }
- }
- }
-
- int accValue = request.getAcc();
- if (accValue == 0) {
- String path = request.getPath();
- int flags = request.getFlags();
- // attrs
- SshFile file = resolveFile(path);
- if (file.doesExist()) {
- if (((flags & SSH_FXF_CREAT) != 0) && ((flags & SSH_FXF_EXCL) != 0)) {
- return new SshFxpStatusReply(id, SSH_FX_FILE_ALREADY_EXISTS, path);
- }
- } else {
- if ((flags & SSH_FXF_CREAT) != 0) {
- if (!file.isWritable()) {
- return new SshFxpStatusReply(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path);
- }
- file.create();
- }
- }
- if ((flags & SSH_FXF_TRUNC) != 0) {
- file.truncate();
- }
- return new SshFxpHandleReply(id, createFileHandle(file, flags));
- } else {
- String path = request.getPath();
- int acc = accValue;
- int flags = request.getFlags();
- // attrs
- SshFile file = resolveFile(path);
- switch (flags & SSH_FXF_ACCESS_DISPOSITION) {
- case SSH_FXF_CREATE_NEW: {
- if (file.doesExist()) {
- return new SshFxpStatusReply(id, SSH_FX_FILE_ALREADY_EXISTS, path);
- } else if (!file.isWritable()) {
- return new SshFxpStatusReply(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path);
- }
- file.create();
- break;
- }
- case SSH_FXF_CREATE_TRUNCATE: {
- if (file.doesExist()) {
- return new SshFxpStatusReply(id, SSH_FX_FILE_ALREADY_EXISTS, path);
- } else if (!file.isWritable()) {
- return new SshFxpStatusReply(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path);
- }
- file.truncate();
- break;
- }
- case SSH_FXF_OPEN_EXISTING: {
- if (!file.doesExist()) {
- if (!file.getParentFile().doesExist()) {
- return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_PATH, path);
- } else {
- return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, path);
- }
- }
- break;
- }
- case SSH_FXF_OPEN_OR_CREATE: {
- if (!file.doesExist()) {
- file.create();
- }
- break;
- }
- case SSH_FXF_TRUNCATE_EXISTING: {
- if (!file.doesExist()) {
- if (!file.getParentFile().doesExist()) {
- return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_PATH, path);
- } else {
- return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, path);
- }
- }
- file.truncate();
- break;
- }
- default:
- throw new IllegalArgumentException("Unsupported open mode: " + flags);
- }
- return new SshFxpHandleReply(id, createFileHandle(file, flags));
- }
- }
-
- private Reply doProcessInit(SshFxpInitRequest request) throws IOException {
- int id = request.getId();
- version = id;
- if (version >= LOWER_SFTP_IMPL) {
- version = Math.min(version, HIGHER_SFTP_IMPL);
- return new SshFxpVersionReply(version);
- } else {
- // We only support version >= 3 (Version 1 and 2 are not common)
- return new SshFxpStatusReply(id, SSH_FX_OP_UNSUPPORTED, "SFTP server only support versions " + ALL_SFTP_IMPL);
- }
- }
-
- protected SshFxpNameReply sendName(int id, Iterator<SshFile> files) throws IOException {
- SshFxpNameReply reply = new SshFxpNameReply(id);
- int nb = 0;
- while (files.hasNext() && nb < MAX_PACKET_LENGTH / 2) {
- SshFile f = files.next();
- String filename = f.getName();
- if (version <= 3) {
- nb += 55 + filename.length() * 2;
- } else {
- nb += filename.length();
- }
- nb += 10; // Attrs size
- int flags = SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_ACCESSTIME;
- reply.addFile(f, filename, getLongName(f), new FileAttributes(f, flags));
- }
- reply.setEol(!files.hasNext());
- return reply;
- }
-
- protected void send(Buffer buffer) throws IOException {
- DataOutputStream dos = new DataOutputStream(out);
- dos.writeInt(buffer.available());
- dos.write(buffer.array(), buffer.rpos(), buffer.available());
- dos.flush();
- }
-
- public void destroy() {
- closed = true;
- }
-
- private SshFile resolveFile(String path) {
- return this.root.getFile(path);
- }
-
-
- private String getLongName(SshFile f) {
- String username = f.getOwner();
- if (username.length() > 8) {
- username = username.substring(0, 8);
- } else {
- for (int i = username.length(); i < 8; i++) {
- username = username + " ";
- }
- }
-
- long length = f.getSize();
- String lengthString = String.format("%1$8s", length);
-
- StringBuilder sb = new StringBuilder();
- sb.append((f.isDirectory() ? "d" : "-"));
- sb.append((f.isReadable() ? "r" : "-"));
- sb.append((f.isWritable() ? "w" : "-"));
- sb.append((f.isExecutable() ? "x" : "-"));
- sb.append((f.isReadable() ? "r" : "-"));
- sb.append((f.isWritable() ? "w" : "-"));
- sb.append((f.isExecutable() ? "x" : "-"));
- sb.append((f.isReadable() ? "r" : "-"));
- sb.append((f.isWritable() ? "w" : "-"));
- sb.append((f.isExecutable() ? "x" : "-"));
- sb.append(" ");
- sb.append(" 1");
- sb.append(" ");
- sb.append(username);
- sb.append(" ");
- sb.append(username);
- sb.append(" ");
- sb.append(lengthString);
- sb.append(" ");
- sb.append(getUnixDate(f.getLastModified()));
- sb.append(" ");
- sb.append(f.getName());
-
- return sb.toString();
- }
-
- private final static String[] MONTHS = {"Jan", "Feb", "Mar", "Apr", "May",
- "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
-
- /**
- * Get unix style date string.
- */
- private final static String getUnixDate(long millis) {
- if (millis < 0) {
- return "------------";
- }
-
- StringBuffer sb = new StringBuffer(16);
- Calendar cal = new GregorianCalendar();
- cal.setTimeInMillis(millis);
-
- // month
- sb.append(MONTHS[cal.get(Calendar.MONTH)]);
- sb.append(' ');
-
- // day
- int day = cal.get(Calendar.DATE);
- if (day < 10) {
- sb.append(' ');
- }
- sb.append(day);
- sb.append(' ');
-
- long sixMonth = 15811200000L; // 183L * 24L * 60L * 60L * 1000L;
- long nowTime = System.currentTimeMillis();
- if (Math.abs(nowTime - millis) > sixMonth) {
-
- // year
- int year = cal.get(Calendar.YEAR);
- sb.append(' ');
- sb.append(year);
- } else {
-
- // hour
- int hh = cal.get(Calendar.HOUR_OF_DAY);
- if (hh < 10) {
- sb.append('0');
- }
- sb.append(hh);
- sb.append(':');
-
- // minute
- int mm = cal.get(Calendar.MINUTE);
- if (mm < 10) {
- sb.append('0');
- }
- sb.append(mm);
- }
- return sb.toString();
- }
-
-}
[10/15] mina-sshd git commit: [SSHD-408] Implement sftp v4, v5 and v6
Posted by gn...@apache.org.
[SSHD-408] Implement sftp v4, v5 and v6
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/22581fb8
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/22581fb8
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/22581fb8
Branch: refs/heads/master
Commit: 22581fb880efcbb1f7f86af68fd53a812df63c47
Parents: 345604b
Author: Guillaume Nodet <gn...@apache.org>
Authored: Wed Feb 11 00:06:45 2015 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Mon Feb 23 16:21:30 2015 +0100
----------------------------------------------------------------------
.../java/org/apache/sshd/client/SftpClient.java | 82 +-
.../sshd/client/sftp/DefaultSftpClient.java | 449 +++-
.../sshd/client/sftp/SftpFileChannel.java | 345 +++
.../apache/sshd/client/sftp/SftpFileSystem.java | 27 +-
.../client/sftp/SftpFileSystemProvider.java | 344 ++-
.../org/apache/sshd/common/scp/ScpHelper.java | 18 +-
.../org/apache/sshd/common/util/Buffer.java | 24 +-
.../org/apache/sshd/common/util/IoUtils.java | 17 +
.../apache/sshd/server/sftp/SftpSubsystem.java | 2105 ++++++++++++------
.../org/apache/sshd/SftpFileSystemTest.java | 68 +-
.../src/test/java/org/apache/sshd/SftpTest.java | 51 +-
11 files changed, 2644 insertions(+), 886 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/22581fb8/sshd-core/src/main/java/org/apache/sshd/client/SftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SftpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SftpClient.java
index 18a9ba4..5c5aeb5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/SftpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SftpClient.java
@@ -22,7 +22,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
+import java.nio.file.attribute.FileTime;
import java.util.EnumSet;
+import java.util.concurrent.TimeUnit;
/**
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
@@ -62,11 +64,20 @@ public interface SftpClient extends AutoCloseable {
Exclusive
}
+ enum CopyMode {
+ Atomic,
+ Overwrite
+ }
+
enum Attribute {
Size,
UidGid,
Perms,
- AcModTime
+ AcModTime,
+ OwnerGroup,
+ AccessTime,
+ ModifyTime,
+ CreateTime,
}
public static class Handle {
@@ -79,16 +90,39 @@ public interface SftpClient extends AutoCloseable {
public static class Attributes {
public EnumSet<Attribute> flags = EnumSet.noneOf(Attribute.class);
public long size;
+ public byte type;
public int uid;
public int gid;
public int perms;
public int atime;
+ public int ctime;
public int mtime;
+ public String owner;
+ public String group;
+ public FileTime accessTime;
+ public FileTime createTime;
+ public FileTime modifyTime;
public Attributes size(long size) {
flags.add(Attribute.Size);
this.size = size;
return this;
}
+ public Attributes owner(String owner) {
+ flags.add(Attribute.OwnerGroup);
+ this.owner = owner;
+ if (group == null) {
+ group = "GROUP@";
+ }
+ return this;
+ }
+ public Attributes group(String group) {
+ flags.add(Attribute.OwnerGroup);
+ this.group = group;
+ if (owner == null) {
+ owner = "OWNER@";
+ }
+ return this;
+ }
public Attributes owner(int uid, int gid) {
flags.add(Attribute.UidGid);
this.uid = uid;
@@ -100,12 +134,48 @@ public interface SftpClient extends AutoCloseable {
this.perms = perms;
return this;
}
+ public Attributes atime(int atime) {
+ flags.add(Attribute.AccessTime);
+ this.atime = atime;
+ this.accessTime = FileTime.from(atime, TimeUnit.SECONDS);
+ return this;
+ }
+ public Attributes ctime(int ctime) {
+ flags.add(Attribute.CreateTime);
+ this.ctime = ctime;
+ this.createTime = FileTime.from(atime, TimeUnit.SECONDS);
+ return this;
+ }
+ public Attributes mtime(int mtime) {
+ flags.add(Attribute.ModifyTime);
+ this.mtime = mtime;
+ this.modifyTime = FileTime.from(atime, TimeUnit.SECONDS);
+ return this;
+ }
public Attributes time(int atime, int mtime) {
flags.add(Attribute.AcModTime);
this.atime = atime;
this.mtime = mtime;
return this;
}
+ public Attributes accessTime(FileTime atime) {
+ flags.add(Attribute.AccessTime);
+ this.atime = (int) atime.to(TimeUnit.SECONDS);
+ this.accessTime = atime;
+ return this;
+ }
+ public Attributes createTime(FileTime ctime) {
+ flags.add(Attribute.CreateTime);
+ this.ctime = (int) ctime.to(TimeUnit.SECONDS);
+ this.createTime = ctime;
+ return this;
+ }
+ public Attributes modifyTime(FileTime mtime) {
+ flags.add(Attribute.ModifyTime);
+ this.mtime = (int) mtime.to(TimeUnit.SECONDS);
+ this.modifyTime = mtime;
+ return this;
+ }
public boolean isRegularFile() {
return (perms & S_IFMT) == S_IFREG;
}
@@ -131,6 +201,8 @@ public interface SftpClient extends AutoCloseable {
}
}
+ int getVersion();
+
/**
* Close the client.
*/
@@ -150,6 +222,8 @@ public interface SftpClient extends AutoCloseable {
void rename(String oldPath, String newPath) throws IOException;
+ void rename(String oldPath, String newPath, CopyMode... options) throws IOException;
+
int read(Handle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException;
void write(Handle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException;
@@ -178,6 +252,12 @@ public interface SftpClient extends AutoCloseable {
void symLink(String linkPath, String targetPath) throws IOException;
+ void link(String linkPath, String targetPath, boolean symbolic) throws IOException;
+
+ void lock(Handle handle, long offset, long length, int mask) throws IOException;
+
+ void unlock(Handle handle, long offset, long length) throws IOException;
+
//
// High level API
//
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/22581fb8/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java
index 526d92d..8ec2bb9 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java
@@ -25,10 +25,13 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.attribute.FileTime;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.sshd.ClientSession;
@@ -63,6 +66,9 @@ public class DefaultSftpClient implements SftpClient {
public static final int SSH_FXP_RENAME = 18;
public static final int SSH_FXP_READLINK = 19;
public static final int SSH_FXP_SYMLINK = 20;
+ public static final int SSH_FXP_LINK = 21; // v6
+ public static final int SSH_FXP_BLOCK = 22; // v6
+ public static final int SSH_FXP_UNBLOCK = 23; // v6
public static final int SSH_FXP_STATUS = 101;
public static final int SSH_FXP_HANDLE = 102;
public static final int SSH_FXP_DATA = 103;
@@ -71,21 +77,29 @@ public class DefaultSftpClient implements SftpClient {
public static final int SSH_FXP_EXTENDED = 200;
public static final int SSH_FXP_EXTENDED_REPLY = 201;
- public static final int SSH_FX_OK = 0;
- public static final int SSH_FX_EOF = 1;
- public static final int SSH_FX_NO_SUCH_FILE = 2;
- public static final int SSH_FX_PERMISSION_DENIED = 3;
- public static final int SSH_FX_FAILURE = 4;
- public static final int SSH_FX_BAD_MESSAGE = 5;
- public static final int SSH_FX_NO_CONNECTION = 6;
- public static final int SSH_FX_CONNECTION_LOST = 7;
- public static final int SSH_FX_OP_UNSUPPORTED = 8;
-
- public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
- public static final int SSH_FILEXFER_ATTR_UIDGID = 0x00000002;
- public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
- public static final int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008; //v3 naming convention
- public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
+ public static final int SSH_FX_OK = 0;
+ public static final int SSH_FX_EOF = 1;
+ public static final int SSH_FX_NO_SUCH_FILE = 2;
+ public static final int SSH_FX_PERMISSION_DENIED = 3;
+ public static final int SSH_FX_FAILURE = 4;
+ public static final int SSH_FX_BAD_MESSAGE = 5;
+ public static final int SSH_FX_NO_CONNECTION = 6;
+ public static final int SSH_FX_CONNECTION_LOST = 7;
+ public static final int SSH_FX_OP_UNSUPPORTED = 8;
+ public static final int SSH_FX_FILE_ALREADY_EXISTS = 11;
+ public static final int SSH_FX_LOCK_CONFLICT = 17;
+
+ public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
+ public static final int SSH_FILEXFER_ATTR_UIDGID = 0x00000002;
+ public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
+ public static final int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008; // v3 naming convention
+ public static final int SSH_FILEXFER_ATTR_ACCESSTIME = 0x00000008; // v4
+ public static final int SSH_FILEXFER_ATTR_CREATETIME = 0x00000010; // v4
+ public static final int SSH_FILEXFER_ATTR_MODIFYTIME = 0x00000020; // v4
+ public static final int SSH_FILEXFER_ATTR_ACL = 0x00000040; // v4
+ public static final int SSH_FILEXFER_ATTR_OWNERGROUP = 0x00000080; // v4
+ public static final int SSH_FILEXFER_ATTR_SUBSECOND_TIMES = 0x00000100; // v4
+ public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
public static final int SSH_FXF_READ = 0x00000001;
public static final int SSH_FXF_WRITE = 0x00000002;
@@ -94,17 +108,59 @@ public class DefaultSftpClient implements SftpClient {
public static final int SSH_FXF_TRUNC = 0x00000010;
public static final int SSH_FXF_EXCL = 0x00000020;
+ public static final int SSH_FXF_ACCESS_DISPOSITION = 0x00000007;
+ public static final int SSH_FXF_CREATE_NEW = 0x00000000;
+ public static final int SSH_FXF_CREATE_TRUNCATE = 0x00000001;
+ public static final int SSH_FXF_OPEN_EXISTING = 0x00000002;
+ public static final int SSH_FXF_OPEN_OR_CREATE = 0x00000003;
+ public static final int SSH_FXF_TRUNCATE_EXISTING = 0x00000004;
+ public static final int SSH_FXF_APPEND_DATA = 0x00000008;
+ public static final int SSH_FXF_APPEND_DATA_ATOMIC = 0x00000010;
+ public static final int SSH_FXF_TEXT_MODE = 0x00000020;
+ public static final int SSH_FXF_READ_LOCK = 0x00000040;
+ public static final int SSH_FXF_WRITE_LOCK = 0x00000080;
+ public static final int SSH_FXF_DELETE_LOCK = 0x00000100;
+
+ public static final int SSH_FXP_RENAME_OVERWRITE = 0x00000001;
+ public static final int SSH_FXP_RENAME_ATOMIC = 0x00000002;
+ public static final int SSH_FXP_RENAME_NATIVE = 0x00000004;
+
+ public static final int ACE4_READ_DATA = 0x00000001;
+ public static final int ACE4_LIST_DIRECTORY = 0x00000001;
+ public static final int ACE4_WRITE_DATA = 0x00000002;
+ public static final int ACE4_ADD_FILE = 0x00000002;
+ public static final int ACE4_APPEND_DATA = 0x00000004;
+ public static final int ACE4_ADD_SUBDIRECTORY = 0x00000004;
+ public static final int ACE4_READ_NAMED_ATTRS = 0x00000008;
+ public static final int ACE4_WRITE_NAMED_ATTRS = 0x00000010;
+ public static final int ACE4_EXECUTE = 0x00000020;
+ public static final int ACE4_DELETE_CHILD = 0x00000040;
+ public static final int ACE4_READ_ATTRIBUTES = 0x00000080;
+ public static final int ACE4_WRITE_ATTRIBUTES = 0x00000100;
+ public static final int ACE4_DELETE = 0x00010000;
+ public static final int ACE4_READ_ACL = 0x00020000;
+ public static final int ACE4_WRITE_ACL = 0x00040000;
+ public static final int ACE4_WRITE_OWNER = 0x00080000;
+ public static final int ACE4_SYNCHRONIZE = 0x00100000;
+
+ public static int SFTP_V3 = 3;
+ public static int SFTP_V4 = 4;
+ public static int SFTP_V5 = 5;
+ public static int SFTP_V6 = 6;
+
private final ClientSession clientSession;
private final ChannelSubsystem channel;
private final Map<Integer, Buffer> messages;
private final AtomicInteger cmdId = new AtomicInteger(100);
private final Buffer receiveBuffer = new Buffer();
private boolean closing;
+ private int version;
+ private final Map<String, byte[]> extensions = new HashMap<>();
public DefaultSftpClient(ClientSession clientSession) throws IOException {
this.clientSession = clientSession;
this.channel = clientSession.createSubsystemChannel("sftp");
- this.messages = new HashMap<Integer, Buffer>();
+ this.messages = new HashMap<>();
try {
this.channel.setOut(new OutputStream() {
@Override
@@ -132,6 +188,11 @@ public class DefaultSftpClient implements SftpClient {
init();
}
+ @Override
+ public int getVersion() {
+ return version;
+ }
+
public boolean isClosing() {
return closing;
}
@@ -258,9 +319,9 @@ public class DefaultSftpClient implements SftpClient {
DataOutputStream dos = new DataOutputStream(channel.getInvertedIn());
dos.writeInt(5);
dos.writeByte(SSH_FXP_INIT);
- dos.writeInt(3);
+ dos.writeInt(SFTP_V6);
dos.flush();
- Buffer buffer = null;
+ Buffer buffer;
synchronized (messages) {
while (messages.isEmpty()) {
try {
@@ -276,8 +337,14 @@ public class DefaultSftpClient implements SftpClient {
int type = buffer.getByte();
int id = buffer.getInt();
if (type == SSH_FXP_VERSION) {
- if (id != 3) {
- throw new SshException("Unable to use SFTP v3, server replied with version " + id);
+ if (id < SFTP_V3) {
+ throw new SshException("Unsupported sftp version " + id);
+ }
+ version = id;
+ while (buffer.available() > 0) {
+ String name = buffer.getString();
+ byte[] data = buffer.getBytes();
+ extensions.put(name, data);
}
} else if (type == SSH_FXP_STATUS) {
int substatus = buffer.getInt();
@@ -353,7 +420,9 @@ public class DefaultSftpClient implements SftpClient {
throw new SshException("SFTP error: received " + len + " names instead of 1");
}
String name = buffer.getString();
- String longName = buffer.getString();
+ if (version == SFTP_V3) {
+ String longName = buffer.getString();
+ }
Attributes attrs = readAttributes(buffer);
return name;
} else {
@@ -364,70 +433,232 @@ public class DefaultSftpClient implements SftpClient {
protected Attributes readAttributes(Buffer buffer) throws IOException {
Attributes attrs = new Attributes();
int flags = buffer.getInt();
- if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
- attrs.flags.add(Attribute.Size);
- attrs.size = buffer.getLong();
- }
- if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
- attrs.flags.add(Attribute.UidGid);
- attrs.uid = buffer.getInt();
- attrs.gid = buffer.getInt();
- }
- if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
- attrs.flags.add(Attribute.Perms);
- attrs.perms = buffer.getInt();
- }
- if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
- attrs.flags.add(Attribute.AcModTime);
- attrs.atime = buffer.getInt();
- attrs.mtime = buffer.getInt();
+ if (version == SFTP_V3) {
+ if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
+ attrs.flags.add(Attribute.Size);
+ attrs.size = buffer.getLong();
+ }
+ if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
+ attrs.flags.add(Attribute.UidGid);
+ attrs.uid = buffer.getInt();
+ attrs.gid = buffer.getInt();
+ }
+ if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+ attrs.flags.add(Attribute.Perms);
+ attrs.perms = buffer.getInt();
+ }
+ if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
+ attrs.flags.add(Attribute.AcModTime);
+ attrs.atime = buffer.getInt();
+ attrs.mtime = buffer.getInt();
+ }
+ } else if (version >= SFTP_V4) {
+ attrs.type = buffer.getByte();
+ if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
+ attrs.flags.add(Attribute.Size);
+ attrs.size = buffer.getLong();
+ }
+ if ((flags & SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
+ attrs.flags.add(Attribute.OwnerGroup);
+ attrs.owner = buffer.getString();
+ attrs.group = buffer.getString();
+ }
+ if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+ attrs.flags.add(Attribute.Perms);
+ attrs.perms = buffer.getInt();
+ }
+ if ((flags & SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
+ attrs.flags.add(Attribute.AccessTime);
+ attrs.accessTime = readTime(buffer, flags);
+ attrs.atime = (int) attrs.accessTime.to(TimeUnit.SECONDS);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_CREATETIME) != 0) {
+ attrs.flags.add(Attribute.CreateTime);
+ attrs.createTime = readTime(buffer, flags);
+ attrs.ctime = (int) attrs.createTime.to(TimeUnit.SECONDS);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
+ attrs.flags.add(Attribute.ModifyTime);
+ attrs.modifyTime = readTime(buffer, flags);
+ attrs.mtime = (int) attrs.modifyTime.to(TimeUnit.SECONDS);
+ }
+ // TODO: acl
+ } else {
+ throw new IllegalStateException();
}
return attrs;
}
+ private FileTime readTime(Buffer buffer, int flags) {
+ long secs = buffer.getLong();
+ long millis = secs * 1000;
+ if ((flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
+ millis += buffer.getInt() / 1000000l;
+ }
+ return FileTime.from(millis, TimeUnit.MILLISECONDS);
+ }
+
+
protected void writeAttributes(Buffer buffer, Attributes attributes) throws IOException {
- int flags = 0;
- for (Attribute a : attributes.flags) {
- switch (a) {
- case Size: flags |= SSH_FILEXFER_ATTR_SIZE; break;
- case UidGid: flags |= SSH_FILEXFER_ATTR_UIDGID; break;
- case Perms: flags |= SSH_FILEXFER_ATTR_PERMISSIONS; break;
- case AcModTime: flags |= SSH_FILEXFER_ATTR_ACMODTIME; break;
+ if (version == SFTP_V3) {
+ int flags = 0;
+ for (Attribute a : attributes.flags) {
+ switch (a) {
+ case Size:
+ flags |= SSH_FILEXFER_ATTR_SIZE;
+ break;
+ case UidGid:
+ flags |= SSH_FILEXFER_ATTR_UIDGID;
+ break;
+ case Perms:
+ flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
+ break;
+ case AcModTime:
+ flags |= SSH_FILEXFER_ATTR_ACMODTIME;
+ break;
+ }
}
- }
- buffer.putInt(flags);
- if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
- buffer.putLong(attributes.size);
- }
- if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
- buffer.putInt(attributes.uid);
- buffer.putInt(attributes.gid);
- }
- if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
- buffer.putInt(attributes.perms);
- }
- if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
- buffer.putInt(attributes.atime);
- buffer.putInt(attributes.mtime);
+ buffer.putInt(flags);
+ if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
+ buffer.putLong(attributes.size);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
+ buffer.putInt(attributes.uid);
+ buffer.putInt(attributes.gid);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+ buffer.putInt(attributes.perms);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
+ buffer.putInt(attributes.atime);
+ buffer.putInt(attributes.mtime);
+ }
+ } else if (version >= SFTP_V4) {
+ int flags = 0;
+ for (Attribute a : attributes.flags) {
+ switch (a) {
+ case Size:
+ flags |= SSH_FILEXFER_ATTR_SIZE;
+ break;
+ case OwnerGroup:
+ flags |= SSH_FILEXFER_ATTR_OWNERGROUP;
+ break;
+ case Perms:
+ flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
+ break;
+ case AccessTime:
+ flags |= SSH_FILEXFER_ATTR_ACCESSTIME;
+ break;
+ case ModifyTime:
+ flags |= SSH_FILEXFER_ATTR_MODIFYTIME;
+ break;
+ case CreateTime:
+ flags |= SSH_FILEXFER_ATTR_CREATETIME;
+ break;
+ }
+ }
+ buffer.putInt(flags);
+ buffer.putByte(attributes.type);
+ if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
+ buffer.putLong(attributes.size);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
+ buffer.putString(attributes.owner != null ? attributes.owner : "OWNER@", StandardCharsets.UTF_8);
+ buffer.putString(attributes.group != null ? attributes.group : "GROUP@", StandardCharsets.UTF_8);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+ buffer.putInt(attributes.perms);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
+ buffer.putLong(attributes.accessTime.to(TimeUnit.SECONDS));
+ if ((flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
+ long nanos = attributes.accessTime.to(TimeUnit.NANOSECONDS);
+ nanos = nanos % TimeUnit.SECONDS.toNanos(1);
+ buffer.putInt((int) nanos);
+ }
+ buffer.putInt(attributes.atime);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_CREATETIME) != 0) {
+ buffer.putLong(attributes.createTime.to(TimeUnit.SECONDS));
+ if ((flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
+ long nanos = attributes.createTime.to(TimeUnit.NANOSECONDS);
+ nanos = nanos % TimeUnit.SECONDS.toNanos(1);
+ buffer.putInt((int) nanos);
+ }
+ buffer.putInt(attributes.atime);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
+ buffer.putLong(attributes.modifyTime.to(TimeUnit.SECONDS));
+ if ((flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
+ long nanos = attributes.modifyTime.to(TimeUnit.NANOSECONDS);
+ nanos = nanos % TimeUnit.SECONDS.toNanos(1);
+ buffer.putInt((int) nanos);
+ }
+ buffer.putInt(attributes.atime);
+ }
+ // TODO: acl
+ } else {
+ throw new IllegalStateException();
}
}
public Handle open(String path, EnumSet<OpenMode> options) throws IOException {
Buffer buffer = new Buffer();
buffer.putString(path);
- int mode = 0;
- for (OpenMode m : options) {
- switch (m) {
- case Read: mode |= SSH_FXF_READ; break;
- case Write: mode |= SSH_FXF_WRITE; break;
- case Append: mode |= SSH_FXF_APPEND; break;
- case Create: mode |= SSH_FXF_CREAT; break;
- case Truncate: mode |= SSH_FXF_TRUNC; break;
- case Exclusive: mode |= SSH_FXF_EXCL; break;
+ if (version == SFTP_V3) {
+ int mode = 0;
+ for (OpenMode m : options) {
+ switch (m) {
+ case Read:
+ mode |= SSH_FXF_READ;
+ break;
+ case Write:
+ mode |= SSH_FXF_WRITE;
+ break;
+ case Append:
+ mode |= SSH_FXF_APPEND;
+ break;
+ case Create:
+ mode |= SSH_FXF_CREAT;
+ break;
+ case Truncate:
+ mode |= SSH_FXF_TRUNC;
+ break;
+ case Exclusive:
+ mode |= SSH_FXF_EXCL;
+ break;
+ }
}
+ buffer.putInt(mode);
+ } else {
+ int mode = 0;
+ int access = 0;
+ if (options.contains(OpenMode.Read)) {
+ access |= ACE4_READ_DATA | ACE4_READ_ATTRIBUTES;
+ }
+ if (options.contains(OpenMode.Write)) {
+ access |= ACE4_WRITE_DATA | ACE4_WRITE_ATTRIBUTES;
+ }
+ if (options.contains(OpenMode.Append)) {
+ access |= ACE4_APPEND_DATA;
+ }
+ if (options.contains(OpenMode.Create) && options.contains(OpenMode.Exclusive)) {
+ mode |= SSH_FXF_CREATE_NEW;
+ } else if (options.contains(OpenMode.Create) && options.contains(OpenMode.Truncate)) {
+ mode |= SSH_FXF_CREATE_TRUNCATE;
+ } else if (options.contains(OpenMode.Create)) {
+ mode |= SSH_FXF_OPEN_OR_CREATE;
+ } else if (options.contains(OpenMode.Truncate)) {
+ mode |= SSH_FXF_TRUNCATE_EXISTING;
+ } else {
+ mode |= SSH_FXF_OPEN_EXISTING;
+ }
+ if (version >= SFTP_V5) {
+ buffer.putInt(access);
+ }
+ buffer.putInt(mode);
}
- buffer.putInt(mode);
- buffer.putInt(0);
+ writeAttributes(buffer, new Attributes());
return checkHandle(receive(send(SSH_FXP_OPEN, buffer)));
}
@@ -444,9 +675,29 @@ public class DefaultSftpClient implements SftpClient {
}
public void rename(String oldPath, String newPath) throws IOException {
+ rename(oldPath, newPath, new CopyMode[0]);
+ }
+
+ public void rename(String oldPath, String newPath, CopyMode... options) throws IOException {
Buffer buffer = new Buffer();
buffer.putString(oldPath);
buffer.putString(newPath);
+ if (version >= SFTP_V5) {
+ int opts = 0;
+ for (CopyMode opt : options) {
+ switch (opt) {
+ case Atomic:
+ opts |= SSH_FXP_RENAME_ATOMIC;
+ break;
+ case Overwrite:
+ opts |= SSH_FXP_RENAME_OVERWRITE;
+ break;
+ }
+ }
+ buffer.putInt(opts);
+ } else if (options.length > 0) {
+ throw new UnsupportedOperationException("copy options can not be used with this SFTP version");
+ }
checkStatus(receive(send(SSH_FXP_RENAME, buffer)));
}
@@ -497,8 +748,11 @@ public class DefaultSftpClient implements SftpClient {
public void mkdir(String path) throws IOException {
Buffer buffer = new Buffer();
- buffer.putString(path);
+ buffer.putString(path, StandardCharsets.UTF_8);
buffer.putInt(0);
+ if (version != SFTP_V3) {
+ buffer.putByte((byte) 0);
+ }
checkStatus(receive(send(SSH_FXP_MKDIR, buffer)));
}
@@ -537,7 +791,7 @@ public class DefaultSftpClient implements SftpClient {
DirEntry[] entries = new DirEntry[len];
for (int i = 0; i < len; i++) {
String name = buffer.getString();
- String longName = buffer.getString();
+ String longName = (version == SFTP_V3) ? buffer.getString() : null;
Attributes attrs = readAttributes(buffer);
entries[i] = new DirEntry(name, longName, attrs);
}
@@ -556,18 +810,27 @@ public class DefaultSftpClient implements SftpClient {
public Attributes stat(String path) throws IOException {
Buffer buffer = new Buffer();
buffer.putString(path);
+ if (version >= SFTP_V4) {
+ buffer.putInt(0);
+ }
return checkAttributes(receive(send(SSH_FXP_STAT, buffer)));
}
public Attributes lstat(String path) throws IOException {
Buffer buffer = new Buffer();
buffer.putString(path);
+ if (version >= SFTP_V4) {
+ buffer.putInt(0);
+ }
return checkAttributes(receive(send(SSH_FXP_LSTAT, buffer)));
}
public Attributes stat(Handle handle) throws IOException {
Buffer buffer = new Buffer();
buffer.putString(handle.id);
+ if (version >= SFTP_V4) {
+ buffer.putInt(0);
+ }
return checkAttributes(receive(send(SSH_FXP_FSTAT, buffer)));
}
@@ -592,10 +855,44 @@ public class DefaultSftpClient implements SftpClient {
}
public void symLink(String linkPath, String targetPath) throws IOException {
+ link(linkPath, targetPath, true);
+ }
+
+ public void link(String linkPath, String targetPath, boolean symbolic) throws IOException {
+ if (version < SFTP_V6) {
+ if (!symbolic) {
+ throw new UnsupportedOperationException("Hard links are not supported in sftp v" + version);
+ }
+ Buffer buffer = new Buffer();
+ buffer.putString(targetPath);
+ buffer.putString(linkPath);
+ checkStatus(receive(send(SSH_FXP_SYMLINK, buffer)));
+ } else {
+ Buffer buffer = new Buffer();
+ buffer.putString(targetPath);
+ buffer.putString(linkPath);
+ buffer.putBoolean(symbolic);
+ checkStatus(receive(send(SSH_FXP_LINK, buffer)));
+ }
+ }
+
+ @Override
+ public void lock(Handle handle, long offset, long length, int mask) throws IOException {
Buffer buffer = new Buffer();
- buffer.putString(linkPath);
- buffer.putString(targetPath);
- checkStatus(receive(send(SSH_FXP_SYMLINK, buffer)));
+ buffer.putString(handle.id);
+ buffer.putLong(offset);
+ buffer.putLong(length);
+ buffer.putInt(mask);
+ checkStatus(receive(send(SSH_FXP_BLOCK, buffer)));
+ }
+
+ @Override
+ public void unlock(Handle handle, long offset, long length) throws IOException {
+ Buffer buffer = new Buffer();
+ buffer.putString(handle.id);
+ buffer.putLong(offset);
+ buffer.putLong(length);
+ checkStatus(receive(send(SSH_FXP_UNBLOCK, buffer)));
}
public Iterable<DirEntry> readDir(final String path) throws IOException {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/22581fb8/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileChannel.java b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileChannel.java
new file mode 100644
index 0000000..52a02d0
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileChannel.java
@@ -0,0 +1,345 @@
+/*
+ * 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.client.sftp;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.sshd.client.SftpClient;
+import org.apache.sshd.client.SftpException;
+
+public class SftpFileChannel extends FileChannel {
+
+ final SftpPath p;
+ final EnumSet<SftpClient.OpenMode> modes;
+ final SftpClient sftp;
+ final SftpClient.Handle handle;
+ final Object lock;
+ volatile long pos;
+ volatile Thread blockingThread;
+
+ public SftpFileChannel(SftpPath p, EnumSet<SftpClient.OpenMode> modes) throws IOException {
+ this.p = p;
+ this.modes = modes;
+ sftp = p.getFileSystem().getClient();
+ handle = sftp.open(p.toString(), modes);
+ lock = new Object();
+ pos = 0;
+ }
+
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ return (int) doRead(Collections.singletonList(dst), -1);
+ }
+
+ @Override
+ public int read(ByteBuffer dst, long position) throws IOException {
+ if (position < 0) {
+ throw new IllegalArgumentException();
+ }
+ return (int) doRead(Collections.singletonList(dst), position);
+ }
+
+ @Override
+ public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
+ List<ByteBuffer> buffers = Arrays.asList(dsts).subList(offset, offset + length);
+ return doRead(buffers, -1);
+ }
+
+ protected long doRead(List<ByteBuffer> buffers, long position) throws IOException {
+ ensureOpen();
+ synchronized (lock) {
+ boolean completed = false;
+ boolean eof = false;
+ long curPos = position >= 0 ? position : pos;
+ try {
+ long totalRead = 0;
+ beginBlocking();
+ loop:
+ for (ByteBuffer buffer : buffers) {
+ while (buffer.remaining() > 0) {
+ ByteBuffer wrap = buffer;
+ if (!buffer.hasArray()) {
+ wrap = ByteBuffer.allocate(Math.min(8192, buffer.remaining()));
+ }
+ int read = sftp.read(handle, curPos, wrap.array(), wrap.arrayOffset() + wrap.position(), wrap.remaining());
+ if (read > 0) {
+ if (wrap == buffer) {
+ wrap.position(wrap.position() + read);
+ } else {
+ buffer.put(wrap.array(), wrap.arrayOffset(), read);
+ }
+ curPos += read;
+ totalRead += read;
+ } else {
+ eof = read == -1;
+ break loop;
+ }
+ }
+ }
+ completed = true;
+ return totalRead > 0 ? totalRead : eof ? -1 : 0;
+ } finally {
+ if (position < 0) {
+ pos = curPos;
+ }
+ endBlocking(completed);
+ }
+ }
+ }
+
+ @Override
+ public int write(ByteBuffer src) throws IOException {
+ return (int) doWrite(Collections.singletonList(src), -1);
+ }
+
+ @Override
+ public int write(ByteBuffer src, long position) throws IOException {
+ if (position < 0) {
+ throw new IllegalArgumentException();
+ }
+ return (int) doWrite(Collections.singletonList(src), position);
+ }
+
+ @Override
+ public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
+ List<ByteBuffer> buffers = Arrays.asList(srcs).subList(offset, offset + length);
+ return doWrite(buffers, -1);
+ }
+
+ protected long doWrite(List<ByteBuffer> buffers, long position) throws IOException {
+ ensureOpen();
+ synchronized (lock) {
+ boolean completed = false;
+ long curPos = position >= 0 ? position : pos;
+ try {
+ long totalWritten = 0;
+ beginBlocking();
+ for (ByteBuffer buffer : buffers) {
+ while (buffer.remaining() > 0) {
+ ByteBuffer wrap = buffer;
+ if (!buffer.hasArray()) {
+ wrap = ByteBuffer.allocate(Math.min(8192, buffer.remaining()));
+ buffer.get(wrap.array(), wrap.arrayOffset(), wrap.remaining());
+ }
+ int written = wrap.remaining();
+ sftp.write(handle, curPos, wrap.array(), wrap.arrayOffset() + wrap.position(), written);
+ if (wrap == buffer) {
+ wrap.position(wrap.position() + written);
+ }
+ curPos += written;
+ totalWritten += written;
+ }
+ }
+ completed = true;
+ return totalWritten;
+ } finally {
+ if (position < 0) {
+ pos = curPos;
+ }
+ endBlocking(completed);
+ }
+ }
+ }
+
+ @Override
+ public long position() throws IOException {
+ ensureOpen();
+ return pos;
+ }
+
+ @Override
+ public FileChannel position(long newPosition) throws IOException {
+ if (newPosition < 0) {
+ throw new IllegalArgumentException();
+ }
+ ensureOpen();
+ synchronized (lock) {
+ pos = newPosition;
+ return this;
+ }
+ }
+
+ @Override
+ public long size() throws IOException {
+ return sftp.stat(handle).size;
+ }
+
+ @Override
+ public FileChannel truncate(long size) throws IOException {
+ sftp.setStat(handle, new SftpClient.Attributes().size(size));
+ return this;
+ }
+
+ @Override
+ public void force(boolean metaData) throws IOException {
+ }
+
+ @Override
+ public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
+ if (position < 0 || count < 0) {
+ throw new IllegalArgumentException();
+ }
+ ensureOpen();
+ synchronized (lock) {
+ boolean completed = false;
+ boolean eof = false;
+ long curPos = position;
+ try {
+ long totalRead = 0;
+ beginBlocking();
+
+ byte[] buffer = new byte[32768];
+ while (totalRead < count) {
+ int read = sftp.read(handle, curPos, buffer, 0, buffer.length);
+ if (read > 0) {
+ ByteBuffer wrap = ByteBuffer.wrap(buffer);
+ while (wrap.remaining() > 0) {
+ target.write(wrap);
+ }
+ curPos += read;
+ totalRead += read;
+ } else {
+ eof = read == -1;
+ }
+ }
+ completed = true;
+ return totalRead > 0 ? totalRead : eof ? -1 : 0;
+ } finally {
+ endBlocking(completed);
+ }
+ }
+ }
+
+ @Override
+ public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
+ if (position < 0 || count < 0) {
+ throw new IllegalArgumentException();
+ }
+ ensureOpen();
+ synchronized (lock) {
+ boolean completed = false;
+ long curPos = position >= 0 ? position : pos;
+ try {
+ long totalRead = 0;
+ beginBlocking();
+
+ byte[] buffer = new byte[32768];
+ while (totalRead < count) {
+ ByteBuffer wrap = ByteBuffer.wrap(buffer, 0, (int) Math.min(buffer.length, count - totalRead));
+ int read = src.read(wrap);
+ if (read > 0) {
+ sftp.write(handle, curPos, buffer, 0, read);
+ curPos += read;
+ totalRead += read;
+ } else {
+ break;
+ }
+ }
+ completed = true;
+ return totalRead;
+ } finally {
+ endBlocking(completed);
+ }
+ }
+ }
+
+ @Override
+ public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public FileLock lock(long position, long size, boolean shared) throws IOException {
+ return tryLock(position, size, shared);
+ }
+
+ @Override
+ public FileLock tryLock(final long position, final long size, boolean shared) throws IOException {
+ try {
+ sftp.lock(handle, position, size, 0);
+ } catch (SftpException e) {
+ if (e.getStatus() == DefaultSftpClient.SSH_FX_LOCK_CONFLICT) {
+ throw new OverlappingFileLockException();
+ }
+ throw e;
+ }
+ return new FileLock(this, position, size, shared) {
+
+ private final AtomicBoolean valid = new AtomicBoolean(true);
+
+ @Override
+ public boolean isValid() {
+ return acquiredBy().isOpen() && valid.get();
+ }
+
+ @Override
+ public void release() throws IOException {
+ if (valid.compareAndSet(true, false)) {
+ sftp.unlock(handle, position, size);
+ }
+ }
+ };
+ }
+
+ @Override
+ protected void implCloseChannel() throws IOException {
+ try {
+ final Thread thread = blockingThread;
+ if (thread != null) {
+ thread.interrupt();
+ }
+ } finally {
+ try {
+ sftp.close(handle);
+ } finally {
+ sftp.close();
+ }
+ }
+ }
+
+ private void beginBlocking() {
+ begin();
+ blockingThread = Thread.currentThread();
+ }
+
+ private void endBlocking(boolean completed) throws AsynchronousCloseException {
+ blockingThread = null;
+ end(completed);
+ }
+
+ private void ensureOpen() throws IOException {
+ if (!isOpen()) {
+ throw new ClosedChannelException();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/22581fb8/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystem.java b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystem.java
index d6de9df..98d6679 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystem.java
@@ -118,6 +118,11 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> {
}
@Override
+ public int getVersion() {
+ return delegate.getVersion();
+ }
+
+ @Override
public boolean isClosing() {
return false;
}
@@ -157,6 +162,11 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> {
}
@Override
+ public void rename(String oldPath, String newPath, CopyMode... options) throws IOException {
+ delegate.rename(oldPath, newPath, options);
+ }
+
+ @Override
public int read(Handle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException {
return delegate.read(handle, fileOffset, dst, dstoff, len);
}
@@ -251,6 +261,20 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> {
return delegate.write(path, mode);
}
+ @Override
+ public void link(String linkPath, String targetPath, boolean symbolic) throws IOException {
+ delegate.link(linkPath, targetPath, symbolic);
+ }
+
+ @Override
+ public void lock(Handle handle, long offset, long length, int mask) throws IOException {
+ delegate.lock(handle, offset, length, mask);
+ }
+
+ @Override
+ public void unlock(Handle handle, long offset, long length) throws IOException {
+ delegate.unlock(handle, offset, length);
+ }
}
protected static class DefaultUserPrincipalLookupService extends UserPrincipalLookupService {
@@ -264,7 +288,6 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> {
public GroupPrincipal lookupPrincipalByGroupName(String group) throws IOException {
return new DefaultGroupPrincipal(group);
}
-
}
protected static class DefaultUserPrincipal implements UserPrincipal {
@@ -278,7 +301,6 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> {
this.name = name;
}
- @Override
public String getName() {
return name;
}
@@ -300,7 +322,6 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> {
public String toString() {
return name;
}
-
}
protected static class DefaultGroupPrincipal extends DefaultUserPrincipal implements GroupPrincipal {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/22581fb8/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
index ca60647..00d3d8a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
@@ -19,10 +19,11 @@
package org.apache.sshd.client.sftp;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InterruptedIOException;
+import java.io.OutputStream;
import java.net.URI;
-import java.nio.ByteBuffer;
-import java.nio.channels.ClosedChannelException;
+import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.AccessDeniedException;
import java.nio.file.AccessMode;
@@ -33,11 +34,13 @@ import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemAlreadyExistsException;
import java.nio.file.FileSystemNotFoundException;
+import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.ProviderMismatchException;
+import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
@@ -52,6 +55,7 @@ import java.nio.file.attribute.UserPrincipal;
import java.nio.file.spi.FileSystemProvider;
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@@ -62,11 +66,12 @@ import org.apache.sshd.SshBuilder;
import org.apache.sshd.SshClient;
import org.apache.sshd.client.SftpClient;
import org.apache.sshd.client.SftpException;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.util.IoUtils;
-public class SftpFileSystemProvider extends FileSystemProvider {
+import static org.apache.sshd.client.SftpClient.*;
- public static final int SSH_FX_NO_SUCH_FILE = 2;
- public static final int SSH_FX_FILE_ALREADY_EXISTS = 11;
+public class SftpFileSystemProvider extends FileSystemProvider {
final SshClient client;
final Map<String, SftpFileSystem> fileSystems = new HashMap<String, SftpFileSystem>();
@@ -140,6 +145,11 @@ public class SftpFileSystemProvider extends FileSystemProvider {
@Override
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
+ return newFileChannel(path, options, attrs);
+ }
+
+ @Override
+ public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
final SftpPath p = toSftpPath(path);
final EnumSet<SftpClient.OpenMode> modes = EnumSet.noneOf(SftpClient.OpenMode.class);
for (OpenOption option : options) {
@@ -162,91 +172,10 @@ public class SftpFileSystemProvider extends FileSystemProvider {
}
if (modes.isEmpty()) {
modes.add(SftpClient.OpenMode.Read);
+ modes.add(SftpClient.OpenMode.Write);
}
- return new SeekableByteChannel() {
- final SftpClient sftp = p.getFileSystem().getClient();
- final SftpClient.Handle handle = sftp.open(p.toString(), modes);
- long pos = 0;
- @Override
- public int read(ByteBuffer dst) throws IOException {
- if (dst.hasArray()) {
- int read = sftp.read(handle, pos, dst.array(), dst.arrayOffset() + dst.position(), dst.remaining());
- if (read > 0) {
- dst.position(dst.position() + read);
- pos += read;
- }
- return read;
- } else {
- int remaining = Math.min(8192, dst.remaining());
- byte[] buf = new byte[remaining];
- int read = sftp.read(handle, pos, buf, 0, remaining);
- if (read > 0) {
- dst.put(buf, 0, read);
- pos += read;
- }
- return read;
- }
- }
-
- @Override
- public int write(ByteBuffer src) throws IOException {
- if (src.hasArray()) {
- int rem = src.remaining();
- sftp.write(handle, pos, src.array(), src.arrayOffset() + src.position(), rem);
- src.position(src.position() + rem);
- pos += rem;
- return rem;
- } else {
- byte[] buf = new byte[Math.min(8192, src.remaining())];
- src.get(buf);
- sftp.write(handle, pos, buf, 0, buf.length);
- pos += buf.length;
- return buf.length;
- }
- }
-
- @Override
- public long position() throws IOException {
- if (pos < 0) {
- throw new ClosedChannelException();
- }
- return pos;
- }
-
- @Override
- public SeekableByteChannel position(long newPosition) throws IOException {
- if (newPosition < 0) {
- throw new IllegalArgumentException();
- }
- pos = newPosition;
- return this;
- }
-
- @Override
- public long size() throws IOException {
- return sftp.stat(handle).size;
- }
-
- @Override
- public SeekableByteChannel truncate(long size) throws IOException {
- sftp.setStat(handle, new SftpClient.Attributes().size(size));
- return this;
- }
-
- @Override
- public boolean isOpen() {
- return pos >= 0;
- }
-
- @Override
- public void close() throws IOException {
- if (pos >= 0) {
- sftp.close(handle);
- sftp.close();
- pos = -1;
- }
- }
- };
+ // TODO: attrs
+ return new SftpFileChannel(p, modes);
}
@Override
@@ -288,15 +217,27 @@ public class SftpFileSystemProvider extends FileSystemProvider {
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
SftpPath p = toSftpPath(dir);
try (SftpClient sftp = p.getFileSystem().getClient()) {
- // TODO: handle attributes
try {
sftp.mkdir(dir.toString());
} catch (SftpException e) {
- if (e.getStatus() == SSH_FX_FILE_ALREADY_EXISTS) {
+ if (sftp.getVersion() == 3 && e.getStatus() == DefaultSftpClient.SSH_FX_FAILURE) {
+ try {
+ Attributes attributes = sftp.stat(dir.toString());
+ if (attributes != null) {
+ throw new FileAlreadyExistsException(p.toString());
+ }
+ } catch (SshException e2) {
+ e.addSuppressed(e2);
+ }
+ }
+ if (e.getStatus() == DefaultSftpClient.SSH_FX_FILE_ALREADY_EXISTS) {
throw new FileAlreadyExistsException(p.toString());
}
throw e;
}
+ for (FileAttribute<?> attr : attrs) {
+ setAttribute(p, attr.name(), attr.value());
+ }
}
}
@@ -316,19 +257,129 @@ public class SftpFileSystemProvider extends FileSystemProvider {
@Override
public void copy(Path source, Path target, CopyOption... options) throws IOException {
- // TODO
+ SftpPath src = toSftpPath(source);
+ SftpPath dst = toSftpPath(target);
+ if (src.getFileSystem() != dst.getFileSystem()) {
+ throw new ProviderMismatchException();
+ }
+ checkAccess(src);
+
+ boolean replaceExisting = false;
+ boolean copyAttributes = false;
+ boolean noFollowLinks = false;
+ for (CopyOption opt : options) {
+ replaceExisting |= opt == StandardCopyOption.REPLACE_EXISTING;
+ copyAttributes |= opt == StandardCopyOption.COPY_ATTRIBUTES;
+ noFollowLinks |= opt == LinkOption.NOFOLLOW_LINKS;
+ }
+ LinkOption[] linkOptions = noFollowLinks ? new LinkOption[] { LinkOption.NOFOLLOW_LINKS }
+ : new LinkOption[0];
+
+ // attributes of source file
+ BasicFileAttributes attrs = readAttributes(source,
+ BasicFileAttributes.class,
+ linkOptions);
+ if (attrs.isSymbolicLink())
+ throw new IOException("Copying of symbolic links not supported");
+
+ // delete target if it exists and REPLACE_EXISTING is specified
+ if (replaceExisting) {
+ deleteIfExists(target);
+ } else if (Files.exists(target))
+ throw new FileAlreadyExistsException(target.toString());
+
+ // create directory or copy file
+ if (attrs.isDirectory()) {
+ createDirectory(target);
+ } else {
+ try (InputStream in = newInputStream(source);
+ OutputStream os = newOutputStream(target)) {
+ IoUtils.copy(in, os);
+ }
+ }
+
+ // copy basic attributes to target
+ if (copyAttributes) {
+ BasicFileAttributeView view = getFileAttributeView(target, BasicFileAttributeView.class, linkOptions);
+ try {
+ view.setTimes(attrs.lastModifiedTime(),
+ attrs.lastAccessTime(),
+ attrs.creationTime());
+ } catch (Throwable x) {
+ // rollback
+ try {
+ delete(target);
+ } catch (Throwable suppressed) {
+ x.addSuppressed(suppressed);
+ }
+ throw x;
+ }
+ }
}
@Override
public void move(Path source, Path target, CopyOption... options) throws IOException {
- // TODO
+ SftpPath src = toSftpPath(source);
+ SftpPath dst = toSftpPath(target);
+ if (src.getFileSystem() != dst.getFileSystem()) {
+ throw new ProviderMismatchException();
+ }
+ checkAccess(src);
+
+ boolean replaceExisting = false;
+ boolean copyAttributes = false;
+ boolean noFollowLinks = false;
+ for (CopyOption opt : options) {
+ replaceExisting |= opt == StandardCopyOption.REPLACE_EXISTING;
+ copyAttributes |= opt == StandardCopyOption.COPY_ATTRIBUTES;
+ noFollowLinks |= opt == LinkOption.NOFOLLOW_LINKS;
+ }
+ LinkOption[] linkOptions = noFollowLinks ? new LinkOption[] { LinkOption.NOFOLLOW_LINKS }
+ : new LinkOption[0];
+
+ // attributes of source file
+ BasicFileAttributes attrs = readAttributes(source,
+ BasicFileAttributes.class,
+ linkOptions);
+ if (attrs.isSymbolicLink())
+ throw new IOException("Copying of symbolic links not supported");
+
+ // delete target if it exists and REPLACE_EXISTING is specified
+ if (replaceExisting) {
+ deleteIfExists(target);
+ } else if (Files.exists(target))
+ throw new FileAlreadyExistsException(target.toString());
+
+ try (SftpClient sftp = src.getFileSystem().getClient()) {
+ sftp.rename(src.toString(), dst.toString());
+ }
+ // copy basic attributes to target
+ if (copyAttributes) {
+ BasicFileAttributeView view = getFileAttributeView(target, BasicFileAttributeView.class, linkOptions);
+ try {
+ view.setTimes(attrs.lastModifiedTime(),
+ attrs.lastAccessTime(),
+ attrs.creationTime());
+ } catch (Throwable x) {
+ // rollback
+ try {
+ delete(target);
+ } catch (Throwable suppressed) {
+ x.addSuppressed(suppressed);
+ }
+ throw x;
+ }
+ }
}
@Override
public boolean isSameFile(Path path1, Path path2) throws IOException {
SftpPath p1 = toSftpPath(path1);
SftpPath p2 = toSftpPath(path2);
+ if (p1.getFileSystem() != p2.getFileSystem()) {
+ throw new ProviderMismatchException();
+ }
checkAccess(p1);
checkAccess(p2);
return p1.equals(p2);
@@ -414,7 +465,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
attributes = client.lstat(p.toString());
}
} catch (SftpException e) {
- if (e.getStatus() == SSH_FX_NO_SUCH_FILE) {
+ if (e.getStatus() == DefaultSftpClient.SSH_FX_NO_SUCH_FILE) {
throw new NoSuchFileException(p.toString());
}
throw e;
@@ -423,20 +474,17 @@ public class SftpFileSystemProvider extends FileSystemProvider {
return new PosixFileAttributes() {
@Override
public UserPrincipal owner() {
- // TODO
- return null;
+ return attributes.owner != null ? new SftpFileSystem.DefaultGroupPrincipal(attributes.owner) : null;
}
@Override
public GroupPrincipal group() {
- // TODO
- return null;
+ return attributes.group != null ? new SftpFileSystem.DefaultGroupPrincipal(attributes.group) : null;
}
@Override
public Set<PosixFilePermission> permissions() {
- // TODO
- return null;
+ return permissionsToAttributes(attributes.perms);
}
@Override
@@ -451,8 +499,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
@Override
public FileTime creationTime() {
- // TODO
- return null;
+ return FileTime.from(attributes.ctime, TimeUnit.SECONDS);
}
@Override
@@ -617,20 +664,26 @@ public class SftpFileSystemProvider extends FileSystemProvider {
SftpClient.Attributes attributes = new SftpClient.Attributes();
switch (attr) {
case "lastModifiedTime":
- attributes.mtime = (int) ((FileTime) value).to(TimeUnit.SECONDS);
+ attributes.mtime((int) ((FileTime) value).to(TimeUnit.SECONDS));
break;
case "lastAccessTime":
- attributes.atime = (int) ((FileTime) value).to(TimeUnit.SECONDS);
+ attributes.atime((int) ((FileTime) value).to(TimeUnit.SECONDS));
+ break;
+ case "creationTime":
+ attributes.ctime((int) ((FileTime) value).to(TimeUnit.SECONDS));
break;
case "size":
- attributes.size = (long) value;
+ attributes.size((long) value);
break;
- case "owner":
case "permissions":
+ attributes.perms(attributesToPermissions((Set<PosixFilePermission>) value));
+ break;
+ case "owner":
+ attributes.owner(((UserPrincipal) value).getName());
+ break;
case "group":
- // TODO: handle those
- throw new IllegalArgumentException(attr);
- case "creationTime":
+ attributes.group(((GroupPrincipal) value).getName());
+ break;
case "isRegularFile":
case "isDirectory":
case "isSymbolicLink":
@@ -653,8 +706,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
return (SftpPath) path;
}
- static boolean followLinks(LinkOption... paramVarArgs)
- {
+ static boolean followLinks(LinkOption... paramVarArgs) {
boolean bool = true;
for (LinkOption localLinkOption : paramVarArgs) {
if (localLinkOption == LinkOption.NOFOLLOW_LINKS) {
@@ -664,4 +716,74 @@ public class SftpFileSystemProvider extends FileSystemProvider {
return bool;
}
+ private Set<PosixFilePermission> permissionsToAttributes(int perms) {
+ Set<PosixFilePermission> p = new HashSet<>();
+ if ((perms & S_IRUSR) != 0) {
+ p.add(PosixFilePermission.OWNER_READ);
+ }
+ if ((perms & S_IWUSR) != 0) {
+ p.add(PosixFilePermission.OWNER_WRITE);
+ }
+ if ((perms & S_IXUSR) != 0) {
+ p.add(PosixFilePermission.OWNER_EXECUTE);
+ }
+ if ((perms & S_IRGRP) != 0) {
+ p.add(PosixFilePermission.GROUP_READ);
+ }
+ if ((perms & S_IWGRP) != 0) {
+ p.add(PosixFilePermission.GROUP_WRITE);
+ }
+ if ((perms & S_IXGRP) != 0) {
+ p.add(PosixFilePermission.GROUP_EXECUTE);
+ }
+ if ((perms & S_IROTH) != 0) {
+ p.add(PosixFilePermission.OTHERS_READ);
+ }
+ if ((perms & S_IWOTH) != 0) {
+ p.add(PosixFilePermission.OTHERS_WRITE);
+ }
+ if ((perms & S_IXOTH) != 0) {
+ p.add(PosixFilePermission.OTHERS_EXECUTE);
+ }
+ return p;
+ }
+
+ protected int attributesToPermissions(Set<PosixFilePermission> perms) {
+ int pf = 0;
+ if (perms != null) {
+ for (PosixFilePermission p : perms) {
+ switch (p) {
+ case OWNER_READ:
+ pf |= S_IRUSR;
+ break;
+ case OWNER_WRITE:
+ pf |= S_IWUSR;
+ break;
+ case OWNER_EXECUTE:
+ pf |= S_IXUSR;
+ break;
+ case GROUP_READ:
+ pf |= S_IRGRP;
+ break;
+ case GROUP_WRITE:
+ pf |= S_IWGRP;
+ break;
+ case GROUP_EXECUTE:
+ pf |= S_IXGRP;
+ break;
+ case OTHERS_READ:
+ pf |= S_IROTH;
+ break;
+ case OTHERS_WRITE:
+ pf |= S_IWOTH;
+ break;
+ case OTHERS_EXECUTE:
+ pf |= S_IXOTH;
+ break;
+ }
+ }
+ }
+ return pf;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/22581fb8/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
index 9827507..235e84b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
@@ -41,6 +41,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.sshd.common.SshException;
import org.apache.sshd.common.util.DirectoryScanner;
+import org.apache.sshd.common.util.IoUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -295,7 +296,7 @@ public class ScpHelper {
};
try (OutputStream os = Files.newOutputStream(file)) {
ack();
- copy(is, os, bufSize);
+ IoUtils.copy(is, os, bufSize);
}
if (preserve) {
@@ -439,7 +440,7 @@ public class ScpHelper {
// TODO: use bufSize
try (InputStream in = Files.newInputStream(path)) {
- copy(in, out, bufSize);
+ IoUtils.copy(in, out, bufSize);
}
ack();
readAck(false);
@@ -610,17 +611,4 @@ public class ScpHelper {
return c;
}
- private static long copy(InputStream source, OutputStream sink, int bufferSize)
- throws IOException
- {
- long nread = 0L;
- byte[] buf = new byte[bufferSize];
- int n;
- while ((n = source.read(buf)) > 0) {
- sink.write(buf, 0, n);
- nread += n;
- }
- return nread;
- }
-
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/22581fb8/sshd-core/src/main/java/org/apache/sshd/common/util/Buffer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/Buffer.java b/sshd-core/src/main/java/org/apache/sshd/common/util/Buffer.java
index a214100..9697bb3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/Buffer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/Buffer.java
@@ -19,6 +19,8 @@
package org.apache.sshd.common.util;
import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
@@ -194,12 +196,16 @@ public final class Buffer implements Readable {
}
public String getString() {
+ return getString(StandardCharsets.UTF_8);
+ }
+
+ public String getString(Charset charset) {
int len = getInt();
if (len < 0) {
throw new IllegalStateException("Bad item length: " + len);
}
ensureAvailable(len);
- String s = new String(data, rpos, len);
+ String s = new String(data, rpos, len, charset);
rpos += len;
return s;
}
@@ -389,6 +395,16 @@ public final class Buffer implements Readable {
}
/**
+ * Writes 16 bits
+ * @param i
+ */
+ public void putShort(int i) {
+ ensureCapacity(2);
+ data[wpos++] = (byte) (i >> 8);
+ data[wpos++] = (byte) (i );
+ }
+
+ /**
* Writes 32 bits
* @param i
*/
@@ -432,7 +448,11 @@ public final class Buffer implements Readable {
}
public void putString(String string) {
- putString(string.getBytes());
+ putString(string, Charset.defaultCharset());
+ }
+
+ public void putString(String string, Charset charset) {
+ putString(string.getBytes(charset));
}
public void putString(byte[] str) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/22581fb8/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java
index da53e91..5a74cc3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java
@@ -20,6 +20,8 @@ package org.apache.sshd.common.util;
import java.io.Closeable;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
/**
* TODO Add javadoc
@@ -28,6 +30,21 @@ import java.io.IOException;
*/
public class IoUtils {
+ public static long copy(InputStream source, OutputStream sink) throws IOException {
+ return copy(source, sink, 8192);
+ }
+
+ public static long copy(InputStream source, OutputStream sink, int bufferSize) throws IOException {
+ long nread = 0L;
+ byte[] buf = new byte[bufferSize];
+ int n;
+ while ((n = source.read(buf)) > 0) {
+ sink.write(buf, 0, n);
+ nread += n;
+ }
+ return nread;
+ }
+
public static void closeQuietly(Closeable... closeables) {
for (Closeable c : closeables) {
try {
[11/15] mina-sshd git commit: [SSHD-378] Switch to nio FileSystem api
for commands (scp and sftp subsystem)
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
index 643a1b7..5313f6e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
@@ -25,25 +25,39 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.AccessDeniedException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.GroupPrincipal;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.UserPrincipal;
+import java.nio.file.attribute.UserPrincipalLookupService;
import java.util.Calendar;
import java.util.Collection;
-import java.util.Date;
import java.util.EnumSet;
import java.util.GregorianCalendar;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.file.FileSystemAware;
-import org.apache.sshd.common.file.FileSystemView;
-import org.apache.sshd.common.file.SshFile;
import org.apache.sshd.common.util.Buffer;
-import org.apache.sshd.common.util.IoUtils;
import org.apache.sshd.common.util.SelectorUtils;
import org.apache.sshd.common.util.ThreadUtils;
import org.apache.sshd.server.Command;
@@ -117,6 +131,46 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
}
+ //
+ // File attributes
+ //
+ enum Attribute {
+ Size, // long
+ Uid, // int
+ Owner, // String
+ Gid, // int
+ Group, // String
+ IsDirectory, // boolean
+ IsRegularFile, // boolean
+ IsSymbolicLink, // boolean
+ Permissions, // EnumSet<Permission>
+ CreationTime, // long
+ LastModifiedTime, // long
+ LastAccessTime, // long
+ NLink // int
+ }
+
+ //
+ // File permissions
+ //
+ enum Permission {
+ UserRead,
+ UserWrite,
+ UserExecute,
+ GroupRead,
+ GroupWrite,
+ GroupExecute,
+ OthersRead,
+ OthersWrite,
+ OthersExecute
+ }
+
+ public enum UnsupportedAttributePolicy {
+ Ignore,
+ Warn,
+ ThrowException
+ }
+
/**
* Properties key for the maximum of available open handles per session.
*/
@@ -212,35 +266,41 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
private ExecutorService executors;
private boolean shutdownExecutor;
private Future<?> pendingFuture;
- private FileSystemView root;
+
+ private FileSystem fileSystem = FileSystems.getDefault();
+ private Path defaultDir = fileSystem.getPath(System.getProperty("user.dir"));
+
private int version;
- private Map<String, Handle> handles = new HashMap<String, Handle>();
+ private Map<String, Handle> handles = new HashMap<>();
+
+ private UnsupportedAttributePolicy unsupportedAttributePolicy = UnsupportedAttributePolicy.Warn;
- protected static abstract class Handle {
- SshFile file;
+ protected static abstract class Handle implements java.io.Closeable {
+ Path file;
- public Handle(SshFile file) {
+ public Handle(Path file) {
this.file = file;
}
- public SshFile getFile() {
+ public Path getFile() {
return file;
}
public void close() throws IOException {
- file.handleClose();
}
}
- protected static class DirectoryHandle extends Handle implements Iterator<SshFile> {
+ protected static class DirectoryHandle extends Handle implements Iterator<Path> {
boolean done;
// the directory should be read once at "open directory"
- List<SshFile> fileList = null;
+ DirectoryStream<Path> ds;
+ Iterator<Path> fileList = null;
int fileIndex;
- public DirectoryHandle(SshFile file) {
+ public DirectoryHandle(Path file) throws IOException {
super(file);
- fileList = file.listSshFiles();
+ ds = Files.newDirectoryStream(file);
+ fileList = ds.iterator();
fileIndex = 0;
}
@@ -253,13 +313,11 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
public boolean hasNext() {
- return fileIndex < fileList.size();
+ return fileList.hasNext();
}
- public SshFile next() {
- SshFile f = fileList.get(fileIndex);
- fileIndex++;
- return f;
+ public Path next() {
+ return fileList.next();
}
public void remove() {
@@ -270,70 +328,47 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
// allow the garbage collector to do the job
fileList = null;
}
+
+ @Override
+ public void close() throws IOException {
+ ds.close();
+ }
}
protected static class FileHandle extends Handle {
- int flags;
- OutputStream output;
- long outputPos;
- InputStream input;
- long inputPos;
- long length;
+ SeekableByteChannel channel;
- public FileHandle(SshFile sshFile, int flags) {
- super(sshFile);
- this.flags = flags;
- }
-
- public int read(byte[] data, long offset) throws IOException {
- if ((flags & SSH_FXF_READ) == 0) {
- throw new IOException("File has not been opened for reading");
- }
- if (input != null && offset >= length) {
- return -1;
+ public FileHandle(Path file, int flags) throws IOException {
+ super(file);
+ Set<OpenOption> options = new HashSet<>();
+ if ((flags & SSH_FXF_READ) != 0) {
+ options.add(StandardOpenOption.READ);
}
- if (input != null && offset != inputPos) {
- IoUtils.closeQuietly(input);
- input = null;
+ if ((flags & SSH_FXF_WRITE) != 0) {
+ options.add(StandardOpenOption.WRITE);
}
- if (input == null) {
- input = file.createInputStream(offset);
- length = file.getSize();
- inputPos = offset;
+ if ((flags & SSH_FXF_APPEND) != 0) {
+ options.add(StandardOpenOption.APPEND);
}
- if (offset >= length) {
- return -1;
+ if ((flags & SSH_FXF_TRUNC) != 0) {
+ options.add(StandardOpenOption.TRUNCATE_EXISTING);
}
- int read = input.read(data);
- inputPos += read;
- return read;
+ channel = Files.newByteChannel(file, options);
+ }
+
+ public int read(byte[] data, long offset) throws IOException {
+ channel.position(offset);
+ return channel.read(ByteBuffer.wrap(data));
}
public void write(byte[] data, long offset) throws IOException {
- if ((flags & SSH_FXF_WRITE) == 0) {
- throw new IOException("File has not been opened for writing");
- }
- if ((flags & SSH_FXF_APPEND) != 0) {
- offset = (output != null) ? outputPos : file.getSize();
- }
- if (output != null && offset != outputPos) {
- IoUtils.closeQuietly(output);
- output = null;
- }
- if (output == null) {
- outputPos = offset;
- output = file.createOutputStream(offset);
- }
- output.write(data);
- outputPos += data.length;
+ channel.position(offset);
+ channel.write(ByteBuffer.wrap(data));
}
@Override
public void close() throws IOException {
- IoUtils.closeQuietly(output, input);
- output = null;
- input = null;
- super.close();
+ channel.close();
}
}
@@ -375,8 +410,8 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
this.session = session;
}
- public void setFileSystemView(FileSystemView view) {
- this.root = view;
+ public void setFileSystem(FileSystem fileSystem) {
+ this.fileSystem = fileSystem;
}
public void setExitCallback(ExitCallback callback) {
@@ -450,8 +485,6 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
}
}
- dis = null;
-
callback.onExit(0);
}
}
@@ -495,16 +528,16 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
String path = buffer.getString();
int pflags = buffer.getInt();
- Map<SshFile.Attribute, Object> attrs = readAttrs(buffer);
+ Map<Attribute, Object> attrs = readAttrs(buffer);
log.debug("Received SSH_FXP_OPEN (path={}, pflags={}, attrs={})", new Object[] { path, pflags, attrs });
try {
- SshFile file = resolveFile(path);
- if (file.doesExist()) {
- if ((pflags & SSH_FXF_READ) != 0 && !file.isReadable()) {
+ Path file = resolveFile(path);
+ if (Files.exists(file)) {
+ if ((pflags & SSH_FXF_READ) != 0 && !Files.isReadable(file)) {
sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not read " + path);
return;
}
- if ((pflags & SSH_FXF_WRITE) != 0 && !file.isWritable()) {
+ if ((pflags & SSH_FXF_WRITE) != 0 && !Files.isWritable(file)) {
sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not write " + path);
return;
}
@@ -514,28 +547,14 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
} else {
if (((pflags & SSH_FXF_CREAT) != 0)) {
- if (!file.isWritable()) {
- sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path);
- return;
- }
- if (!file.create()) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, "No such file " + path);
- return;
- }
+ Files.createFile(file);
} else {
sendStatus(id, SSH_FX_NO_SUCH_FILE, "No such file " + path);
return;
}
}
- if ((pflags & SSH_FXF_TRUNC) != 0) {
- if (!file.isWritable()) {
- sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not truncate " + path);
- return;
- }
- file.truncate();
- }
- if ((pflags & SSH_FXF_CREAT) != 0) {
- file.setAttributes(attrs);
+ if (((pflags & SSH_FXF_CREAT) != 0)) {
+ setAttributes(file, attrs);
}
String handle = UUID.randomUUID().toString();
handles.put(handle, new FileHandle(file, pflags));
@@ -602,10 +621,6 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
} else {
FileHandle fh = (FileHandle) p;
fh.write(data, offset);
- SshFile sshFile = fh.getFile();
-
- sshFile.setLastModified(new Date().getTime());
-
sendStatus(id, SSH_FX_OK, "");
}
} catch (IOException e) {
@@ -617,7 +632,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
String path = buffer.getString();
log.debug("Received SSH_FXP_LSTAT (path={})", path);
try {
- SshFile p = resolveFile(path);
+ Path p = resolveFile(path);
sendAttrs(id, p, false);
} catch (FileNotFoundException e) {
sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage());
@@ -645,11 +660,11 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
case SSH_FXP_SETSTAT: {
String path = buffer.getString();
- Map<SshFile.Attribute, Object> attrs = readAttrs(buffer);
+ Map<Attribute, Object> attrs = readAttrs(buffer);
log.debug("Received SSH_FXP_SETSTAT (path={}, attrs={})", path, attrs);
try {
- SshFile p = resolveFile(path);
- p.setAttributes(attrs);
+ Path p = resolveFile(path);
+ setAttributes(p, attrs);
sendStatus(id, SSH_FX_OK, "");
} catch (FileNotFoundException e) {
sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage());
@@ -662,21 +677,19 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
case SSH_FXP_FSETSTAT: {
String handle = buffer.getString();
- Map<SshFile.Attribute, Object> attrs = readAttrs(buffer);
+ Map<Attribute, Object> attrs = readAttrs(buffer);
log.debug("Received SSH_FXP_FSETSTAT (handle={}, attrs={})", handle, attrs);
try {
Handle p = handles.get(handle);
if (p == null) {
sendStatus(id, SSH_FX_FAILURE, handle);
} else {
- p.getFile().setAttributes(attrs);
+ setAttributes(p.getFile(), attrs);
sendStatus(id, SSH_FX_OK, "");
}
} catch (FileNotFoundException e) {
sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage());
- } catch (IOException e) {
- sendStatus(id, SSH_FX_FAILURE, e.getMessage());
- } catch (UnsupportedOperationException e) {
+ } catch (IOException | UnsupportedOperationException e) {
sendStatus(id, SSH_FX_FAILURE, e.getMessage());
}
break;
@@ -685,12 +698,12 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
String path = buffer.getString();
log.debug("Received SSH_FXP_OPENDIR (path={})", path);
try {
- SshFile p = resolveFile(path);
- if (!p.doesExist()) {
+ Path p = resolveFile(path);
+ if (!Files.exists(p)) {
sendStatus(id, SSH_FX_NO_SUCH_FILE, path);
- } else if (!p.isDirectory()) {
+ } else if (!Files.isDirectory(p)) {
sendStatus(id, SSH_FX_NO_SUCH_FILE, path);
- } else if (!p.isReadable()) {
+ } else if (!Files.isReadable(p)) {
sendStatus(id, SSH_FX_PERMISSION_DENIED, path);
} else {
String handle = UUID.randomUUID().toString();
@@ -711,12 +724,12 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
sendStatus(id, SSH_FX_FAILURE, handle);
} else if (((DirectoryHandle) p).isDone()) {
sendStatus(id, SSH_FX_EOF, "", "");
- } else if (!p.getFile().doesExist()) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getFile().getAbsolutePath());
- } else if (!p.getFile().isDirectory()) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getFile().getAbsolutePath());
- } else if (!p.getFile().isReadable()) {
- sendStatus(id, SSH_FX_PERMISSION_DENIED, p.getFile().getAbsolutePath());
+ } else if (!Files.exists(p.getFile())) {
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getFile().toString());
+ } else if (!Files.isDirectory(p.getFile())) {
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getFile().toString());
+ } else if (!Files.isReadable(p.getFile())) {
+ sendStatus(id, SSH_FX_PERMISSION_DENIED, p.getFile().toString());
} else {
DirectoryHandle dh = (DirectoryHandle) p;
if (dh.hasNext()) {
@@ -745,14 +758,13 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
String path = buffer.getString();
log.debug("Received SSH_FXP_REMOVE (path={})", path);
try {
- SshFile p = resolveFile(path);
- if (!p.doesExist()) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath());
- } else if (p.isDirectory()) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath());
- } else if (!p.delete()) {
- sendStatus(id, SSH_FX_FAILURE, "Failed to delete file");
+ Path p = resolveFile(path);
+ if (!Files.exists(p)) {
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString());
+ } else if (Files.isDirectory(p)) {
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString());
} else {
+ Files.delete(p);
sendStatus(id, SSH_FX_OK, "");
}
} catch (IOException e) {
@@ -762,26 +774,25 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
case SSH_FXP_MKDIR: {
String path = buffer.getString();
- Map<SshFile.Attribute, Object> attrs = readAttrs(buffer);
+ Map<Attribute, Object> attrs = readAttrs(buffer);
log.debug("Received SSH_FXP_MKDIR (path={})", path);
// attrs
try {
- SshFile p = resolveFile(path);
- if (p.doesExist()) {
- if (p.isDirectory()) {
- sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, p.getAbsolutePath());
+ Path p = resolveFile(path);
+ if (Files.exists(p)) {
+ if (Files.isDirectory(p)) {
+ sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, p.toString());
} else {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath());
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString());
}
- } else if (!p.isWritable()) {
- sendStatus(id, SSH_FX_PERMISSION_DENIED, p.getAbsolutePath());
- } else if (!p.mkdir()) {
- throw new IOException("Error creating dir " + path);
} else {
- p.setAttributes(attrs);
+ Files.createDirectory(p);
+ setAttributes(p, attrs);
sendStatus(id, SSH_FX_OK, "");
}
+ } catch (AccessDeniedException e) {
+ sendStatus(id, SSH_FX_PERMISSION_DENIED, e.getMessage());
} catch (IOException e) {
sendStatus(id, SSH_FX_FAILURE, e.getMessage());
}
@@ -792,23 +803,12 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
log.debug("Received SSH_FXP_RMDIR (path={})", path);
// attrs
try {
- SshFile p = resolveFile(path);
- if (p.isDirectory()) {
- if (p.doesExist()) {
- if (p.listSshFiles().size() == 0) {
- if (p.delete()) {
- sendStatus(id, SSH_FX_OK, "");
- } else {
- sendStatus(id, SSH_FX_FAILURE, "Unable to delete directory " + path);
- }
- } else {
- sendStatus(id, SSH_FX_FAILURE, path);
- }
- } else {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, path);
- }
+ Path p = resolveFile(path);
+ if (Files.isDirectory(p)) {
+ Files.delete(p);
+ sendStatus(id, SSH_FX_OK, "");
} else {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath());
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString());
}
} catch (IOException e) {
sendStatus(id, SSH_FX_FAILURE, e.getMessage());
@@ -822,7 +822,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
path = ".";
}
try {
- SshFile p = resolveFile(path);
+ Path p = resolveFile(path).toAbsolutePath().normalize();
sendPath(id, p, false);
} catch (FileNotFoundException e) {
e.printStackTrace();
@@ -837,7 +837,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
String path = buffer.getString();
log.debug("Received SSH_FXP_STAT (path={})", path);
try {
- SshFile p = resolveFile(path);
+ Path p = resolveFile(path);
sendAttrs(id, p, true);
} catch (FileNotFoundException e) {
sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage());
@@ -851,15 +851,14 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
String newPath = buffer.getString();
log.debug("Received SSH_FXP_RENAME (oldPath={}, newPath={})", oldPath, newPath);
try {
- SshFile o = resolveFile(oldPath);
- SshFile n = resolveFile(newPath);
- if (!o.doesExist()) {
- sendStatus(id, SSH_FX_NO_SUCH_FILE, o.getAbsolutePath());
- } else if (n.doesExist()) {
- sendStatus(id, SSH_FX_FAILURE, n.getAbsolutePath());
- } else if (!o.move(n)) {
- sendStatus(id, SSH_FX_FAILURE, "Failed to rename file");
+ Path o = resolveFile(oldPath);
+ Path n = resolveFile(newPath);
+ if (!Files.exists(o)) {
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, o.toString());
+ } else if (Files.exists(n)) {
+ sendStatus(id, SSH_FX_FAILURE, n.toString());
} else {
+ Files.move(o, n);
sendStatus(id, SSH_FX_OK, "");
}
} catch (IOException e) {
@@ -871,8 +870,8 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
String path = buffer.getString();
log.debug("Received SSH_FXP_READLINK (path={})", path);
try {
- SshFile f = resolveFile(path);
- String l = f.readSymbolicLink();
+ Path f = resolveFile(path);
+ String l = Files.readSymbolicLink(f).toString();
sendLink(id, l);
} catch (UnsupportedOperationException e) {
sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command " + type + " is unsupported or not implemented");
@@ -886,10 +885,9 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
String targetpath = buffer.getString();
log.debug("Received SSH_FXP_SYMLINK (linkpath={}, targetpath={})", linkpath, targetpath);
try {
- SshFile link = resolveFile(linkpath);
- // TODO: resolving the file is wrong, we should keep it relative
- SshFile target = resolveFile(targetpath);
- link.createSymbolicLink(target);
+ Path link = resolveFile(linkpath);
+ Path target = fileSystem.getPath(targetpath);
+ Files.createSymbolicLink(link, target);
sendStatus(id, SSH_FX_OK, "");
} catch (UnsupportedOperationException e) {
sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command " + type + " is unsupported or not implemented");
@@ -914,7 +912,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
send(buffer);
}
- protected void sendAttrs(int id, SshFile file, boolean followLinks) throws IOException {
+ protected void sendAttrs(int id, Path file, boolean followLinks) throws IOException {
Buffer buffer = new Buffer();
buffer.putByte((byte) SSH_FXP_ATTRS);
buffer.putInt(id);
@@ -922,23 +920,23 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
send(buffer);
}
- protected void sendPath(int id, SshFile f) throws IOException {
+ protected void sendPath(int id, Path f) throws IOException {
sendPath(id, f, true);
}
- protected void sendPath(int id, SshFile f, boolean sendAttrs) throws IOException {
+ protected void sendPath(int id, Path f, boolean sendAttrs) throws IOException {
Buffer buffer = new Buffer();
buffer.putByte((byte) SSH_FXP_NAME);
buffer.putInt(id);
buffer.putInt(1);
//normalize the given path, use *nix style separator
- String normalizedPath = SelectorUtils.normalizePath(f.getAbsolutePath(), "/");
+ String normalizedPath = SelectorUtils.normalizePath(f.toString(), "/");
if (normalizedPath.length() == 0) {
normalizedPath = "/";
}
buffer.putString(normalizedPath);
f = resolveFile(normalizedPath);
- if (f.getName().length() == 0) {
+ if (f.getFileName() == null) {
f = resolveFile(".");
}
buffer.putString(getLongName(f, sendAttrs)); // Format specified in the specs
@@ -958,7 +956,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
send(buffer);
}
- protected void sendName(int id, Iterator<SshFile> files) throws IOException {
+ protected void sendName(int id, Iterator<Path> files) throws IOException {
Buffer buffer = new Buffer();
buffer.putByte((byte) SSH_FXP_NAME);
buffer.putInt(id);
@@ -966,8 +964,8 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
buffer.putInt(0);
int nb = 0;
while (files.hasNext() && buffer.wpos() < MAX_PACKET_LENGTH) {
- SshFile f = files.next();
- buffer.putString(f.getName());
+ Path f = files.next();
+ buffer.putString(f.getFileName().toString());
buffer.putString(getLongName(f)); // Format specified in the specs
writeAttrs(buffer, f, false);
nb++;
@@ -979,26 +977,26 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
send(buffer);
}
- private String getLongName(SshFile f) throws IOException {
+ private String getLongName(Path f) throws IOException {
return getLongName(f, true);
}
- private String getLongName(SshFile f, boolean sendAttrs) throws IOException {
- Map<SshFile.Attribute, Object> attributes;
+ private String getLongName(Path f, boolean sendAttrs) throws IOException {
+ Map<Attribute, Object> attributes;
if (sendAttrs) {
- attributes = f.getAttributes(false);
+ attributes = getAttributes(f, false);
} else {
- attributes = new HashMap<SshFile.Attribute, Object>();
- attributes.put(SshFile.Attribute.Owner, "owner");
- attributes.put(SshFile.Attribute.Group, "group");
- attributes.put(SshFile.Attribute.Size, (long) 0);
- attributes.put(SshFile.Attribute.IsDirectory, false);
- attributes.put(SshFile.Attribute.IsSymbolicLink, false);
- attributes.put(SshFile.Attribute.IsRegularFile, false);
- attributes.put(SshFile.Attribute.Permissions, EnumSet.noneOf(SshFile.Permission.class));
- attributes.put(SshFile.Attribute.LastModifiedTime, (long) 0);
- }
- String username = (String) attributes.get(SshFile.Attribute.Owner);
+ attributes = new HashMap<>();
+ attributes.put(Attribute.Owner, "owner");
+ attributes.put(Attribute.Group, "group");
+ attributes.put(Attribute.Size, (long) 0);
+ attributes.put(Attribute.IsDirectory, false);
+ attributes.put(Attribute.IsSymbolicLink, false);
+ attributes.put(Attribute.IsRegularFile, false);
+ attributes.put(Attribute.Permissions, EnumSet.noneOf(Permission.class));
+ attributes.put(Attribute.LastModifiedTime, (long) 0);
+ }
+ String username = (String) attributes.get(Attribute.Owner);
if (username.length() > 8) {
username = username.substring(0, 8);
} else {
@@ -1006,7 +1004,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
username = username + " ";
}
}
- String group = (String) attributes.get(SshFile.Attribute.Group);
+ String group = (String) attributes.get(Attribute.Group);
if (group.length() > 8) {
group = group.substring(0, 8);
} else {
@@ -1015,11 +1013,11 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
}
- long length = (Long) attributes.get(SshFile.Attribute.Size);
+ long length = (Long) attributes.get(Attribute.Size);
String lengthString = String.format("%1$8s", length);
- boolean isDirectory = (Boolean) attributes.get(SshFile.Attribute.IsDirectory);
- boolean isLink = (Boolean) attributes.get(SshFile.Attribute.IsSymbolicLink);
+ boolean isDirectory = (Boolean) attributes.get(Attribute.IsDirectory);
+ boolean isLink = (Boolean) attributes.get(Attribute.IsSymbolicLink);
int perms = getPermissions(attributes);
StringBuilder sb = new StringBuilder();
@@ -1034,8 +1032,8 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
sb.append((perms & S_IWOTH) != 0 ? "w" : "-");
sb.append((perms & S_IXOTH) != 0 ? "x" : "-");
sb.append(" ");
- sb.append(attributes.containsKey(SshFile.Attribute.NLink)
- ? attributes.get(SshFile.Attribute.NLink) : "1");
+ sb.append(attributes.containsKey(Attribute.NLink)
+ ? attributes.get(Attribute.NLink) : "1");
sb.append(" ");
sb.append(username);
sb.append(" ");
@@ -1043,63 +1041,63 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
sb.append(" ");
sb.append(lengthString);
sb.append(" ");
- sb.append(getUnixDate((Long) attributes.get(SshFile.Attribute.LastModifiedTime)));
+ sb.append(getUnixDate((Long) attributes.get(Attribute.LastModifiedTime)));
sb.append(" ");
- sb.append(f.getName());
+ sb.append(f.getFileName().toString());
return sb.toString();
}
- protected Map<SshFile.Attribute, Object> getPermissions(int perms) {
- Map<SshFile.Attribute, Object> attrs = new HashMap<SshFile.Attribute, Object>();
+ protected Map<Attribute, Object> getPermissions(int perms) {
+ Map<Attribute, Object> attrs = new HashMap<>();
if ((perms & S_IFMT) == S_IFREG) {
- attrs.put(SshFile.Attribute.IsRegularFile, Boolean.TRUE);
+ attrs.put(Attribute.IsRegularFile, Boolean.TRUE);
}
if ((perms & S_IFMT) == S_IFDIR) {
- attrs.put(SshFile.Attribute.IsDirectory, Boolean.TRUE);
+ attrs.put(Attribute.IsDirectory, Boolean.TRUE);
}
if ((perms & S_IFMT) == S_IFLNK) {
- attrs.put(SshFile.Attribute.IsSymbolicLink, Boolean.TRUE);
+ attrs.put(Attribute.IsSymbolicLink, Boolean.TRUE);
}
- EnumSet<SshFile.Permission> p = EnumSet.noneOf(SshFile.Permission.class);
+ EnumSet<Permission> p = EnumSet.noneOf(Permission.class);
if ((perms & S_IRUSR) != 0) {
- p.add(SshFile.Permission.UserRead);
+ p.add(Permission.UserRead);
}
if ((perms & S_IWUSR) != 0) {
- p.add(SshFile.Permission.UserWrite);
+ p.add(Permission.UserWrite);
}
if ((perms & S_IXUSR) != 0) {
- p.add(SshFile.Permission.UserExecute);
+ p.add(Permission.UserExecute);
}
if ((perms & S_IRGRP) != 0) {
- p.add(SshFile.Permission.GroupRead);
+ p.add(Permission.GroupRead);
}
if ((perms & S_IWGRP) != 0) {
- p.add(SshFile.Permission.GroupWrite);
+ p.add(Permission.GroupWrite);
}
if ((perms & S_IXGRP) != 0) {
- p.add(SshFile.Permission.GroupExecute);
+ p.add(Permission.GroupExecute);
}
if ((perms & S_IROTH) != 0) {
- p.add(SshFile.Permission.OthersRead);
+ p.add(Permission.OthersRead);
}
if ((perms & S_IWOTH) != 0) {
- p.add(SshFile.Permission.OthersWrite);
+ p.add(Permission.OthersWrite);
}
if ((perms & S_IXOTH) != 0) {
- p.add(SshFile.Permission.OthersExecute);
+ p.add(Permission.OthersExecute);
}
- attrs.put(SshFile.Attribute.Permissions, p);
+ attrs.put(Attribute.Permissions, p);
return attrs;
}
- protected int getPermissions(Map<SshFile.Attribute, Object> attributes) {
- boolean isReg = (Boolean) attributes.get(SshFile.Attribute.IsRegularFile);
- boolean isDir = (Boolean) attributes.get(SshFile.Attribute.IsDirectory);
- boolean isLnk = (Boolean) attributes.get(SshFile.Attribute.IsSymbolicLink);
+ protected int getPermissions(Map<Attribute, Object> attributes) {
+ boolean isReg = (Boolean) attributes.get(Attribute.IsRegularFile);
+ boolean isDir = (Boolean) attributes.get(Attribute.IsDirectory);
+ boolean isLnk = (Boolean) attributes.get(Attribute.IsSymbolicLink);
int pf = 0;
- EnumSet<SshFile.Permission> perms = (EnumSet<SshFile.Permission>) attributes.get(SshFile.Attribute.Permissions);
- for (SshFile.Permission p : perms) {
+ EnumSet<Permission> perms = (EnumSet<Permission>) attributes.get(Attribute.Permissions);
+ for (Permission p : perms) {
switch (p) {
case UserRead: pf |= S_IRUSR; break;
case UserWrite: pf |= S_IWUSR; break;
@@ -1118,41 +1116,41 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
return pf;
}
- protected void writeAttrs(Buffer buffer, SshFile file, boolean followLinks) throws IOException {
- if (!file.doesExist()) {
- throw new FileNotFoundException(file.getAbsolutePath());
+ protected void writeAttrs(Buffer buffer, Path file, boolean followLinks) throws IOException {
+ if (!Files.exists(file)) {
+ throw new FileNotFoundException(file.toString());
}
- Map<SshFile.Attribute, Object> attributes = file.getAttributes(followLinks);
- boolean isReg = getBool((Boolean) attributes.get(SshFile.Attribute.IsRegularFile));
- boolean isDir = getBool((Boolean) attributes.get(SshFile.Attribute.IsDirectory));
- boolean isLnk = getBool((Boolean) attributes.get(SshFile.Attribute.IsSymbolicLink));
+ Map<Attribute, Object> attributes = getAttributes(file, followLinks);
+ boolean isReg = getBool((Boolean) attributes.get(Attribute.IsRegularFile));
+ boolean isDir = getBool((Boolean) attributes.get(Attribute.IsDirectory));
+ boolean isLnk = getBool((Boolean) attributes.get(Attribute.IsSymbolicLink));
int flags = 0;
- if ((isReg || isLnk) && attributes.containsKey(SshFile.Attribute.Size)) {
+ if ((isReg || isLnk) && attributes.containsKey(Attribute.Size)) {
flags |= SSH_FILEXFER_ATTR_SIZE;
}
- if (attributes.containsKey(SshFile.Attribute.Uid) && attributes.containsKey(SshFile.Attribute.Gid)) {
+ if (attributes.containsKey(Attribute.Uid) && attributes.containsKey(Attribute.Gid)) {
flags |= SSH_FILEXFER_ATTR_UIDGID;
}
- if (attributes.containsKey(SshFile.Attribute.Permissions)) {
+ if (attributes.containsKey(Attribute.Permissions)) {
flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
}
- if (attributes.containsKey(SshFile.Attribute.LastAccessTime) && attributes.containsKey(SshFile.Attribute.LastModifiedTime)) {
+ if (attributes.containsKey(Attribute.LastAccessTime) && attributes.containsKey(Attribute.LastModifiedTime)) {
flags |= SSH_FILEXFER_ATTR_ACMODTIME;
}
buffer.putInt(flags);
if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
- buffer.putLong((Long) attributes.get(SshFile.Attribute.Size));
+ buffer.putLong((Long) attributes.get(Attribute.Size));
}
if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
- buffer.putInt((Integer) attributes.get(SshFile.Attribute.Uid));
- buffer.putInt((Integer) attributes.get(SshFile.Attribute.Gid));
+ buffer.putInt((Integer) attributes.get(Attribute.Uid));
+ buffer.putInt((Integer) attributes.get(Attribute.Gid));
}
if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
buffer.putInt(getPermissions(attributes));
}
if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
- buffer.putInt(((Long) attributes.get(SshFile.Attribute.LastAccessTime)) / 1000);
- buffer.putInt(((Long) attributes.get(SshFile.Attribute.LastModifiedTime)) / 1000);
+ buffer.putInt(((Long) attributes.get(Attribute.LastAccessTime)) / 1000);
+ buffer.putInt(((Long) attributes.get(Attribute.LastModifiedTime)) / 1000);
}
}
@@ -1160,22 +1158,190 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
return bool != null && bool;
}
- protected Map<SshFile.Attribute, Object> readAttrs(Buffer buffer) throws IOException {
- Map<SshFile.Attribute, Object> attrs = new HashMap<SshFile.Attribute, Object>();
+ protected Map<Attribute, Object> getAttributes(Path file, boolean followLinks) throws IOException {
+ String[] attrs = new String[] { "unix:*", "posix:*", "*" };
+ Map<String, Object> a = null;
+ for (String attr : attrs) {
+ try {
+ a = Files.readAttributes(
+ file, attr,
+ followLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS});
+ break;
+ } catch (UnsupportedOperationException e) {
+ // Ignore
+ }
+ }
+ if (a == null) {
+ throw new IllegalStateException();
+ }
+ Map<Attribute, Object> map = new HashMap<>();
+ map.put(Attribute.Size, a.get("size"));
+ if (a.containsKey("uid")) {
+ map.put(Attribute.Uid, a.get("uid"));
+ }
+ if (a.containsKey("owner")) {
+ map.put(Attribute.Owner, ((UserPrincipal) a.get("owner")).getName());
+ } else {
+ map.put(Attribute.Owner, session.getUsername());
+ }
+ if (a.containsKey("gid")) {
+ map.put(Attribute.Gid, a.get("gid"));
+ }
+ if (a.containsKey("group")) {
+ map.put(Attribute.Group, ((GroupPrincipal) a.get("group")).getName());
+ } else {
+ map.put(Attribute.Group, session.getUsername());
+ }
+ if (a.containsKey("nlink")) {
+ map.put(Attribute.NLink, a.get("nlink"));
+ }
+ map.put(Attribute.IsDirectory, a.get("isDirectory"));
+ map.put(Attribute.IsRegularFile, a.get("isRegularFile"));
+ map.put(Attribute.IsSymbolicLink, a.get("isSymbolicLink"));
+ map.put(Attribute.CreationTime, ((FileTime) a.get("creationTime")).toMillis());
+ map.put(Attribute.LastModifiedTime, ((FileTime) a.get("lastModifiedTime")).toMillis());
+ map.put(Attribute.LastAccessTime, ((FileTime) a.get("lastAccessTime")).toMillis());
+ if (a.containsKey("permissions")) {
+ map.put(Attribute.Permissions, fromPerms((Set<PosixFilePermission>) a.get("permissions")));
+ } else {
+ EnumSet<Permission> p = EnumSet.noneOf(Permission.class);
+ if (Files.isReadable(file)) {
+ p.add(Permission.UserRead);
+ p.add(Permission.GroupRead);
+ p.add(Permission.OthersRead);
+ }
+ if (Files.isWritable(file)) {
+ p.add(Permission.UserWrite);
+ p.add(Permission.GroupWrite);
+ p.add(Permission.OthersWrite);
+ }
+ if (Files.isExecutable(file)) {
+ p.add(Permission.UserExecute);
+ p.add(Permission.GroupExecute);
+ p.add(Permission.OthersExecute);
+ }
+ map.put(Attribute.Permissions, p);
+ }
+ return map;
+ }
+
+ protected void setAttributes(Path file, Map<Attribute, Object> attributes) throws IOException {
+ Set<Attribute> unsupported = new HashSet<>();
+ for (Attribute attribute : attributes.keySet()) {
+ String name = null;
+ Object value = attributes.get(attribute);
+ switch (attribute) {
+ case Size: {
+ long newSize = (Long) value;
+ try (FileChannel channel = FileChannel.open(file, StandardOpenOption.WRITE)) {
+ channel.truncate(newSize);
+ }
+ continue;
+ }
+ case Uid: name = "unix:uid"; break;
+ case Gid: name = "unix:gid"; break;
+ case Owner: name = "posix:owner"; value = toUser(file, (String) value); break;
+ case Group: name = "posix:group"; value = toGroup(file, (String) value); break;
+ case Permissions: name = "posix:permissions"; value = toPerms((EnumSet<Permission>) value); break;
+ case CreationTime: name = "basic:creationTime"; value = FileTime.fromMillis((Long) value); break;
+ case LastModifiedTime: name = "basic:lastModifiedTime"; value = FileTime.fromMillis((Long) value); break;
+ case LastAccessTime: name = "basic:lastAccessTime"; value = FileTime.fromMillis((Long) value); break;
+ }
+ if (name != null && value != null) {
+ try {
+ Files.setAttribute(file, name, value, LinkOption.NOFOLLOW_LINKS);
+ } catch (UnsupportedOperationException e) {
+ unsupported.add(attribute);
+ }
+ }
+ }
+ handleUnsupportedAttributes(unsupported);
+ }
+
+ protected void handleUnsupportedAttributes(Collection<Attribute> attributes) {
+ if (!attributes.isEmpty()) {
+ StringBuilder sb = new StringBuilder();
+ for (Attribute attr : attributes) {
+ if (sb.length() > 0) {
+ sb.append(", ");
+ }
+ sb.append(attr.name());
+ }
+ switch (unsupportedAttributePolicy) {
+ case Ignore:
+ break;
+ case Warn:
+ log.warn("Unsupported attributes: " + sb.toString());
+ break;
+ case ThrowException:
+ throw new UnsupportedOperationException("Unsupported attributes: " + sb.toString());
+ }
+ }
+ }
+
+
+ private EnumSet<Permission> fromPerms(Set<PosixFilePermission> perms) {
+ EnumSet<Permission> p = EnumSet.noneOf(Permission.class);
+ for (PosixFilePermission perm : perms) {
+ switch (perm) {
+ case OWNER_READ: p.add(Permission.UserRead); break;
+ case OWNER_WRITE: p.add(Permission.UserWrite); break;
+ case OWNER_EXECUTE: p.add(Permission.UserExecute); break;
+ case GROUP_READ: p.add(Permission.GroupRead); break;
+ case GROUP_WRITE: p.add(Permission.GroupWrite); break;
+ case GROUP_EXECUTE: p.add(Permission.GroupExecute); break;
+ case OTHERS_READ: p.add(Permission.OthersRead); break;
+ case OTHERS_WRITE: p.add(Permission.OthersWrite); break;
+ case OTHERS_EXECUTE: p.add(Permission.OthersExecute); break;
+ }
+ }
+ return p;
+ }
+
+ private GroupPrincipal toGroup(Path file, String name) throws IOException {
+ UserPrincipalLookupService lookupService = file.getFileSystem().getUserPrincipalLookupService();
+ return lookupService.lookupPrincipalByGroupName(name);
+ }
+
+ private UserPrincipal toUser(Path file, String name) throws IOException {
+ UserPrincipalLookupService lookupService = file.getFileSystem().getUserPrincipalLookupService();
+ return lookupService.lookupPrincipalByName(name);
+ }
+
+ private Set<PosixFilePermission> toPerms(EnumSet<Permission> perms) {
+ Set<PosixFilePermission> set = new HashSet<>();
+ for (Permission p : perms) {
+ switch (p) {
+ case UserRead: set.add(PosixFilePermission.OWNER_READ); break;
+ case UserWrite: set.add(PosixFilePermission.OWNER_WRITE); break;
+ case UserExecute: set.add(PosixFilePermission.OWNER_EXECUTE); break;
+ case GroupRead: set.add(PosixFilePermission.GROUP_READ); break;
+ case GroupWrite: set.add(PosixFilePermission.GROUP_WRITE); break;
+ case GroupExecute: set.add(PosixFilePermission.GROUP_EXECUTE); break;
+ case OthersRead: set.add(PosixFilePermission.OTHERS_READ); break;
+ case OthersWrite: set.add(PosixFilePermission.OTHERS_WRITE); break;
+ case OthersExecute: set.add(PosixFilePermission.OTHERS_EXECUTE); break;
+ }
+ }
+ return set;
+ }
+
+ protected Map<Attribute, Object> readAttrs(Buffer buffer) throws IOException {
+ Map<Attribute, Object> attrs = new HashMap<>();
int flags = buffer.getInt();
if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
- attrs.put(SshFile.Attribute.Size, buffer.getLong());
+ attrs.put(Attribute.Size, buffer.getLong());
}
if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
- attrs.put(SshFile.Attribute.Uid, buffer.getInt());
- attrs.put(SshFile.Attribute.Gid, buffer.getInt());
+ attrs.put(Attribute.Uid, buffer.getInt());
+ attrs.put(Attribute.Gid, buffer.getInt());
}
if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
attrs.putAll(getPermissions(buffer.getInt()));
}
if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
- attrs.put(SshFile.Attribute.LastAccessTime, ((long) buffer.getInt()) * 1000);
- attrs.put(SshFile.Attribute.LastModifiedTime, ((long) buffer.getInt()) * 1000);
+ attrs.put(Attribute.LastAccessTime, ((long) buffer.getInt()) * 1000);
+ attrs.put(Attribute.LastModifiedTime, ((long) buffer.getInt()) * 1000);
}
return attrs;
}
@@ -1203,37 +1369,46 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
public void destroy() {
- // if thread has not completed, cancel it
- if ((pendingFuture != null) && (!pendingFuture.isDone())) {
- boolean result = pendingFuture.cancel(true);
- // TODO consider waiting some reasonable (?) amount of time for cancellation
+ if (!closed) {
if (log.isDebugEnabled()) {
- log.debug("destroy() - cancel pending future=" + result);
+ log.debug("destroy() - mark as closed");
}
- }
- pendingFuture = null;
+ closed = true;
- if ((executors != null) && shutdownExecutor) {
- Collection<Runnable> runners = executors.shutdownNow();
- if (log.isDebugEnabled()) {
- log.debug("destroy() - shutdown executor service - runners count=" + ((runners == null) ? 0 : runners.size()));
+ // if thread has not completed, cancel it
+ if ((pendingFuture != null) && (!pendingFuture.isDone())) {
+ boolean result = pendingFuture.cancel(true);
+ // TODO consider waiting some reasonable (?) amount of time for cancellation
+ if (log.isDebugEnabled()) {
+ log.debug("destroy() - cancel pending future=" + result);
+ }
}
- }
- executors = null;
+ pendingFuture = null;
- if (!closed) {
- if (log.isDebugEnabled()) {
- log.debug("destroy() - mark as closed");
+ if ((executors != null) && shutdownExecutor) {
+ Collection<Runnable> runners = executors.shutdownNow();
+ if (log.isDebugEnabled()) {
+ log.debug("destroy() - shutdown executor service - runners count=" + ((runners == null) ? 0 : runners.size()));
+ }
}
- closed = true;
+ executors = null;
+
+ try {
+ fileSystem.close();
+ } catch (UnsupportedOperationException e) {
+ // Ignore
+ } catch (IOException e) {
+ log.debug("Error closing FileSystem", e);
+ }
}
}
- private SshFile resolveFile(String path) {
- return this.root.getNormalizedView().getFile(path);
+ private Path resolveFile(String path) {
+ return defaultDir.resolve(path);
+// return this.fileSystem.getPath(path);
}
private final static String[] MONTHS = { "Jan", "Feb", "Mar", "Apr", "May",
@@ -1242,12 +1417,12 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
/**
* Get unix style date string.
*/
- private final static String getUnixDate(long millis) {
+ private static String getUnixDate(long millis) {
if (millis < 0) {
return "------------";
}
- StringBuffer sb = new StringBuffer(16);
+ StringBuilder sb = new StringBuilder(16);
Calendar cal = new GregorianCalendar();
cal.setTimeInMillis(millis);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java b/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
index 42d1dcc..750ecc9 100644
--- a/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
@@ -94,9 +94,10 @@ public class SftpFileSystemTest extends BaseTest {
// assertEquals("test.txt", Files.readSymbolicLink(link).toString());
Path link = fs.getPath("target/sftp/client/test2.txt");
- Files.createSymbolicLink(link, file);
+ Files.createSymbolicLink(link, link.getParent().relativize(file));
assertTrue(Files.isSymbolicLink(link));
- assertEquals(file.toAbsolutePath().toString(), Files.readSymbolicLink(link).toString());
+ assertEquals("test.txt", Files.readSymbolicLink(link).toString());
+ Files.delete(link);
attrs = Files.readAttributes(file, "*", LinkOption.NOFOLLOW_LINKS);
System.out.println(attrs);
@@ -109,4 +110,15 @@ public class SftpFileSystemTest extends BaseTest {
fs.close();
}
+ @Test
+ public void testRootFileSystem() throws Exception {
+ Path rootNative = Paths.get("target/root").toAbsolutePath();
+ Utils.deleteRecursive(rootNative.toFile());
+ Files.createDirectories(rootNative);
+
+ FileSystem fs = FileSystems.newFileSystem(URI.create("root:" + rootNative.toUri().toString() + "!/"), null);
+
+ Files.createDirectories(fs.getPath("test/foo"));
+ }
+
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SftpTest.java b/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
index b4f2c0e..b44f6af 100644
--- a/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
@@ -458,7 +458,7 @@ public class SftpTest extends BaseTest {
assertTrue(target.exists());
assertEquals("0123456789", readFile(unixPath));
- c.symlink(unixPath, linkUnixPath);
+ c.symlink(linkUnixPath, unixPath);
assertTrue(link.exists());
assertEquals("0123456789", readFile(linkUnixPath));
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeFileSystemViewTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeFileSystemViewTest.java b/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeFileSystemViewTest.java
deleted file mode 100644
index a77d950..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeFileSystemViewTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.nativefs;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.sshd.common.file.FileSystemView;
-import org.apache.sshd.common.file.SshFile;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-
-public class NativeFileSystemViewTest {
-
- @Test
- public void testResolveWithVirtualRoots() throws Exception {
-
- Map<String, String> roots = new HashMap<String, String>();
- roots.put("A:", "/fs/rootA");
- roots.put("Z:", "/fs/rootZ");
- String current = "Z:/git";
- NativeFileSystemView view = new NativeFileSystemView("user", roots, current, '/', false);
-
- assertEquals("Z:/git/foo/bar", view.getFile("foo/bar").getAbsolutePath());
- assertEquals("Z:/foo/bar", view.getFile("../foo/bar").getAbsolutePath());
- assertEquals("A:/temp", view.getFile("A:/./a/../temp").getAbsolutePath());
- assertEquals("A:/temp", view.getFile("A:/../../temp").getAbsolutePath());
-
- FileSystemView normView = view.getNormalizedView();
-
- assertEquals("/Z/git/foo/bar", normView.getFile("foo/bar").getAbsolutePath());
- assertEquals("/Z/foo/bar", normView.getFile("../foo/bar").getAbsolutePath());
- assertEquals("/A/temp", normView.getFile("/A/./a/../temp").getAbsolutePath());
- assertEquals("/Z/temp", normView.getFile("/A/../Z/temp").getAbsolutePath());
- }
-
- @Test
- public void testResolveWithVirtualRootsWithBackslash() throws Exception {
-
- Map<String, String> roots = new HashMap<String, String>();
- roots.put("A:", "/fs/rootA");
- roots.put("Z:", "/fs/rootZ");
- String current = "Z:/git";
- NativeFileSystemView view = new NativeFileSystemView("user", roots, current, '\\', false);
-
- assertEquals("Z:\\git\\foo\\bar", view.getFile("/Z:/git/foo/bar").getAbsolutePath());
- assertEquals("Z:\\git\\foo\\bar", view.getFile("foo/bar").getAbsolutePath());
- assertEquals("Z:\\git\\foo", view.getFile("foo/bar").getParentFile().getAbsolutePath());
- assertEquals("Z:\\git", view.getFile("foo/bar").getParentFile().getParentFile().getAbsolutePath());
- assertEquals("Z:\\", view.getFile("foo/bar").getParentFile().getParentFile().getParentFile().getAbsolutePath());
- assertFalse(view.getFile("foo/bar").getParentFile().getParentFile().getParentFile().isRemovable());
- assertEquals("Z:\\foo\\bar", view.getFile("../foo/bar").getAbsolutePath());
- assertEquals("A:\\temp", view.getFile("A:/./a/../temp").getAbsolutePath());
- assertEquals("A:\\temp", view.getFile("A:/../../temp").getAbsolutePath());
-
- FileSystemView normView = view.getNormalizedView();
-
- assertEquals("/Z/git/foo/bar", normView.getFile("foo/bar").getAbsolutePath());
- assertEquals("/Z/git/foo", normView.getFile("foo/bar").getParentFile().getAbsolutePath());
- assertEquals("/Z/foo/bar", normView.getFile("../foo/bar").getAbsolutePath());
- assertEquals("/A/temp", normView.getFile("/A/./a/../temp").getAbsolutePath());
- assertEquals("/Z/temp", normView.getFile("/A/../Z/temp").getAbsolutePath());
- }
-
- @Test
- public void testResolveWithPhysicalRoots() throws Exception {
-
- Map<String, String> roots = new HashMap<String, String>();
- roots.put("V:", "A:/bar");
- roots.put("X:", "B:");
- String current = "X:/git";
- NativeFileSystemView view = new NativeFileSystemView("user", roots, current, '/', false);
-
- assertEquals("X:/git/foo/bar", view.getFile("foo/bar").getAbsolutePath());
-
- assertEquals("X:/foo/bar", view.getFile("X:/foo/bar").getAbsolutePath());
- assertEquals(new File("B:/foo/bar").toString(), ((NativeSshFile) view.getFile("X:/foo/bar")).getNativeFile().toString());
-
- assertEquals("X:/foo/bar", view.getFile("../foo/bar").getAbsolutePath());
- assertEquals(new File("B:/foo/bar").toString(), ((NativeSshFile) view.getFile("../foo/bar")).getNativeFile().toString());
-
- assertEquals("V:/temp", view.getFile("V:/./a/../temp").getAbsolutePath());
-
- assertEquals("V:/temp", view.getFile("V:/../../temp").getAbsolutePath());
- assertEquals(new File("A:/bar/temp").toString(), ((NativeSshFile) view.getFile("V:/../../temp")).getNativeFile().toString());
-
- assertEquals("X:/", view.getFile("..").getAbsolutePath());
-
- SshFile cur = view.getFile(".");
- assertEquals("X:/git", cur.getAbsolutePath());
- cur = view.getFile(cur, "..");
- assertEquals("X:/", cur.getAbsolutePath());
-
-
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeSshFileTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeSshFileTest.java b/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeSshFileTest.java
deleted file mode 100644
index 7ff2787..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeSshFileTest.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.nativefs;
-
-import org.apache.sshd.util.BaseTest;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-public class NativeSshFileTest extends BaseTest {
-
- @Test
- public void testResolve() {
- assertEquals("Z:/git/mina-sshd/sshd-core/target/scp/remote/out.txt",
- NativeSshFile.getPhysicalName("Z:\\git/", "Z:\\git\\mina-sshd\\sshd-core", "\\mina-sshd\\sshd-core\\target\\scp\\remote\\out.txt", false));
- assertEquals("Z:/git/mina-sshd/sshd-core/target/scp/remote/out.txt",
- NativeSshFile.getPhysicalName("Z:/", "Z:\\git\\mina-sshd\\sshd-core", "\\git\\mina-sshd\\sshd-core\\target\\scp\\remote\\out.txt", false));
- assertEquals("Z:/git/mina-sshd/sshd-core/target/scp/remote/out.txt",
- NativeSshFile.getPhysicalName("Z:/", "Z:\\git\\mina-sshd\\sshd-core", "/git/mina-sshd/sshd-core/target/scp/remote/out.txt", false));
-
- assertEquals("/bar", NativeSshFile.getPhysicalName("/", "/foo", "/bar", false));
- assertEquals("/bar", NativeSshFile.getPhysicalName("/", "/", "/bar", false));
- assertEquals("/bar", NativeSshFile.getPhysicalName("/", "/", "bar", false));
- assertEquals("/foo/bar", NativeSshFile.getPhysicalName("/", "/foo", "bar", false));
- assertEquals("/foo/bar", NativeSshFile.getPhysicalName("/", "/foo/xyz", "../bar", false));
- assertEquals("/foo/xyz/bar", NativeSshFile.getPhysicalName("/", "/foo/xyz", "./bar", false));
-
- assertEquals("/foo/bar", NativeSshFile.getPhysicalName("/foo", "/", "bar", false));
- assertEquals("/foo/bar", NativeSshFile.getPhysicalName("/foo", "/xyz", "../bar", false));
- assertEquals("/foo/bar", NativeSshFile.getPhysicalName("/foo", "/xyz", "../../bar", false));
- assertEquals("/foo/xyz/bar", NativeSshFile.getPhysicalName("/foo", "/xyz", "./bar", false));
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/test/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemTest.java b/sshd-core/src/test/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemTest.java
deleted file mode 100644
index 17e651e..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemTest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.virtualfs;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.apache.sshd.SshServer;
-import org.apache.sshd.common.file.FileSystemView;
-import org.apache.sshd.common.file.SshFile;
-import org.apache.sshd.common.file.nativefs.NativeFileSystemFactory;
-import org.apache.sshd.common.file.nativefs.NativeSshFile;
-import org.apache.sshd.common.session.AbstractSession;
-import org.apache.sshd.common.util.Buffer;
-import org.apache.sshd.util.BaseTest;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-public class VirtualFileSystemTest extends BaseTest {
-
- @Test
- public void testNativeFileSystem() throws IOException {
- String homeDir = System.getProperty("user.dir");
- NativeFileSystemFactory vfs = new NativeFileSystemFactory();
-
- FileSystemView view = vfs.createFileSystemView(new TestSession());
-
- SshFile file = view.getFile("foo");
- String physicalName = ((NativeSshFile) file).getNativeFile().getAbsolutePath();
- assertEquals(homeDir + File.separator + "foo", physicalName);
-
- file = view.getFile(view.getFile("foo"), "../bar");
- physicalName = ((NativeSshFile) file).getNativeFile().getAbsolutePath();
- assertEquals(homeDir + File.separator + "bar", physicalName);
-
- file = view.getFile("../bar");
- physicalName = ((NativeSshFile) file).getNativeFile().getAbsolutePath();
- assertEquals(new File(homeDir, "../bar").getCanonicalPath(), physicalName);
- }
-
- @Test
- public void testVirtualFileSystem() {
- String homeDir = System.getProperty("user.dir");
- VirtualFileSystemFactory vfs = new VirtualFileSystemFactory(homeDir);
-
- FileSystemView view = vfs.createFileSystemView(new TestSession());
-
- SshFile file = view.getFile("foo");
- String physicalName = ((NativeSshFile) file).getNativeFile().getAbsolutePath();
- assertEquals(homeDir + File.separator + "foo", physicalName);
-
- file = view.getFile(view.getFile("foo"), "../bar");
- physicalName = ((NativeSshFile) file).getNativeFile().getAbsolutePath();
- assertEquals(homeDir + File.separator + "bar", physicalName);
-
- file = view.getFile("../bar");
- physicalName = ((NativeSshFile) file).getNativeFile().getAbsolutePath();
- assertEquals(homeDir + File.separator + "bar", physicalName);
- }
-
- static class TestSession extends AbstractSession {
- TestSession() {
- super(true, SshServer.setUpDefaultServer(), null);
- this.username = "userName";
- }
- @Override
- protected void handleMessage(Buffer buffer) throws Exception {
- }
- @Override
- protected boolean readIdentification(Buffer buffer) throws IOException {
- return false;
- }
- @Override
- protected void sendKexInit() throws IOException {
- }
- @Override
- protected void checkKeys() {
- }
- @Override
- protected void receiveKexInit(Buffer buffer) throws IOException {
- }
- @Override
- public void startService(String name) throws Exception {
- }
- @Override
- public void resetIdleTimeout() {
- }
- }
-
-}
[14/15] mina-sshd git commit: [SSHD-416] Avoid unnecessary memory
copies when reading / writing data to files
Posted by gn...@apache.org.
[SSHD-416] Avoid unnecessary memory copies when reading / writing data to files
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/656f0d17
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/656f0d17
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/656f0d17
Branch: refs/heads/master
Commit: 656f0d17e5cf2b7384156feb77c193d5c85f0e77
Parents: 55d45e9
Author: Guillaume Nodet <gn...@apache.org>
Authored: Mon Feb 23 01:08:48 2015 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Mon Feb 23 16:21:30 2015 +0100
----------------------------------------------------------------------
.../apache/sshd/server/sftp/SftpSubsystem.java | 29 ++++++++++++++------
1 file changed, 20 insertions(+), 9 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/656f0d17/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
index 28fba31..b78315c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
@@ -1196,15 +1196,23 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
protected void doWrite(Buffer buffer, int id) throws IOException {
String handle = buffer.getString();
long offset = buffer.getLong();
- byte[] data = buffer.getBytes();
- log.debug("Received SSH_FXP_WRITE (handle={}, offset={}, data=byte[{}])", new Object[] { handle, offset, data.length });
+ int length = buffer.getInt();
+ if (length < 0) {
+ throw new IllegalStateException();
+ }
+ if (buffer.available() < length) {
+ throw new BufferUnderflowException();
+ }
+ byte[] data = buffer.array();
+ int doff = buffer.rpos();
+ log.debug("Received SSH_FXP_WRITE (handle={}, offset={}, data=byte[{}])", new Object[] { handle, offset, length });
try {
Handle p = handles.get(handle);
if (!(p instanceof FileHandle)) {
sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
} else {
FileHandle fh = (FileHandle) p;
- fh.write(data, offset);
+ fh.write(data, doff, length, offset);
sendStatus(id, SSH_FX_OK, "");
}
} catch (IOException e) {
@@ -1223,13 +1231,16 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
} else {
FileHandle fh = (FileHandle) p;
- byte[] b = new byte[Math.min(len, Buffer.MAX_LEN)];
- len = fh.read(b, offset);
+ Buffer buf = new Buffer(len + 9);
+ buf.putByte((byte) SSH_FXP_DATA);
+ buf.putInt(id);
+ int pos = buf.wpos();
+ buf.putInt(0);
+ len = fh.read(buf.array(), buf.wpos(), len, offset);
if (len >= 0) {
- Buffer buf = new Buffer(len + 5);
- buf.putByte((byte) SSH_FXP_DATA);
- buf.putInt(id);
- buf.putBytes(b, 0, len);
+ buf.wpos(pos);
+ buf.putInt(len);
+ buf.wpos(pos + 4 + len);
send(buf);
} else {
sendStatus(id, SSH_FX_EOF, "");