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 2014/10/17 16:06:40 UTC
[2/2] git commit: [SSHD-355] Correctly handle permissions when
opening files through SFTP [SSHD-364] Fix SFTP writes with offset
[SSHD-355] Correctly handle permissions when opening files through SFTP
[SSHD-364] Fix SFTP writes with offset
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/d4a524e4
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/d4a524e4
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/d4a524e4
Branch: refs/heads/master
Commit: d4a524e428449f53a0e4b2402ca5419a436eb77d
Parents: 8e99ed7
Author: Guillaume Nodet <gn...@apache.org>
Authored: Fri Oct 17 15:32:16 2014 +0200
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Fri Oct 17 16:00:17 2014 +0200
----------------------------------------------------------------------
.../sshd/client/sftp/DefaultSftpClient.java | 2 +-
.../common/file/nativefs/NativeSshFile.java | 34 ++++----
.../common/file/nativefs/NativeSshFileNio.java | 37 +++++++++
.../apache/sshd/server/sftp/SftpSubsystem.java | 37 +++++++--
.../src/test/java/org/apache/sshd/SftpTest.java | 81 +++++++++++++++++++-
5 files changed, 163 insertions(+), 28 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d4a524e4/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 9a945cf..8eb6b52 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
@@ -700,7 +700,7 @@ public class DefaultSftpClient implements SftpClient {
}
public OutputStream write(final String path) throws IOException {
- return write(path, EnumSet.of(OpenMode.Write));
+ return write(path, EnumSet.of(OpenMode.Write, OpenMode.Create, OpenMode.Truncate));
}
public OutputStream write(final String path, final EnumSet<OpenMode> mode) throws IOException {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d4a524e4/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
index d63d446..8b336ef 100644
--- 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
@@ -22,6 +22,7 @@ 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;
@@ -318,13 +319,8 @@ public class NativeSshFile implements SshFile {
/**
* Truncate file to length 0.
*/
- public void truncate() throws IOException{
- RandomAccessFile tempFile = new RandomAccessFile(file, "rw");
- try {
- tempFile.setLength(0);
- } finally {
- tempFile.close();
- }
+ public void truncate() throws IOException {
+ new FileWriter(file).close();
}
/**
@@ -412,9 +408,12 @@ public class NativeSshFile implements SshFile {
}
// 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.setLength(offset);
raf.seek(offset);
// The IBM jre needs to have both the stream and the random access file
@@ -423,6 +422,9 @@ public class NativeSshFile implements SshFile {
public void close() throws IOException {
super.close();
raf.close();
+ if (!canRead) {
+ file.setReadable(false, true);
+ }
}
};
} catch (IOException e) {
@@ -442,20 +444,12 @@ public class NativeSshFile implements SshFile {
}
// move to the appropriate offset and create input stream
- final RandomAccessFile raf = new RandomAccessFile(file, "r");
+ final FileInputStream fis = new FileInputStream(file);
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 FileInputStream(raf.getFD()) {
- public void close() throws IOException {
- super.close();
- raf.close();
- }
- };
+ fis.getChannel().position(offset);
+ return fis;
} catch (IOException e) {
- raf.close();
+ fis.close();
throw e;
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d4a524e4/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 1cfef6b..9e67c72 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
@@ -22,11 +22,15 @@ 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;
@@ -212,4 +216,37 @@ public class NativeSshFileNio extends NativeSshFile {
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/d4a524e4/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 063ae17..fbc27cd 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
@@ -228,17 +228,22 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
protected static class FileHandle extends Handle {
+ int flags;
OutputStream output;
long outputPos;
InputStream input;
long inputPos;
long length;
- public FileHandle(SshFile sshFile) {
+ 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;
}
@@ -260,6 +265,12 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
}
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;
@@ -406,6 +417,14 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
try {
SshFile file = resolveFile(path);
if (file.doesExist()) {
+ if ((pflags & SSH_FXP_READ) != 0 && !file.isReadable()) {
+ sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not read " + path);
+ return;
+ }
+ if ((pflags & SSH_FXP_WRITE) != 0 && !file.isWritable()) {
+ 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;
@@ -416,19 +435,27 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path);
return;
}
- file.create();
+ if (!file.create()) {
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, "No such file " + path);
+ return;
+ }
+ } else {
+ sendStatus(id, SSH_FX_NO_SUCH_FILE, "No such file " + path);
+ return;
}
}
- String acc = ((pflags & (SSH_FXF_READ | SSH_FXF_WRITE)) != 0 ? "r" : "") +
- ((pflags & SSH_FXF_WRITE) != 0 ? "w" : "");
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);
}
String handle = UUID.randomUUID().toString();
- handles.put(handle, new FileHandle(file));
+ handles.put(handle, new FileHandle(file, pflags));
sendHandle(id, handle);
} catch (IOException e) {
sendStatus(id, SSH_FX_FAILURE, e.getMessage() == null ? "" : e.getMessage());
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d4a524e4/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 62c72a5..78aa49b 100644
--- a/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
@@ -21,6 +21,8 @@ package org.apache.sshd;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.IOError;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
@@ -52,6 +54,7 @@ import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
public class SftpTest extends BaseTest {
@@ -93,6 +96,80 @@ public class SftpTest extends BaseTest {
}
@Test
+ public void testOpen() throws Exception {
+ SshClient client = SshClient.setUpDefaultClient();
+ client.start();
+ ClientSession session = client.connect("x", "localhost", port).await().getSession();
+ session.addPasswordIdentity("x");
+ session.auth().verify();
+
+ String file = "target/sftp/client/test.txt";
+
+ new File(file).getParentFile().mkdirs();
+ new File(file).createNewFile();
+ new File(file).setWritable(false, false);
+ new File(file).setReadable(false, false);
+
+ SftpClient sftp = session.createSftpClient();
+ SftpClient.Handle h;
+
+ try {
+ sftp.open(file, EnumSet.of(SftpClient.OpenMode.Read));
+ fail("Should have failed");
+ } catch (IOException e) {
+ // ok
+ }
+
+ try {
+ sftp.open(file, EnumSet.of(SftpClient.OpenMode.Write));
+ fail("Should have failed");
+ } catch (IOException e) {
+ // ok
+ }
+
+ try {
+ sftp.open(file, EnumSet.of(SftpClient.OpenMode.Truncate));
+ fail("Should have failed");
+ } catch (IOException e) {
+ // ok
+ }
+
+ Assert.assertEquals(0, (sftp.stat(file).perms & (SftpClient.S_IWUSR | SftpClient.S_IRUSR)));
+
+ new File(file).setWritable(true, false);
+
+ h = sftp.open(file, EnumSet.of(SftpClient.OpenMode.Truncate, SftpClient.OpenMode.Write));
+ sftp.close(h);
+
+ h = sftp.open(file, EnumSet.of(SftpClient.OpenMode.Write));
+ byte[] d = "0123456789\n".getBytes();
+ sftp.write(h, 0, d, 0, d.length);
+ sftp.write(h, d.length, d, 0, d.length);
+ sftp.close(h);
+ h = sftp.open(file, EnumSet.of(SftpClient.OpenMode.Write));
+ sftp.write(h, d.length * 2, d, 0, d.length);
+ sftp.close(h);
+ h = sftp.open(file, EnumSet.of(SftpClient.OpenMode.Write));
+ sftp.write(h, 3, "-".getBytes(), 0, 1);
+ sftp.close(h);
+
+ try {
+ sftp.open(file, EnumSet.of(SftpClient.OpenMode.Read));
+ fail("Should have failed");
+ } catch (IOException e) {
+ // ok
+ }
+
+ new File(file).setReadable(true, false);
+
+ h = sftp.open(file, EnumSet.of(SftpClient.OpenMode.Read));
+ byte[] buf = new byte[3];
+ int l = sftp.read(h, 2l, buf, 0, 3);
+ assertEquals("2-4", new String(buf, 0, l));
+ sftp.close(h);
+ }
+
+ @Test
public void testClient() throws Exception {
SshClient client = SshClient.setUpDefaultClient();
client.start();
@@ -109,7 +186,7 @@ public class SftpTest extends BaseTest {
sftp.mkdir("target/sftp/client");
- SftpClient.Handle h = sftp.open("target/sftp/client/test.txt", EnumSet.of(SftpClient.OpenMode.Write));
+ SftpClient.Handle h = sftp.open("target/sftp/client/test.txt", EnumSet.of(SftpClient.OpenMode.Write, SftpClient.OpenMode.Create));
byte[] d = "0123456789\n".getBytes();
sftp.write(h, 0, d, 0, d.length);
sftp.write(h, d.length, d, 0, d.length);
@@ -208,7 +285,7 @@ public class SftpTest extends BaseTest {
c.disconnect();
assertTrue(target.exists());
- assertEquals("01234a", readFile(unixPath));
+ assertEquals("01234a6789", readFile(unixPath));
target.delete();
assertFalse(target.exists());