This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-vfs.git
commit f0fa4dbc810acdecea55c785ab4c7d47c3fef150 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Thu Jul 15 16:12:11 2021 -0400 Sort members. --- .../commons/vfs2/provider/sftp/SftpFileObject.java | 528 ++++++++++----------- 1 file changed, 264 insertions(+), 264 deletions(-) diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileObject.java index 2bc3489..43c17be 100644 --- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileObject.java +++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileObject.java @@ -49,98 +49,61 @@ import com.jcraft.jsch.SftpException; */ public class SftpFileObject extends AbstractFileObject<SftpFileSystem> { - private static final long MOD_TIME_FACTOR = 1000L; - - private SftpATTRS attrs; - private final String relPath; - - private boolean inRefresh; - - protected SftpFileObject(final AbstractFileName name, final SftpFileSystem fileSystem) throws FileSystemException { - super(name, fileSystem); - relPath = UriParser.decode(fileSystem.getRootName().getRelativeName(name)); - } - - /** @since 2.0 */ - @Override - protected void doDetach() throws Exception { - attrs = null; - } - /** - * Determines the type of this file, returns null if the file does not exist. + * An InputStream that monitors for end-of-file. */ - @Override - protected FileType doGetType() throws Exception { - if (attrs == null) { - statSelf(); - } + private class SftpInputStream extends MonitorInputStream { + private final ChannelSftp channel; - if (attrs == null) { - return FileType.IMAGINARY; + public SftpInputStream(final ChannelSftp channel, final InputStream in) { + super(in); + this.channel = channel; } - if ((attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS) == 0) { - throw new FileSystemException("vfs.provider.sftp/unknown-permissions.error"); + public SftpInputStream(final ChannelSftp channel, final InputStream in, final int bufferSize) { + super(in, bufferSize); + this.channel = channel; } - if (attrs.isDir()) { - return FileType.FOLDER; + + /** + * Called after the stream has been closed. + */ + @Override + protected void onClose() throws IOException { + getAbstractFileSystem().putChannel(channel); } - return FileType.FILE; } /** - * Called when the type or content of this file changes. + * An OutputStream that wraps an sftp OutputStream, and closes the channel when the stream is closed. */ - @Override - protected void onChange() throws Exception { - statSelf(); - } + private class SftpOutputStream extends MonitorOutputStream { + private final ChannelSftp channel; - /** - * Fetches file attributes from server. - * - * @throws IOException if an error occurs. - */ - private void statSelf() throws IOException { - ChannelSftp channelSftp = null; - try { - channelSftp = getAbstractFileSystem().getChannel(); - setStat(channelSftp.stat(relPath)); - } catch (final SftpException e) { - try { - // maybe the channel has some problems, so recreate the channel and retry - if (e.id != ChannelSftp.SSH_FX_NO_SUCH_FILE) { - channelSftp.disconnect(); - channelSftp = getAbstractFileSystem().getChannel(); - setStat(channelSftp.stat(relPath)); - } else { - // Really does not exist - attrs = null; - } - } catch (final SftpException innerEx) { - // TODO - not strictly true, but jsch 0.1.2 does not give us - // enough info in the exception. Should be using: - // if ( e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE ) - // However, sometimes the exception has the correct id, and - // sometimes - // it does not. Need to look into why. + public SftpOutputStream(final ChannelSftp channel, final OutputStream out) { + super(out); + this.channel = channel; + } - // Does not exist - attrs = null; - } - } finally { - if (channelSftp != null) { - getAbstractFileSystem().putChannel(channelSftp); - } + /** + * Called after this stream is closed. + */ + @Override + protected void onClose() throws IOException { + getAbstractFileSystem().putChannel(channel); } } + private static final long MOD_TIME_FACTOR = 1000L; - /** - * Sets attrs from listChildrenResolved - */ - private void setStat(final SftpATTRS attrs) { - this.attrs = attrs; + private SftpATTRS attrs; + + private final String relPath; + + private boolean inRefresh; + + protected SftpFileObject(final AbstractFileName name, final SftpFileSystem fileSystem) throws FileSystemException { + super(name, fileSystem); + relPath = UriParser.decode(fileSystem.getRootName().getRelativeName(name)); } /** @@ -156,38 +119,6 @@ public class SftpFileObject extends AbstractFileObject<SftpFileSystem> { } } - @Override - protected long doGetLastModifiedTime() throws Exception { - if (attrs == null || (attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_ACMODTIME) == 0) { - throw new FileSystemException("vfs.provider.sftp/unknown-modtime.error"); - } - return attrs.getMTime() * MOD_TIME_FACTOR; - } - - /** - * Sets the last modified time of this file. Is only called if {@link #doGetType} does not return - * {@link FileType#IMAGINARY}. - * - * @param modtime is modification time in milliseconds. SFTP protocol can send times with nanosecond precision but - * at the moment jsch send them with second precision. - */ - @Override - protected boolean doSetLastModifiedTime(final long modtime) throws Exception { - final int newMTime = (int) (modtime / MOD_TIME_FACTOR); - attrs.setACMODTIME(attrs.getATime(), newMTime); - flushStat(); - return true; - } - - private void flushStat() throws IOException, SftpException { - final ChannelSftp channel = getAbstractFileSystem().getChannel(); - try { - channel.setStat(relPath, attrs); - } finally { - getAbstractFileSystem().putChannel(channel); - } - } - /** * Deletes the file. */ @@ -205,86 +136,117 @@ public class SftpFileObject extends AbstractFileObject<SftpFileSystem> { } } + /** @since 2.0 */ + @Override + protected void doDetach() throws Exception { + attrs = null; + } + /** - * Renames the file. + * Returns the size of the file content (in bytes). */ @Override - protected void doRename(final FileObject newFile) throws Exception { - final ChannelSftp channel = getAbstractFileSystem().getChannel(); - try { - final SftpFileObject newSftpFileObject = (SftpFileObject) FileObjectUtils.getAbstractFileObject(newFile); - channel.rename(relPath, newSftpFileObject.relPath); - } finally { - getAbstractFileSystem().putChannel(channel); + protected long doGetContentSize() throws Exception { + if (attrs == null || (attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_SIZE) == 0) { + throw new FileSystemException("vfs.provider.sftp/unknown-size.error"); } + return attrs.getSize(); } /** - * Returns the POSIX type permissions of the file. - * - * @param checkIds {@code true} if user and group ID should be checked (needed for some access rights checks) - * @return A PosixPermission object - * @throws Exception If an error occurs - * @since 2.1 + * Creates an input stream to read the file content from. */ - protected PosixPermissions getPermissions(final boolean checkIds) throws Exception { - statSelf(); - boolean isInGroup = false; - if (checkIds) { - if(getAbstractFileSystem().isExecDisabled()) { - // Exec is disabled, so we won't be able to ascertain the current user's UID and GID. - // Return "always-true" permissions as a workaround, knowing that the SFTP server won't - // let us perform unauthorized actions anyway. - return new UserIsOwnerPosixPermissions(attrs.getPermissions()); - } + @SuppressWarnings("resource") + @Override + protected InputStream doGetInputStream(final int bufferSize) throws Exception { + // VFS-113: avoid npe + synchronized (getAbstractFileSystem()) { + final ChannelSftp channel = getAbstractFileSystem().getChannel(); + try { + // return channel.get(getName().getPath()); + // hmmm - using the in memory method is soooo much faster ... - for (final int groupId : getAbstractFileSystem().getGroupsIds()) { - if (groupId == attrs.getGId()) { - isInGroup = true; - break; + // TODO - Don't read the entire file into memory. Use the + // stream-based methods on ChannelSftp once they work properly + + /* + * final ByteArrayOutputStream outstr = new ByteArrayOutputStream(); channel.get(relPath, outstr); + * outstr.close(); return new ByteArrayInputStream(outstr.toByteArray()); + */ + + final InputStream inputStream; + try { + // VFS-210: sftp allows to gather an input stream even from a directory and will + // fail on first read. So we need to check the type anyway + if (!getType().hasContent()) { + throw new FileSystemException("vfs.provider/read-not-file.error", getName()); + } + + inputStream = channel.get(relPath); + } catch (final SftpException e) { + if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { + throw new FileNotFoundException(getName()); + } + + throw new FileSystemException(e); } + + return new SftpInputStream(channel, inputStream, bufferSize); + + } finally { + // getAbstractFileSystem().putChannel(channel); } } - final boolean isOwner = checkIds && attrs.getUId() == getAbstractFileSystem().getUId(); - return new PosixPermissions(attrs.getPermissions(), isOwner, isInGroup); } @Override - protected boolean doIsReadable() throws Exception { - return getPermissions(true).isReadable(); + protected long doGetLastModifiedTime() throws Exception { + if (attrs == null || (attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_ACMODTIME) == 0) { + throw new FileSystemException("vfs.provider.sftp/unknown-modtime.error"); + } + return attrs.getMTime() * MOD_TIME_FACTOR; } + /** + * Creates an output stream to write the file content to. + */ @Override - protected boolean doSetReadable(final boolean readable, final boolean ownerOnly) throws Exception { - final PosixPermissions permissions = getPermissions(false); - final int newPermissions = permissions.makeReadable(readable, ownerOnly); - if (newPermissions == permissions.getPermissions()) { - return true; - } - - attrs.setPERMISSIONS(newPermissions); - flushStat(); + protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception { + // TODO - Don't write the entire file into memory. Use the stream-based + // methods on ChannelSftp once the work properly + /* + * final ChannelSftp channel = getAbstractFileSystem().getChannel(); return new SftpOutputStream(channel); + */ - return true; + final ChannelSftp channel = getAbstractFileSystem().getChannel(); + return new SftpOutputStream(channel, channel.put(relPath, bAppend ? ChannelSftp.APPEND : ChannelSftp.OVERWRITE)); } @Override - protected boolean doIsWriteable() throws Exception { - return getPermissions(true).isWritable(); + protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception { + return new SftpRandomAccessContent(this, mode); } + /** + * Determines the type of this file, returns null if the file does not exist. + */ @Override - protected boolean doSetWritable(final boolean writable, final boolean ownerOnly) throws Exception { - final PosixPermissions permissions = getPermissions(false); - final int newPermissions = permissions.makeWritable(writable, ownerOnly); - if (newPermissions == permissions.getPermissions()) { - return true; + protected FileType doGetType() throws Exception { + if (attrs == null) { + statSelf(); + } + + if (attrs == null) { + return FileType.IMAGINARY; } - attrs.setPERMISSIONS(newPermissions); - flushStat(); - - return true; + if ((attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS) == 0) { + throw new FileSystemException("vfs.provider.sftp/unknown-permissions.error"); + } + if (attrs.isDir()) { + return FileType.FOLDER; + } + return FileType.FILE; } @Override @@ -293,17 +255,22 @@ public class SftpFileObject extends AbstractFileObject<SftpFileSystem> { } @Override - protected boolean doSetExecutable(final boolean executable, final boolean ownerOnly) throws Exception { - final PosixPermissions permissions = getPermissions(false); - final int newPermissions = permissions.makeExecutable(executable, ownerOnly); - if (newPermissions == permissions.getPermissions()) { - return true; - } + protected boolean doIsReadable() throws Exception { + return getPermissions(true).isReadable(); + } - attrs.setPERMISSIONS(newPermissions); - flushStat(); + @Override + protected boolean doIsWriteable() throws Exception { + return getPermissions(true).isWritable(); + } - return true; + /** + * Lists the children of this file. + */ + @Override + protected String[] doListChildren() throws Exception { + // use doListChildrenResolved for performance + return null; } /** @@ -385,28 +352,83 @@ public class SftpFileObject extends AbstractFileObject<SftpFileSystem> { } /** - * Lists the children of this file. + * Renames the file. */ @Override - protected String[] doListChildren() throws Exception { - // use doListChildrenResolved for performance - return null; + protected void doRename(final FileObject newFile) throws Exception { + final ChannelSftp channel = getAbstractFileSystem().getChannel(); + try { + final SftpFileObject newSftpFileObject = (SftpFileObject) FileObjectUtils.getAbstractFileObject(newFile); + channel.rename(relPath, newSftpFileObject.relPath); + } finally { + getAbstractFileSystem().putChannel(channel); + } + } + + @Override + protected boolean doSetExecutable(final boolean executable, final boolean ownerOnly) throws Exception { + final PosixPermissions permissions = getPermissions(false); + final int newPermissions = permissions.makeExecutable(executable, ownerOnly); + if (newPermissions == permissions.getPermissions()) { + return true; + } + + attrs.setPERMISSIONS(newPermissions); + flushStat(); + + return true; } /** - * Returns the size of the file content (in bytes). + * Sets the last modified time of this file. Is only called if {@link #doGetType} does not return + * {@link FileType#IMAGINARY}. + * + * @param modtime is modification time in milliseconds. SFTP protocol can send times with nanosecond precision but + * at the moment jsch send them with second precision. */ @Override - protected long doGetContentSize() throws Exception { - if (attrs == null || (attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_SIZE) == 0) { - throw new FileSystemException("vfs.provider.sftp/unknown-size.error"); + protected boolean doSetLastModifiedTime(final long modtime) throws Exception { + final int newMTime = (int) (modtime / MOD_TIME_FACTOR); + attrs.setACMODTIME(attrs.getATime(), newMTime); + flushStat(); + return true; + } + + @Override + protected boolean doSetReadable(final boolean readable, final boolean ownerOnly) throws Exception { + final PosixPermissions permissions = getPermissions(false); + final int newPermissions = permissions.makeReadable(readable, ownerOnly); + if (newPermissions == permissions.getPermissions()) { + return true; } - return attrs.getSize(); + + attrs.setPERMISSIONS(newPermissions); + flushStat(); + + return true; } @Override - protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception { - return new SftpRandomAccessContent(this, mode); + protected boolean doSetWritable(final boolean writable, final boolean ownerOnly) throws Exception { + final PosixPermissions permissions = getPermissions(false); + final int newPermissions = permissions.makeWritable(writable, ownerOnly); + if (newPermissions == permissions.getPermissions()) { + return true; + } + + attrs.setPERMISSIONS(newPermissions); + flushStat(); + + return true; + } + + private void flushStat() throws IOException, SftpException { + final ChannelSftp channel = getAbstractFileSystem().getChannel(); + try { + channel.setStat(relPath, attrs); + } finally { + getAbstractFileSystem().putChannel(channel); + } } /** @@ -426,108 +448,86 @@ public class SftpFileObject extends AbstractFileObject<SftpFileSystem> { } /** - * Creates an input stream to read the file content from. + * Returns the POSIX type permissions of the file. + * + * @param checkIds {@code true} if user and group ID should be checked (needed for some access rights checks) + * @return A PosixPermission object + * @throws Exception If an error occurs + * @since 2.1 */ - @SuppressWarnings("resource") - @Override - protected InputStream doGetInputStream(final int bufferSize) throws Exception { - // VFS-113: avoid npe - synchronized (getAbstractFileSystem()) { - final ChannelSftp channel = getAbstractFileSystem().getChannel(); - try { - // return channel.get(getName().getPath()); - // hmmm - using the in memory method is soooo much faster ... - - // TODO - Don't read the entire file into memory. Use the - // stream-based methods on ChannelSftp once they work properly - - /* - * final ByteArrayOutputStream outstr = new ByteArrayOutputStream(); channel.get(relPath, outstr); - * outstr.close(); return new ByteArrayInputStream(outstr.toByteArray()); - */ - - final InputStream inputStream; - try { - // VFS-210: sftp allows to gather an input stream even from a directory and will - // fail on first read. So we need to check the type anyway - if (!getType().hasContent()) { - throw new FileSystemException("vfs.provider/read-not-file.error", getName()); - } - - inputStream = channel.get(relPath); - } catch (final SftpException e) { - if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { - throw new FileNotFoundException(getName()); - } + protected PosixPermissions getPermissions(final boolean checkIds) throws Exception { + statSelf(); + boolean isInGroup = false; + if (checkIds) { + if(getAbstractFileSystem().isExecDisabled()) { + // Exec is disabled, so we won't be able to ascertain the current user's UID and GID. + // Return "always-true" permissions as a workaround, knowing that the SFTP server won't + // let us perform unauthorized actions anyway. + return new UserIsOwnerPosixPermissions(attrs.getPermissions()); + } - throw new FileSystemException(e); + for (final int groupId : getAbstractFileSystem().getGroupsIds()) { + if (groupId == attrs.getGId()) { + isInGroup = true; + break; } - - return new SftpInputStream(channel, inputStream, bufferSize); - - } finally { - // getAbstractFileSystem().putChannel(channel); } } + final boolean isOwner = checkIds && attrs.getUId() == getAbstractFileSystem().getUId(); + return new PosixPermissions(attrs.getPermissions(), isOwner, isInGroup); } /** - * Creates an output stream to write the file content to. + * Called when the type or content of this file changes. */ @Override - protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception { - // TODO - Don't write the entire file into memory. Use the stream-based - // methods on ChannelSftp once the work properly - /* - * final ChannelSftp channel = getAbstractFileSystem().getChannel(); return new SftpOutputStream(channel); - */ - - final ChannelSftp channel = getAbstractFileSystem().getChannel(); - return new SftpOutputStream(channel, channel.put(relPath, bAppend ? ChannelSftp.APPEND : ChannelSftp.OVERWRITE)); + protected void onChange() throws Exception { + statSelf(); } /** - * An InputStream that monitors for end-of-file. + * Sets attrs from listChildrenResolved */ - private class SftpInputStream extends MonitorInputStream { - private final ChannelSftp channel; - - public SftpInputStream(final ChannelSftp channel, final InputStream in) { - super(in); - this.channel = channel; - } - - public SftpInputStream(final ChannelSftp channel, final InputStream in, final int bufferSize) { - super(in, bufferSize); - this.channel = channel; - } - - /** - * Called after the stream has been closed. - */ - @Override - protected void onClose() throws IOException { - getAbstractFileSystem().putChannel(channel); - } + private void setStat(final SftpATTRS attrs) { + this.attrs = attrs; } /** - * An OutputStream that wraps an sftp OutputStream, and closes the channel when the stream is closed. + * Fetches file attributes from server. + * + * @throws IOException if an error occurs. */ - private class SftpOutputStream extends MonitorOutputStream { - private final ChannelSftp channel; - - public SftpOutputStream(final ChannelSftp channel, final OutputStream out) { - super(out); - this.channel = channel; - } + private void statSelf() throws IOException { + ChannelSftp channelSftp = null; + try { + channelSftp = getAbstractFileSystem().getChannel(); + setStat(channelSftp.stat(relPath)); + } catch (final SftpException e) { + try { + // maybe the channel has some problems, so recreate the channel and retry + if (e.id != ChannelSftp.SSH_FX_NO_SUCH_FILE) { + channelSftp.disconnect(); + channelSftp = getAbstractFileSystem().getChannel(); + setStat(channelSftp.stat(relPath)); + } else { + // Really does not exist + attrs = null; + } + } catch (final SftpException innerEx) { + // TODO - not strictly true, but jsch 0.1.2 does not give us + // enough info in the exception. Should be using: + // if ( e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE ) + // However, sometimes the exception has the correct id, and + // sometimes + // it does not. Need to look into why. - /** - * Called after this stream is closed. - */ - @Override - protected void onClose() throws IOException { - getAbstractFileSystem().putChannel(channel); + // Does not exist + attrs = null; + } + } finally { + if (channelSftp != null) { + getAbstractFileSystem().putChannel(channelSftp); + } } }