http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e3b8acd7/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java deleted file mode 100644 index f8cd75b..0000000 --- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java +++ /dev/null @@ -1,1312 +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.client.subsystem.sftp; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.channels.FileChannel; -import java.nio.charset.Charset; -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.FileSystemException; -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.StandardCopyOption; -import java.nio.file.attribute.AclEntry; -import java.nio.file.attribute.AclFileAttributeView; -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.FileOwnerAttributeView; -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.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.NavigableMap; -import java.util.Objects; -import java.util.Set; -import java.util.TreeMap; -import java.util.concurrent.TimeUnit; - -import org.apache.sshd.client.SshClient; -import org.apache.sshd.client.session.ClientSession; -import org.apache.sshd.client.subsystem.sftp.SftpClient.Attributes; -import org.apache.sshd.common.PropertyResolver; -import org.apache.sshd.common.PropertyResolverUtils; -import org.apache.sshd.common.SshConstants; -import org.apache.sshd.common.SshException; -import org.apache.sshd.common.auth.BasicCredentialsImpl; -import org.apache.sshd.common.auth.BasicCredentialsProvider; -import org.apache.sshd.common.auth.MutableBasicCredentials; -import org.apache.sshd.common.io.IoSession; -import org.apache.sshd.common.subsystem.sftp.SftpConstants; -import org.apache.sshd.common.subsystem.sftp.SftpException; -import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.NumberUtils; -import org.apache.sshd.common.util.ValidateUtils; -import org.apache.sshd.common.util.io.IoUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A registered {@link FileSystemProvider} that registers the "sftp://" - * scheme so that URLs with this protocol are handled as remote SFTP {@link Path}-s - * - e.g., "{@code sftp://user:password@host/remote/file/path}" - * - * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> - */ -public class SftpFileSystemProvider extends FileSystemProvider { - public static final String READ_BUFFER_PROP_NAME = "sftp-fs-read-buffer-size"; - public static final int DEFAULT_READ_BUFFER_SIZE = SftpClient.DEFAULT_READ_BUFFER_SIZE; - public static final String WRITE_BUFFER_PROP_NAME = "sftp-fs-write-buffer-size"; - public static final int DEFAULT_WRITE_BUFFER_SIZE = SftpClient.DEFAULT_WRITE_BUFFER_SIZE; - public static final String CONNECT_TIME_PROP_NAME = "sftp-fs-connect-time"; - public static final long DEFAULT_CONNECT_TIME = SftpClient.DEFAULT_WAIT_TIMEOUT; - public static final String AUTH_TIME_PROP_NAME = "sftp-fs-auth-time"; - public static final long DEFAULT_AUTH_TIME = SftpClient.DEFAULT_WAIT_TIMEOUT; - public static final String NAME_DECORDER_CHARSET_PROP_NAME = "sftp-fs-name-decoder-charset"; - public static final Charset DEFAULT_NAME_DECODER_CHARSET = SftpClient.DEFAULT_NAME_DECODING_CHARSET; - - /** - * <P> - * URI parameter that can be used to specify a special version selection. Options are: - * </P> - * <UL> - * <LI>{@code max} - select maximum available version for the client</LI> - * <LI>{@code min} - select minimum available version for the client</LI> - * <LI>{@code current} - whatever version is reported by the server</LI> - * <LI>{@code nnn} - select <U>only</U> the specified version</LI> - * <LI>{@code a,b,c} - select one of the specified versions (if available) in preference order</LI> - * </UL> - */ - public static final String VERSION_PARAM = "version"; - - public static final Set<Class<? extends FileAttributeView>> UNIVERSAL_SUPPORTED_VIEWS = - Collections.unmodifiableSet( - GenericUtils.asSet( - PosixFileAttributeView.class, - FileOwnerAttributeView.class, - BasicFileAttributeView.class - )); - - protected final Logger log; - - private final SshClient client; - private final SftpClientFactory factory; - private final SftpVersionSelector selector; - private final NavigableMap<String, SftpFileSystem> fileSystems = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - - public SftpFileSystemProvider() { - this((SshClient) null); - } - - public SftpFileSystemProvider(SftpVersionSelector selector) { - this(null, selector); - } - - /** - * @param client The {@link SshClient} to use - if {@code null} then a - * default one will be setup and started. Otherwise, it is assumed that - * the client has already been started - * @see SshClient#setUpDefaultClient() - */ - public SftpFileSystemProvider(SshClient client) { - this(client, SftpVersionSelector.CURRENT); - } - - public SftpFileSystemProvider(SshClient client, SftpVersionSelector selector) { - this(client, null, selector); - } - - public SftpFileSystemProvider(SshClient client, SftpClientFactory factory, SftpVersionSelector selector) { - this.log = LoggerFactory.getLogger(getClass()); - this.factory = factory; - this.selector = selector; - if (client == null) { - // TODO: make this configurable using system properties - client = SshClient.setUpDefaultClient(); - client.start(); - } - this.client = client; - } - - @Override - public String getScheme() { - return SftpConstants.SFTP_SUBSYSTEM_NAME; - } - - public final SftpVersionSelector getSftpVersionSelector() { - return selector; - } - - @Override // NOTE: co-variant return - public SftpFileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException { - String host = ValidateUtils.checkNotNullAndNotEmpty(uri.getHost(), "Host not provided"); - int port = uri.getPort(); - if (port <= 0) { - port = SshConstants.DEFAULT_PORT; - } - - BasicCredentialsProvider credentials = parseCredentials(uri); - ValidateUtils.checkState(credentials != null, "No credentials provided"); - String username = credentials.getUsername(); - String id = getFileSystemIdentifier(host, port, username); - Map<String, Object> params = resolveFileSystemParameters(env, parseURIParameters(uri)); - PropertyResolver resolver = PropertyResolverUtils.toPropertyResolver(params); - SftpVersionSelector selector = resolveSftpVersionSelector(uri, getSftpVersionSelector(), resolver); - Charset decodingCharset = - PropertyResolverUtils.getCharset(resolver, NAME_DECORDER_CHARSET_PROP_NAME, DEFAULT_NAME_DECODER_CHARSET); - long maxConnectTime = resolver.getLongProperty(CONNECT_TIME_PROP_NAME, DEFAULT_CONNECT_TIME); - long maxAuthTime = resolver.getLongProperty(AUTH_TIME_PROP_NAME, DEFAULT_AUTH_TIME); - String password = credentials.getPassword(); - - SftpFileSystem fileSystem; - synchronized (fileSystems) { - if (fileSystems.containsKey(id)) { - throw new FileSystemAlreadyExistsException(id); - } - - // TODO try and find a way to avoid doing this while locking the file systems cache - ClientSession session = null; - try { - session = client.connect(username, host, port) - .verify(maxConnectTime) - .getSession(); - if (GenericUtils.size(params) > 0) { - // Cannot use forEach because the session is not effectively final - for (Map.Entry<String, ?> pe : params.entrySet()) { - String key = pe.getKey(); - Object value = pe.getValue(); - if (VERSION_PARAM.equalsIgnoreCase(key)) { - continue; - } - - PropertyResolverUtils.updateProperty(session, key, value); - } - - PropertyResolverUtils.updateProperty(session, SftpClient.NAME_DECODING_CHARSET, decodingCharset); - } - - session.addPasswordIdentity(password); - session.auth().verify(maxAuthTime); - - fileSystem = new SftpFileSystem(this, id, session, factory, selector); - fileSystems.put(id, fileSystem); - } catch (Exception e) { - if (session != null) { - try { - session.close(); - } catch (IOException t) { - if (log.isDebugEnabled()) { - log.debug("Failed (" + t.getClass().getSimpleName() + ")" - + " to close session for new file system on " + host + ":" + port - + " due to " + e.getClass().getSimpleName() + "[" + e.getMessage() + "]" - + ": " + t.getMessage()); - } - } - } - - if (e instanceof IOException) { - throw (IOException) e; - } else if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } else { - throw new IOException(e); - } - } - } - - fileSystem.setReadBufferSize(resolver.getIntProperty(READ_BUFFER_PROP_NAME, DEFAULT_READ_BUFFER_SIZE)); - fileSystem.setWriteBufferSize(resolver.getIntProperty(WRITE_BUFFER_PROP_NAME, DEFAULT_WRITE_BUFFER_SIZE)); - if (log.isDebugEnabled()) { - log.debug("newFileSystem({}): {}", uri.toASCIIString(), fileSystem); - } - return fileSystem; - } - - protected SftpVersionSelector resolveSftpVersionSelector(URI uri, SftpVersionSelector defaultSelector, PropertyResolver resolver) { - String preference = resolver.getString(VERSION_PARAM); - if (GenericUtils.isEmpty(preference)) { - return defaultSelector; - } - - if (log.isDebugEnabled()) { - log.debug("resolveSftpVersionSelector({}) preference={}", uri, preference); - } - - if ("max".equalsIgnoreCase(preference)) { - return SftpVersionSelector.MAXIMUM; - } else if ("min".equalsIgnoreCase(preference)) { - return SftpVersionSelector.MINIMUM; - } else if ("current".equalsIgnoreCase(preference)) { - return SftpVersionSelector.CURRENT; - } - - String[] values = GenericUtils.split(preference, ','); - if (values.length == 1) { - return SftpVersionSelector.fixedVersionSelector(Integer.parseInt(values[0])); - } - - int[] preferred = new int[values.length]; - for (int index = 0; index < values.length; index++) { - preferred[index] = Integer.parseInt(values[index]); - } - - return SftpVersionSelector.preferredVersionSelector(preferred); - } - - // NOTE: URI parameters override environment ones - public static Map<String, Object> resolveFileSystemParameters(Map<String, ?> env, Map<String, Object> uriParams) { - if (GenericUtils.isEmpty(env)) { - return GenericUtils.isEmpty(uriParams) ? Collections.emptyMap() : uriParams; - } else if (GenericUtils.isEmpty(uriParams)) { - return Collections.unmodifiableMap(env); - } - - Map<String, Object> resolved = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - resolved.putAll(env); - resolved.putAll(uriParams); - return resolved; - } - - /** - * Attempts to parse the user information from the URI - * - * @param uri The {@link URI} value - ignored if {@code null} or does not - * contain any {@link URI#getUserInfo() user info}. - * @return The parsed credentials - {@code null} if none available - */ - public static MutableBasicCredentials parseCredentials(URI uri) { - return parseCredentials((uri == null) ? "" : uri.getUserInfo()); - } - - public static MutableBasicCredentials parseCredentials(String userInfo) { - if (GenericUtils.isEmpty(userInfo)) { - return null; - } - - String[] ui = GenericUtils.split(userInfo, ':'); - ValidateUtils.checkTrue(GenericUtils.length(ui) == 2, "Invalid user info: %s", userInfo); - return new BasicCredentialsImpl(ui[0], ui[1]); - } - - public static Map<String, Object> parseURIParameters(URI uri) { - return parseURIParameters((uri == null) ? "" : uri.getQuery()); - } - - public static Map<String, Object> parseURIParameters(String params) { - if (GenericUtils.isEmpty(params)) { - return Collections.emptyMap(); - } - - if (params.charAt(0) == '?') { - if (params.length() == 1) { - return Collections.emptyMap(); - } - params = params.substring(1); - } - - String[] pairs = GenericUtils.split(params, '&'); - Map<String, Object> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - for (String p : pairs) { - int pos = p.indexOf('='); - if (pos < 0) { - map.put(p, Boolean.TRUE); - continue; - } - - String key = p.substring(0, pos); - String value = p.substring(pos + 1); - if (NumberUtils.isIntegerNumber(value)) { - map.put(key, Long.valueOf(value)); - } else if ("true".equals(value) || "false".equals("value")) { - map.put(key, Boolean.valueOf(value)); - } else { - map.put(key, value); - } - } - - return map; - } - - public SftpFileSystem newFileSystem(ClientSession session) throws IOException { - String id = getFileSystemIdentifier(session); - SftpFileSystem fileSystem; - synchronized (fileSystems) { - if (fileSystems.containsKey(id)) { - throw new FileSystemAlreadyExistsException(id); - } - fileSystem = new SftpFileSystem(this, id, session, factory, getSftpVersionSelector()); - fileSystems.put(id, fileSystem); - } - - fileSystem.setReadBufferSize(session.getIntProperty(READ_BUFFER_PROP_NAME, DEFAULT_READ_BUFFER_SIZE)); - fileSystem.setWriteBufferSize(session.getIntProperty(WRITE_BUFFER_PROP_NAME, DEFAULT_WRITE_BUFFER_SIZE)); - if (log.isDebugEnabled()) { - log.debug("newFileSystem: {}", fileSystem); - } - - return fileSystem; - } - - @Override - public FileSystem getFileSystem(URI uri) { - String id = getFileSystemIdentifier(uri); - SftpFileSystem fs = getFileSystem(id); - if (fs == null) { - throw new FileSystemNotFoundException(id); - } - return fs; - } - - /** - * @param id File system identifier - ignored if {@code null}/empty - * @return The removed {@link SftpFileSystem} - {@code null} if no match - */ - public SftpFileSystem removeFileSystem(String id) { - if (GenericUtils.isEmpty(id)) { - return null; - } - - SftpFileSystem removed; - synchronized (fileSystems) { - removed = fileSystems.remove(id); - } - - if (log.isDebugEnabled()) { - log.debug("removeFileSystem({}): {}", id, removed); - } - return removed; - } - - /** - * @param id File system identifier - ignored if {@code null}/empty - * @return The cached {@link SftpFileSystem} - {@code null} if no match - */ - public SftpFileSystem getFileSystem(String id) { - if (GenericUtils.isEmpty(id)) { - return null; - } - - synchronized (fileSystems) { - return fileSystems.get(id); - } - } - - @Override - public Path getPath(URI uri) { - FileSystem fs = getFileSystem(uri); - return fs.getPath(uri.getPath()); - } - - @Override - public FileChannel 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 { - Collection<SftpClient.OpenMode> modes = SftpClient.OpenMode.fromOpenOptions(options); - if (modes.isEmpty()) { - modes = EnumSet.of(SftpClient.OpenMode.Read, SftpClient.OpenMode.Write); - } - // TODO: process file attributes - return new SftpFileSystemChannel(toSftpPath(path), modes); - } - - @Override - public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException { - final SftpPath p = toSftpPath(dir); - return new SftpDirectoryStream(p); - } - - @Override - public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException { - SftpPath p = toSftpPath(dir); - SftpFileSystem fs = p.getFileSystem(); - if (log.isDebugEnabled()) { - log.debug("createDirectory({}) {} ({})", fs, dir, Arrays.asList(attrs)); - } - try (SftpClient sftp = fs.getClient()) { - try { - sftp.mkdir(dir.toString()); - } catch (SftpException e) { - int sftpStatus = e.getStatus(); - if ((sftp.getVersion() == SftpConstants.SFTP_V3) && (sftpStatus == SftpConstants.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 (sftpStatus == SftpConstants.SSH_FX_FILE_ALREADY_EXISTS) { - throw new FileAlreadyExistsException(p.toString()); - } - throw e; - } - for (FileAttribute<?> attr : attrs) { - setAttribute(p, attr.name(), attr.value()); - } - } - } - - @Override - public void delete(Path path) throws IOException { - SftpPath p = toSftpPath(path); - checkAccess(p, AccessMode.WRITE); - - SftpFileSystem fs = p.getFileSystem(); - if (log.isDebugEnabled()) { - log.debug("delete({}) {}", fs, path); - } - - try (SftpClient sftp = fs.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 { - SftpPath src = toSftpPath(source); - SftpPath dst = toSftpPath(target); - if (src.getFileSystem() != dst.getFileSystem()) { - throw new ProviderMismatchException("Mismatched file system providers for " + src + " vs. " + dst); - } - 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 = IoUtils.getLinkOptions(!noFollowLinks); - - // 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 - Boolean status = IoUtils.checkFileExists(target, linkOptions); - if (status == null) { - throw new AccessDeniedException("Existence cannot be determined for copy target: " + target); - } - - if (log.isDebugEnabled()) { - log.debug("copy({})[{}] {} => {}", src.getFileSystem(), Arrays.asList(options), src, dst); - } - - if (replaceExisting) { - deleteIfExists(target); - } else { - if (status) { - 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 { - SftpPath src = toSftpPath(source); - SftpFileSystem fsSrc = src.getFileSystem(); - SftpPath dst = toSftpPath(target); - - if (src.getFileSystem() != dst.getFileSystem()) { - throw new ProviderMismatchException("Mismatched file system providers for " + src + " vs. " + dst); - } - 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 = IoUtils.getLinkOptions(noFollowLinks); - - // attributes of source file - BasicFileAttributes attrs = readAttributes(source, BasicFileAttributes.class, linkOptions); - if (attrs.isSymbolicLink()) { - throw new IOException("Moving of source symbolic link (" + source + ") to " + target + " not supported"); - } - - // delete target if it exists and REPLACE_EXISTING is specified - Boolean status = IoUtils.checkFileExists(target, linkOptions); - if (status == null) { - throw new AccessDeniedException("Existence cannot be determined for move target " + target); - } - - if (log.isDebugEnabled()) { - log.debug("move({})[{}] {} => {}", src.getFileSystem(), Arrays.asList(options), src, dst); - } - - if (replaceExisting) { - deleteIfExists(target); - } else if (status) { - throw new FileAlreadyExistsException(target.toString()); - } - - try (SftpClient sftp = fsSrc.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("Mismatched file system providers for " + p1 + " vs. " + p2); - } - 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 { - FileSystem fs = path.getFileSystem(); - if (!(fs instanceof SftpFileSystem)) { - throw new FileSystemException(path.toString(), path.toString(), "getFileStore(" + path + ") path not attached to an SFTP file system"); - } - - SftpFileSystem sftpFs = (SftpFileSystem) fs; - String id = sftpFs.getId(); - SftpFileSystem cached = getFileSystem(id); - if (cached != sftpFs) { - throw new FileSystemException(path.toString(), path.toString(), "Mismatched file system instance for id=" + id); - } - - return sftpFs.getFileStores().get(0); - } - - @Override - public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs) throws IOException { - SftpPath l = toSftpPath(link); - SftpFileSystem fsLink = l.getFileSystem(); - SftpPath t = toSftpPath(target); - if (fsLink != t.getFileSystem()) { - throw new ProviderMismatchException("Mismatched file system providers for " + l + " vs. " + t); - } - - if (log.isDebugEnabled()) { - log.debug("createSymbolicLink({})[{}] {} => {}", fsLink, Arrays.asList(attrs), link, target); - } - - try (SftpClient client = fsLink.getClient()) { - client.symLink(l.toString(), t.toString()); - } - } - - @Override - public Path readSymbolicLink(Path link) throws IOException { - SftpPath l = toSftpPath(link); - SftpFileSystem fsLink = l.getFileSystem(); - try (SftpClient client = fsLink.getClient()) { - String linkPath = client.readLink(l.toString()); - if (log.isDebugEnabled()) { - log.debug("readSymbolicLink({})[{}] {} => {}", fsLink, link, linkPath); - } - - return fsLink.getPath(linkPath); - } - } - - @Override - public void checkAccess(Path path, AccessMode... modes) throws IOException { - SftpPath p = toSftpPath(path); - boolean w = false; - boolean x = false; - if (GenericUtils.length(modes) > 0) { - for (AccessMode mode : modes) { - switch (mode) { - case READ: - break; - case WRITE: - w = true; - break; - case EXECUTE: - x = true; - break; - default: - throw new UnsupportedOperationException("Unsupported mode: " + mode); - } - } - } - - BasicFileAttributes attrs = getFileAttributeView(p, BasicFileAttributeView.class).readAttributes(); - if ((attrs == null) && !(p.isAbsolute() && p.getNameCount() == 0)) { - throw new NoSuchFileException(path.toString()); - } - - SftpFileSystem fs = p.getFileSystem(); - if (x || (w && fs.isReadOnly())) { - throw new AccessDeniedException("Filesystem is read-only: " + path.toString()); - } - } - - @Override - public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, final LinkOption... options) { - if (isSupportedFileAttributeView(path, type)) { - if (AclFileAttributeView.class.isAssignableFrom(type)) { - return type.cast(new SftpAclFileAttributeView(this, path, options)); - } else if (BasicFileAttributeView.class.isAssignableFrom(type)) { - return type.cast(new SftpPosixFileAttributeView(this, path, options)); - } - } - - throw new UnsupportedOperationException("getFileAttributeView(" + path + ") view not supported: " + type.getSimpleName()); - } - - public boolean isSupportedFileAttributeView(Path path, Class<? extends FileAttributeView> type) { - return isSupportedFileAttributeView(toSftpPath(path).getFileSystem(), type); - } - - public boolean isSupportedFileAttributeView(SftpFileSystem fs, Class<? extends FileAttributeView> type) { - Collection<String> views = fs.supportedFileAttributeViews(); - if ((type == null) || GenericUtils.isEmpty(views)) { - return false; - } else if (PosixFileAttributeView.class.isAssignableFrom(type)) { - return views.contains("posix"); - } else if (AclFileAttributeView.class.isAssignableFrom(type)) { - return views.contains("acl"); // must come before owner view - } else if (FileOwnerAttributeView.class.isAssignableFrom(type)) { - return views.contains("owner"); - } else if (BasicFileAttributeView.class.isAssignableFrom(type)) { - return views.contains("basic"); // must be last - } else { - return false; - } - } - - @Override - public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options) throws IOException { - if (type.isAssignableFrom(PosixFileAttributes.class)) { - return type.cast(getFileAttributeView(path, PosixFileAttributeView.class, options).readAttributes()); - } - - throw new UnsupportedOperationException("readAttributes(" + path + ")[" + type.getSimpleName() + "] N/A"); - } - - @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); - } - - return readAttributes(path, view, attrs, options); - } - - public Map<String, Object> readAttributes(Path path, String view, String attrs, LinkOption... options) throws IOException { - SftpPath p = toSftpPath(path); - SftpFileSystem fs = p.getFileSystem(); - Collection<String> views = fs.supportedFileAttributeViews(); - if (GenericUtils.isEmpty(views) || (!views.contains(view))) { - throw new UnsupportedOperationException("readAttributes(" + path + ")[" + view + ":" + attrs + "] view not supported: " + views); - } - - if ("basic".equalsIgnoreCase(view) || "posix".equalsIgnoreCase(view) || "owner".equalsIgnoreCase(view)) { - return readPosixViewAttributes(p, view, attrs, options); - } else if ("acl".equalsIgnoreCase(view)) { - return readAclViewAttributes(p, view, attrs, options); - } else { - return readCustomViewAttributes(p, view, attrs, options); - } - } - - protected Map<String, Object> readCustomViewAttributes(SftpPath path, String view, String attrs, LinkOption... options) throws IOException { - throw new UnsupportedOperationException("readCustomViewAttributes(" + path + ")[" + view + ":" + attrs + "] view not supported"); - } - - protected NavigableMap<String, Object> readAclViewAttributes(SftpPath path, String view, String attrs, LinkOption... options) throws IOException { - if ("*".equals(attrs)) { - attrs = "acl,owner"; - } - - SftpFileSystem fs = path.getFileSystem(); - SftpClient.Attributes attributes; - try (SftpClient client = fs.getClient()) { - attributes = readRemoteAttributes(path, options); - } - - NavigableMap<String, Object> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - String[] attrValues = GenericUtils.split(attrs, ','); - boolean traceEnabled = log.isTraceEnabled(); - for (String attr : attrValues) { - switch (attr) { - case "acl": - List<AclEntry> acl = attributes.getAcl(); - if (acl != null) { - map.put(attr, acl); - } - break; - case "owner": - String owner = attributes.getOwner(); - if (GenericUtils.length(owner) > 0) { - map.put(attr, new SftpFileSystem.DefaultUserPrincipal(owner)); - } - break; - default: - if (traceEnabled) { - log.trace("readAclViewAttributes({})[{}] unknown attribute: {}", fs, attrs, attr); - } - } - } - - return map; - } - - public SftpClient.Attributes readRemoteAttributes(SftpPath path, LinkOption... options) throws IOException { - SftpFileSystem fs = path.getFileSystem(); - try (SftpClient client = fs.getClient()) { - try { - SftpClient.Attributes attrs; - if (IoUtils.followLinks(options)) { - attrs = client.stat(path.toString()); - } else { - attrs = client.lstat(path.toString()); - } - if (log.isTraceEnabled()) { - log.trace("readRemoteAttributes({})[{}]: {}", fs, path, attrs); - } - return attrs; - } catch (SftpException e) { - if (e.getStatus() == SftpConstants.SSH_FX_NO_SUCH_FILE) { - throw new NoSuchFileException(path.toString()); - } - throw e; - } - } - } - - protected NavigableMap<String, Object> readPosixViewAttributes( - SftpPath path, String view, String attrs, LinkOption... options) - throws IOException { - PosixFileAttributes v = readAttributes(path, PosixFileAttributes.class, options); - if ("*".equals(attrs)) { - attrs = "lastModifiedTime,lastAccessTime,creationTime,size,isRegularFile,isDirectory,isSymbolicLink,isOther,fileKey,owner,permissions,group"; - } - - NavigableMap<String, Object> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - boolean traceEnabled = log.isTraceEnabled(); - String[] attrValues = GenericUtils.split(attrs, ','); - for (String attr : attrValues) { - 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; - default: - if (traceEnabled) { - log.trace("readPosixViewAttributes({})[{}:{}] ignored for {}", path, view, attr, attrs); - } - } - } - 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); - } - - setAttribute(path, view, attr, value, options); - } - - public void setAttribute(Path path, String view, String attr, Object value, LinkOption... options) throws IOException { - SftpPath p = toSftpPath(path); - SftpFileSystem fs = p.getFileSystem(); - Collection<String> views = fs.supportedFileAttributeViews(); - if (GenericUtils.isEmpty(views) || (!views.contains(view))) { - throw new UnsupportedOperationException("setAttribute(" + path + ")[" + view + ":" + attr + "=" + value + "] view " + view + " not supported: " + views); - } - - SftpClient.Attributes attributes = new SftpClient.Attributes(); - switch (attr) { - case "lastModifiedTime": - attributes.modifyTime((int) ((FileTime) value).to(TimeUnit.SECONDS)); - break; - case "lastAccessTime": - attributes.accessTime((int) ((FileTime) value).to(TimeUnit.SECONDS)); - break; - case "creationTime": - attributes.createTime((int) ((FileTime) value).to(TimeUnit.SECONDS)); - break; - case "size": - attributes.size(((Number) value).longValue()); - break; - case "permissions": { - @SuppressWarnings("unchecked") - Set<PosixFilePermission> attrSet = (Set<PosixFilePermission>) value; - attributes.perms(attributesToPermissions(path, attrSet)); - break; - } - case "owner": - attributes.owner(((UserPrincipal) value).getName()); - break; - case "group": - attributes.group(((GroupPrincipal) value).getName()); - break; - case "acl": { - ValidateUtils.checkTrue("acl".equalsIgnoreCase(view), "ACL cannot be set via view=%s", view); - @SuppressWarnings("unchecked") - List<AclEntry> acl = (List<AclEntry>) value; - attributes.acl(acl); - break; - } - case "isRegularFile": - case "isDirectory": - case "isSymbolicLink": - case "isOther": - case "fileKey": - throw new UnsupportedOperationException("setAttribute(" + path + ")[" + view + ":" + attr + "=" + value + "] modification N/A"); - default: - if (log.isTraceEnabled()) { - log.trace("setAttribute({})[{}] ignore {}:{}={}", fs, path, view, attr, value); - } - } - - if (log.isDebugEnabled()) { - log.debug("setAttribute({}) {}: {}", fs, path, attributes); - } - - try (SftpClient client = fs.getClient()) { - client.setStat(p.toString(), attributes); - } - } - - public SftpPath toSftpPath(Path path) { - Objects.requireNonNull(path, "No path provided"); - if (!(path instanceof SftpPath)) { - throw new ProviderMismatchException("Path is not SFTP: " + path); - } - return (SftpPath) path; - } - - protected int attributesToPermissions(Path path, Collection<PosixFilePermission> perms) { - if (GenericUtils.isEmpty(perms)) { - return 0; - } - - int pf = 0; - boolean traceEnabled = log.isTraceEnabled(); - for (PosixFilePermission p : perms) { - switch (p) { - case OWNER_READ: - pf |= SftpConstants.S_IRUSR; - break; - case OWNER_WRITE: - pf |= SftpConstants.S_IWUSR; - break; - case OWNER_EXECUTE: - pf |= SftpConstants.S_IXUSR; - break; - case GROUP_READ: - pf |= SftpConstants.S_IRGRP; - break; - case GROUP_WRITE: - pf |= SftpConstants.S_IWGRP; - break; - case GROUP_EXECUTE: - pf |= SftpConstants.S_IXGRP; - break; - case OTHERS_READ: - pf |= SftpConstants.S_IROTH; - break; - case OTHERS_WRITE: - pf |= SftpConstants.S_IWOTH; - break; - case OTHERS_EXECUTE: - pf |= SftpConstants.S_IXOTH; - break; - default: - if (traceEnabled) { - log.trace("attributesToPermissions(" + path + ") ignored " + p); - } - } - } - - return pf; - } - - public static String getRWXPermissions(int perms) { - StringBuilder sb = new StringBuilder(10 /* 3 * rwx + (d)irectory */); - if ((perms & SftpConstants.S_IFLNK) == SftpConstants.S_IFLNK) { - sb.append('l'); - } else if ((perms & SftpConstants.S_IFDIR) == SftpConstants.S_IFDIR) { - sb.append('d'); - } else { - sb.append('-'); - } - - if ((perms & SftpConstants.S_IRUSR) == SftpConstants.S_IRUSR) { - sb.append('r'); - } else { - sb.append('-'); - } - if ((perms & SftpConstants.S_IWUSR) == SftpConstants.S_IWUSR) { - sb.append('w'); - } else { - sb.append('-'); - } - if ((perms & SftpConstants.S_IXUSR) == SftpConstants.S_IXUSR) { - sb.append('x'); - } else { - sb.append('-'); - } - - if ((perms & SftpConstants.S_IRGRP) == SftpConstants.S_IRGRP) { - sb.append('r'); - } else { - sb.append('-'); - } - if ((perms & SftpConstants.S_IWGRP) == SftpConstants.S_IWGRP) { - sb.append('w'); - } else { - sb.append('-'); - } - if ((perms & SftpConstants.S_IXGRP) == SftpConstants.S_IXGRP) { - sb.append('x'); - } else { - sb.append('-'); - } - - if ((perms & SftpConstants.S_IROTH) == SftpConstants.S_IROTH) { - sb.append('r'); - } else { - sb.append('-'); - } - if ((perms & SftpConstants.S_IWOTH) == SftpConstants.S_IWOTH) { - sb.append('w'); - } else { - sb.append('-'); - } - if ((perms & SftpConstants.S_IXOTH) == SftpConstants.S_IXOTH) { - sb.append('x'); - } else { - sb.append('-'); - } - - return sb.toString(); - } - - public static String getOctalPermissions(int perms) { - Collection<PosixFilePermission> attrs = permissionsToAttributes(perms); - return getOctalPermissions(attrs); - } - - public static Set<PosixFilePermission> permissionsToAttributes(int perms) { - Set<PosixFilePermission> p = EnumSet.noneOf(PosixFilePermission.class); - if ((perms & SftpConstants.S_IRUSR) == SftpConstants.S_IRUSR) { - p.add(PosixFilePermission.OWNER_READ); - } - if ((perms & SftpConstants.S_IWUSR) == SftpConstants.S_IWUSR) { - p.add(PosixFilePermission.OWNER_WRITE); - } - if ((perms & SftpConstants.S_IXUSR) == SftpConstants.S_IXUSR) { - p.add(PosixFilePermission.OWNER_EXECUTE); - } - if ((perms & SftpConstants.S_IRGRP) == SftpConstants.S_IRGRP) { - p.add(PosixFilePermission.GROUP_READ); - } - if ((perms & SftpConstants.S_IWGRP) == SftpConstants.S_IWGRP) { - p.add(PosixFilePermission.GROUP_WRITE); - } - if ((perms & SftpConstants.S_IXGRP) == SftpConstants.S_IXGRP) { - p.add(PosixFilePermission.GROUP_EXECUTE); - } - if ((perms & SftpConstants.S_IROTH) == SftpConstants.S_IROTH) { - p.add(PosixFilePermission.OTHERS_READ); - } - if ((perms & SftpConstants.S_IWOTH) == SftpConstants.S_IWOTH) { - p.add(PosixFilePermission.OTHERS_WRITE); - } - if ((perms & SftpConstants.S_IXOTH) == SftpConstants.S_IXOTH) { - p.add(PosixFilePermission.OTHERS_EXECUTE); - } - return p; - } - - public static String getOctalPermissions(Collection<PosixFilePermission> perms) { - int pf = 0; - - for (PosixFilePermission p : perms) { - switch (p) { - case OWNER_READ: - pf |= SftpConstants.S_IRUSR; - break; - case OWNER_WRITE: - pf |= SftpConstants.S_IWUSR; - break; - case OWNER_EXECUTE: - pf |= SftpConstants.S_IXUSR; - break; - case GROUP_READ: - pf |= SftpConstants.S_IRGRP; - break; - case GROUP_WRITE: - pf |= SftpConstants.S_IWGRP; - break; - case GROUP_EXECUTE: - pf |= SftpConstants.S_IXGRP; - break; - case OTHERS_READ: - pf |= SftpConstants.S_IROTH; - break; - case OTHERS_WRITE: - pf |= SftpConstants.S_IWOTH; - break; - case OTHERS_EXECUTE: - pf |= SftpConstants.S_IXOTH; - break; - default: // ignored - } - } - - return String.format("%04o", pf); - } - - /** - * Uses the host, port and username to create a unique identifier - * - * @param uri The {@link URI} - <B>Note:</B> not checked to make sure - * that the scheme is {@code sftp://} - * @return The unique identifier - * @see #getFileSystemIdentifier(String, int, String) - */ - public static String getFileSystemIdentifier(URI uri) { - String userInfo = ValidateUtils.checkNotNullAndNotEmpty(uri.getUserInfo(), "UserInfo not provided"); - String[] ui = GenericUtils.split(userInfo, ':'); - ValidateUtils.checkTrue(GenericUtils.length(ui) == 2, "Invalid user info: %s", userInfo); - return getFileSystemIdentifier(uri.getHost(), uri.getPort(), ui[0]); - } - - /** - * Uses the remote host address, port and current username to create a unique identifier - * - * @param session The {@link ClientSession} - * @return The unique identifier - * @see #getFileSystemIdentifier(String, int, String) - */ - public static String getFileSystemIdentifier(ClientSession session) { - IoSession ioSession = session.getIoSession(); - SocketAddress addr = ioSession.getRemoteAddress(); - String username = session.getUsername(); - if (addr instanceof InetSocketAddress) { - InetSocketAddress inetAddr = (InetSocketAddress) addr; - return getFileSystemIdentifier(inetAddr.getHostString(), inetAddr.getPort(), username); - } else { - return getFileSystemIdentifier(addr.toString(), SshConstants.DEFAULT_PORT, username); - } - } - - public static String getFileSystemIdentifier(String host, int port, String username) { - return GenericUtils.trimToEmpty(host) + ':' - + ((port <= 0) ? SshConstants.DEFAULT_PORT : port) + ':' - + GenericUtils.trimToEmpty(username); - } - - public static URI createFileSystemURI(String host, int port, String username, String password) { - return createFileSystemURI(host, port, username, password, Collections.emptyMap()); - } - - public static URI createFileSystemURI(String host, int port, String username, String password, Map<String, ?> params) { - ValidateUtils.checkNotNullAndNotEmpty(host, "No host provided"); - - String queryPart = null; - int numParams = GenericUtils.size(params); - if (numParams > 0) { - StringBuilder sb = new StringBuilder(numParams * Short.SIZE); - for (Map.Entry<String, ?> pe : params.entrySet()) { - String key = pe.getKey(); - Object value = pe.getValue(); - if (sb.length() > 0) { - sb.append('&'); - } - sb.append(key); - if (value != null) { - sb.append('=').append(Objects.toString(value, null)); - } - } - - queryPart = sb.toString(); - } - - try { - String userAuth = encodeCredentials(username, password); - return new URI(SftpConstants.SFTP_SUBSYSTEM_NAME, userAuth, host, port, "/", queryPart, null); - } catch (URISyntaxException e) { - throw new IllegalArgumentException("Failed (" + e.getClass().getSimpleName() + ")" - + " to create access URI: " + e.getMessage(), e); - } - } - - public static String encodeCredentials(String username, String password) { - ValidateUtils.checkNotNullAndNotEmpty(username, "No username provided"); - ValidateUtils.checkNotNullAndNotEmpty(password, "No password provided"); - /* - * There is no way to properly encode/decode credentials that already contain - * colon. See also https://tools.ietf.org/html/rfc3986#section-3.2.1: - * - * - * Use of the format "user:password" in the userinfo field is - * deprecated. Applications should not render as clear text any data - * after the first colon (":") character found within a userinfo - * subcomponent unless the data after the colon is the empty string - * (indicating no password). Applications may choose to ignore or - * reject such data when it is received as part of a reference and - * should reject the storage of such data in unencrypted form. - */ - ValidateUtils.checkTrue((username.indexOf(':') < 0) && (password.indexOf(':') < 0), "Reserved character used in credentials"); - return username + ":" + password; - } -}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e3b8acd7/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPath.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPath.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPath.java deleted file mode 100644 index 5567b58..0000000 --- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPath.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.client.subsystem.sftp; - -import java.io.IOException; -import java.nio.file.FileSystem; -import java.nio.file.LinkOption; -import java.nio.file.spi.FileSystemProvider; -import java.util.List; - -import org.apache.sshd.common.file.util.BasePath; - -public class SftpPath extends BasePath<SftpPath, SftpFileSystem> { - public SftpPath(SftpFileSystem fileSystem, String root, List<String> names) { - super(fileSystem, root, names); - } - - @Override - public SftpPath toRealPath(LinkOption... options) throws IOException { - // TODO: handle links - SftpPath absolute = toAbsolutePath(); - FileSystem fs = getFileSystem(); - FileSystemProvider provider = fs.provider(); - provider.checkAccess(absolute); - return absolute; - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e3b8acd7/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPathIterator.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPathIterator.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPathIterator.java deleted file mode 100644 index 49b4b48..0000000 --- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPathIterator.java +++ /dev/null @@ -1,82 +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.client.subsystem.sftp; - -import java.nio.file.Path; -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> - */ -public class SftpPathIterator implements Iterator<Path> { - private final SftpPath p; - private final Iterator<? extends SftpClient.DirEntry> it; - private boolean dotIgnored; - private boolean dotdotIgnored; - private SftpClient.DirEntry curEntry; - - public SftpPathIterator(SftpPath path, Iterable<? extends SftpClient.DirEntry> iter) { - this(path, (iter == null) ? null : iter.iterator()); - } - - public SftpPathIterator(SftpPath path, Iterator<? extends SftpClient.DirEntry> iter) { - p = path; - it = iter; - curEntry = nextEntry(); - } - - @Override - public boolean hasNext() { - return curEntry != null; - } - - @Override - public Path next() { - if (curEntry == null) { - throw new NoSuchElementException("No next entry"); - } - - SftpClient.DirEntry entry = curEntry; - curEntry = nextEntry(); - return p.resolve(entry.getFilename()); - } - - private SftpClient.DirEntry nextEntry() { - while ((it != null) && it.hasNext()) { - SftpClient.DirEntry entry = it.next(); - String name = entry.getFilename(); - if (".".equals(name) && (!dotIgnored)) { - dotIgnored = true; - } else if ("..".equals(name) && (!dotdotIgnored)) { - dotdotIgnored = true; - } else { - return entry; - } - } - - return null; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("newDirectoryStream(" + p + ") Iterator#remove() N/A"); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e3b8acd7/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributeView.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributeView.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributeView.java deleted file mode 100644 index 1fb614c..0000000 --- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributeView.java +++ /dev/null @@ -1,94 +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.client.subsystem.sftp; - -import java.io.IOException; -import java.nio.file.LinkOption; -import java.nio.file.Path; -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.util.Set; - -import org.apache.sshd.client.subsystem.sftp.impl.AbstractSftpFileAttributeView; -import org.apache.sshd.common.util.GenericUtils; - -/** - * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> - */ -public class SftpPosixFileAttributeView extends AbstractSftpFileAttributeView implements PosixFileAttributeView { - public SftpPosixFileAttributeView(SftpFileSystemProvider provider, Path path, LinkOption... options) { - super(provider, path, options); - } - - @Override - public String name() { - return "posix"; - } - - @Override - public PosixFileAttributes readAttributes() throws IOException { - return new SftpPosixFileAttributes(path, readRemoteAttributes()); - } - - @Override - public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime) throws IOException { - SftpClient.Attributes attrs = new SftpClient.Attributes(); - if (lastModifiedTime != null) { - attrs.modifyTime(lastModifiedTime); - } - if (lastAccessTime != null) { - attrs.accessTime(lastAccessTime); - } - if (createTime != null) { - attrs.createTime(createTime); - } - - if (GenericUtils.isEmpty(attrs.getFlags())) { - if (log.isDebugEnabled()) { - log.debug("setTimes({}) no changes", path); - } - } else { - writeRemoteAttributes(attrs); - } - } - - @Override - public void setPermissions(Set<PosixFilePermission> perms) throws IOException { - provider.setAttribute(path, "permissions", perms, options); - } - - @Override - public void setGroup(GroupPrincipal group) throws IOException { - provider.setAttribute(path, "group", group, options); - } - - @Override - public UserPrincipal getOwner() throws IOException { - return readAttributes().owner(); - } - - @Override - public void setOwner(UserPrincipal owner) throws IOException { - provider.setAttribute(path, "owner", owner, options); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e3b8acd7/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributes.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributes.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributes.java deleted file mode 100644 index a07e67f..0000000 --- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributes.java +++ /dev/null @@ -1,113 +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.client.subsystem.sftp; - -import java.nio.file.Path; -import java.nio.file.attribute.FileTime; -import java.nio.file.attribute.GroupPrincipal; -import java.nio.file.attribute.PosixFileAttributes; -import java.nio.file.attribute.PosixFilePermission; -import java.nio.file.attribute.UserPrincipal; -import java.util.Set; - -import org.apache.sshd.client.subsystem.sftp.SftpClient.Attributes; -import org.apache.sshd.common.util.GenericUtils; - -/** - * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> - */ -public class SftpPosixFileAttributes implements PosixFileAttributes { - private final Path path; - private final Attributes attributes; - - public SftpPosixFileAttributes(Path path, Attributes attributes) { - this.path = path; - this.attributes = attributes; - } - - /** - * @return The referenced attributes file {@link Path} - */ - public final Path getPath() { - return path; - } - - @Override - public UserPrincipal owner() { - String owner = attributes.getOwner(); - return GenericUtils.isEmpty(owner) ? null : new SftpFileSystem.DefaultUserPrincipal(owner); - } - - @Override - public GroupPrincipal group() { - String group = attributes.getGroup(); - return GenericUtils.isEmpty(group) ? null : new SftpFileSystem.DefaultGroupPrincipal(group); - } - - @Override - public Set<PosixFilePermission> permissions() { - return SftpFileSystemProvider.permissionsToAttributes(attributes.getPermissions()); - } - - @Override - public FileTime lastModifiedTime() { - return attributes.getModifyTime(); - } - - @Override - public FileTime lastAccessTime() { - return attributes.getAccessTime(); - } - - @Override - public FileTime creationTime() { - return attributes.getCreateTime(); - } - - @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.getSize(); - } - - @Override - public Object fileKey() { - // TODO consider implementing this - return null; - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e3b8acd7/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/fs/SftpAclFileAttributeView.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/fs/SftpAclFileAttributeView.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/fs/SftpAclFileAttributeView.java new file mode 100644 index 0000000..fee19b0 --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/fs/SftpAclFileAttributeView.java @@ -0,0 +1,68 @@ +/* + * 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.subsystem.sftp.fs; + +import java.io.IOException; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.attribute.AclEntry; +import java.nio.file.attribute.AclFileAttributeView; +import java.nio.file.attribute.PosixFileAttributes; +import java.nio.file.attribute.UserPrincipal; +import java.util.List; + +import org.apache.sshd.client.subsystem.sftp.SftpClient; +import org.apache.sshd.client.subsystem.sftp.impl.AbstractSftpFileAttributeView; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public class SftpAclFileAttributeView extends AbstractSftpFileAttributeView implements AclFileAttributeView { + public SftpAclFileAttributeView(SftpFileSystemProvider provider, Path path, LinkOption... options) { + super(provider, path, options); + } + + @Override + public UserPrincipal getOwner() throws IOException { + PosixFileAttributes v = provider.readAttributes(path, PosixFileAttributes.class, options); + return v.owner(); + } + + @Override + public void setOwner(UserPrincipal owner) throws IOException { + provider.setAttribute(path, "posix", "owner", owner, options); + } + + @Override + public String name() { + return "acl"; + } + + @Override + public List<AclEntry> getAcl() throws IOException { + return readRemoteAttributes().getAcl(); + } + + @Override + public void setAcl(List<AclEntry> acl) throws IOException { + writeRemoteAttributes(new SftpClient.Attributes().acl(acl)); + } + +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e3b8acd7/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/fs/SftpDirectoryStream.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/fs/SftpDirectoryStream.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/fs/SftpDirectoryStream.java new file mode 100644 index 0000000..f250af5 --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/fs/SftpDirectoryStream.java @@ -0,0 +1,67 @@ +/* + * 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.subsystem.sftp.fs; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Path; +import java.util.Iterator; + +import org.apache.sshd.client.subsystem.sftp.SftpClient; + +/** + * Implements a remote {@link DirectoryStream} + * + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public class SftpDirectoryStream implements DirectoryStream<Path> { + private final SftpClient sftp; + private final Iterable<SftpClient.DirEntry> iter; + private final SftpPath p; + + /** + * @param path The remote {@link SftpPath} + * @throws IOException If failed to initialize the directory access handle + */ + public SftpDirectoryStream(SftpPath path) throws IOException { + SftpFileSystem fs = path.getFileSystem(); + p = path; + sftp = fs.getClient(); + iter = sftp.readDir(path.toString()); + } + + /** + * Client instance used to access the remote directory + * + * @return The {@link SftpClient} instance used to access the remote directory + */ + public final SftpClient getClient() { + return sftp; + } + + @Override + public Iterator<Path> iterator() { + return new SftpPathIterator(p, iter); + } + + @Override + public void close() throws IOException { + sftp.close(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e3b8acd7/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/fs/SftpFileStore.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/fs/SftpFileStore.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/fs/SftpFileStore.java new file mode 100644 index 0000000..a909e2d --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/fs/SftpFileStore.java @@ -0,0 +1,105 @@ +/* + * 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.subsystem.sftp.fs; + +import java.io.IOException; +import java.nio.file.FileStore; +import java.nio.file.FileSystem; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.FileStoreAttributeView; +import java.util.Collection; + +import org.apache.sshd.common.subsystem.sftp.SftpConstants; +import org.apache.sshd.common.util.GenericUtils; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public class SftpFileStore extends FileStore { + private final SftpFileSystem fs; + private final String name; + + public SftpFileStore(String name, SftpFileSystem fs) { + this.name = name; + this.fs = fs; + } + + public final SftpFileSystem getFileSystem() { + return fs; + } + + @Override + public String name() { + return name; + } + + @Override + public String type() { + return SftpConstants.SFTP_SUBSYSTEM_NAME; + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public long getTotalSpace() throws IOException { + return Long.MAX_VALUE; // TODO use SFTPv6 space-available extension + } + + @Override + public long getUsableSpace() throws IOException { + return Long.MAX_VALUE; + } + + @Override + public long getUnallocatedSpace() throws IOException { + return Long.MAX_VALUE; + } + + @Override + public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) { + SftpFileSystem sftpFs = getFileSystem(); + SftpFileSystemProvider provider = sftpFs.provider(); + return provider.isSupportedFileAttributeView(sftpFs, type); + } + + @Override + public boolean supportsFileAttributeView(String name) { + if (GenericUtils.isEmpty(name)) { + return false; // debug breakpoint + } + + FileSystem sftpFs = getFileSystem(); + Collection<String> views = sftpFs.supportedFileAttributeViews(); + return !GenericUtils.isEmpty(views) && views.contains(name); + } + + @Override + public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) { + return null; // no special views supported + } + + @Override + public Object getAttribute(String attribute) throws IOException { + return null; // no special attributes supported + } +}