Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.pastdev.jsch.nio.file.UnixSshFileSystemProvider Maven / Gradle / Ivy
package com.pastdev.jsch.nio.file;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.AccessDeniedException;
import java.nio.file.AccessMode;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.DirectoryStream;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
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.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.GroupPrincipal;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.nio.file.attribute.UserPrincipal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jcraft.jsch.JSchException;
import com.pastdev.jsch.command.CommandRunner;
import com.pastdev.jsch.command.CommandRunner.ChannelExecWrapper;
import com.pastdev.jsch.command.CommandRunner.ExecuteResult;
public class UnixSshFileSystemProvider extends AbstractSshFileSystemProvider {
private static Logger logger = LoggerFactory.getLogger( UnixSshFileSystemProvider.class );
private static final String ASCII_UNIT_SEPARATOR = Character.toString( (char)31 );
private static final SupportedAttribute[] BASIC_SUPPORTED_ATTRIBUTES = new SupportedAttribute[] {
SupportedAttribute.creationTime,
SupportedAttribute.fileKey,
SupportedAttribute.isDirectory,
SupportedAttribute.isRegularFile,
SupportedAttribute.isSymbolicLink,
SupportedAttribute.isOther,
SupportedAttribute.lastAccessTime,
SupportedAttribute.lastModifiedTime,
SupportedAttribute.size };
public static final char PATH_SEPARATOR = '/';
public static final String PATH_SEPARATOR_STRING = "/";
private static final SupportedAttribute[] POSIX_ADDITIONAL_SUPPORTED_ATTRIBUTES = new SupportedAttribute[] {
SupportedAttribute.permissions,
SupportedAttribute.owner,
SupportedAttribute.group };
public static final String SCHEME_SSH_UNIX = "ssh.unix";
private static final SimpleDateFormat TOUCH_DATE_FORMAT = new SimpleDateFormat( "yyyyMMddHHmm.ss" );
private Map fileSystemMap;
public UnixSshFileSystemProvider() {
this.fileSystemMap = new HashMap();
}
UnixSshPath checkPath( Path path ) {
if ( path == null ) {
throw new NullPointerException();
}
if ( !(path instanceof UnixSshPath) ) {
throw new IllegalArgumentException( "path not an instanceof UnixSshPath" );
}
return (UnixSshPath)path;
}
@Override
public void checkAccess( Path path, AccessMode... modes ) throws IOException {
UnixSshPath unixPath = checkPath( path ).toAbsolutePath();
String pathString = unixPath.toAbsolutePath().quotedString();
String testCommand = unixPath.getFileSystem().getCommand( "test" );
if ( execute( unixPath, testCommand + " -e " + pathString ).getExitCode() != 0 ) {
throw new NoSuchFileException( pathString );
}
Set modesSet = toSet( modes );
if ( modesSet.contains( AccessMode.READ ) ) {
if ( execute( unixPath, testCommand + " -r " + pathString ).getExitCode() != 0 ) {
throw new AccessDeniedException( pathString );
}
}
if ( modesSet.contains( AccessMode.WRITE ) ) {
if ( execute( unixPath, testCommand + " -w " + pathString ).getExitCode() != 0 ) {
throw new AccessDeniedException( pathString );
}
}
if ( modesSet.contains( AccessMode.EXECUTE ) ) {
if ( execute( unixPath, testCommand + " -x " + pathString ).getExitCode() != 0 ) {
throw new AccessDeniedException( pathString );
}
}
}
@Override
public void copy( Path from, Path to, CopyOption... copyOptions ) throws IOException {
copyOrMove( "cp", from, to, copyOptions );
}
public void copyOrMove( String cpOrMv, Path from, Path to, CopyOption... copyOptions ) throws IOException {
UnixSshPath unixFrom = checkPath( from );
UnixSshPath unixTo = checkPath( to );
Set options = toSet( copyOptions );
if ( options.contains( StandardCopyOption.ATOMIC_MOVE ) ) {
throw new AtomicMoveNotSupportedException( from.toString(), to.toString(),
"to complicated to think about right now, try again at a later release." );
}
BasicFileAttributesImpl fromAttributes = new BasicFileAttributesImpl( unixFrom );
if ( exists( unixTo ) ) {
PosixFileAttributesImpl toAttributes = new PosixFileAttributesImpl( unixTo );
if ( fromAttributes.isSameFile( toAttributes ) ) return;
if ( options.contains( StandardCopyOption.REPLACE_EXISTING ) ) {
delete( unixTo, toAttributes );
}
else {
throw new FileAlreadyExistsException( to.toString() );
}
}
String command = unixFrom.getFileSystem().getCommand( cpOrMv )
+ " " + unixFrom.toAbsolutePath().quotedString()
+ " " + unixTo.toAbsolutePath().quotedString();
executeForStdout( unixTo, command );
}
@Override
@SuppressWarnings("unchecked")
public void createDirectory( Path path, FileAttribute>... fileAttributes ) throws IOException {
UnixSshPath unixPath = checkPath( path );
Set permissions = null;
for ( FileAttribute> fileAttribute : fileAttributes ) {
if ( fileAttribute.name().equals( "posix:permissions" ) ) {
permissions = (Set)fileAttribute.value();
}
}
StringBuilder commandBuilder = new StringBuilder( unixPath.getFileSystem().getCommand( "mkdir" ) )
.append( " " );
if ( permissions != null ) {
commandBuilder.append( "-m " ).append( toMode( permissions ) );
}
commandBuilder.append( unixPath.toAbsolutePath().quotedString() );
executeForStdout( unixPath, commandBuilder.toString() );
}
@SuppressWarnings("unchecked")
PosixFileAttributes createFile( UnixSshPath path, FileAttribute>... fileAttributes ) throws IOException {
Set permissions = null;
UserPrincipal owner = null;
GroupPrincipal group = null;
for ( FileAttribute> fileAttribute : fileAttributes ) {
String name = fileAttribute.name();
if ( name.equals( "posix:permissions" ) ) {
permissions = (Set)fileAttribute.value();
}
else if ( name.equals( "posix:owner" ) ) {
owner = (UserPrincipal)fileAttribute.value();
}
else if ( name.equals( "posix:group" ) ) {
group = (GroupPrincipal)fileAttribute.value();
}
}
// TODO i think this can be done with dd atomically
String command = path.getFileSystem().getCommand( "touch" ) + " " + path.toAbsolutePath().quotedString();
executeForStdout( path, command );
if ( permissions != null ) {
setPermissions( path, permissions );
}
if ( owner != null ) {
setOwner( path, owner );
}
if ( group != null ) {
setGroup( path, group );
}
return readAttributes( path, PosixFileAttributes.class );
}
@Override
public void delete( Path path ) throws IOException {
delete( checkPath( path ), new BasicFileAttributesImpl( path ) );
}
private void delete( UnixSshPath path, BasicFileAttributes attributes ) throws IOException {
if ( attributes.isDirectory() ) {
if ( execute( path, path.getFileSystem().getCommand( "rmdir" )
+ " " + path.toAbsolutePath().quotedString() )
.getExitCode() != 0 ) {
throw new DirectoryNotEmptyException( path.toString() );
}
}
else {
executeForStdout( path, path.getFileSystem().getCommand( "unlink" )
+ " " + path.toAbsolutePath().quotedString() );
}
}
private ExecuteResult execute( UnixSshPath path, String command ) throws IOException {
CommandRunner commandRunner = path.getFileSystem().getCommandRunner();
try {
return commandRunner.execute( command );
}
catch ( JSchException e ) {
throw new IOException( e );
}
}
private String executeForStdout( UnixSshPath path, String command ) throws IOException {
ExecuteResult result = execute( path, command );
if ( result.getExitCode() != 0 ) {
throw new UnixSshCommandFailedException( command, result );
}
return result.getStdout();
}
private boolean exists( Path path ) throws IOException {
try {
checkAccess( path );
return true;
}
catch ( NoSuchFileException e ) {
return false;
}
}
UnixSshBasicFileAttributeView getFileAttributeView( Path path, String viewName, LinkOption... linkOptions ) {
if ( viewName.equals( "basic" ) ) {
return new UnixSshBasicFileAttributeView( checkPath( path ), linkOptions );
}
else if ( viewName.equals( "posix" ) ) {
return new UnixSshPosixFileAttributeView( checkPath( path ), linkOptions );
}
return null;
}
@Override
@SuppressWarnings("unchecked")
public V getFileAttributeView( Path path, Class type, LinkOption... linkOptions ) {
if ( type == BasicFileAttributeView.class ) {
return (V)getFileAttributeView( path, "basic", linkOptions );
}
if ( type == PosixFileAttributeView.class ) {
return (V)getFileAttributeView( path, "posix", linkOptions );
}
if ( type == null ) {
throw new NullPointerException();
}
return (V)null;
}
@Override
public FileStore getFileStore( Path path ) throws IOException {
// TODO Auto-generated method stub
throw new UnsupportedOperationException( "no idea what a file store would mean in this context, so for now, you have to deal with this exception" );
}
@Override
public FileSystem getFileSystem( URI uri ) {
UnixSshFileSystem fileSystem = fileSystemMap.get( uri.resolve( PATH_SEPARATOR_STRING ) );
if ( fileSystem == null ) {
throw new FileSystemNotFoundException( "no filesystem defined for " + uri.toString() );
}
return fileSystem;
}
@Override
public String getScheme() {
return SCHEME_SSH_UNIX;
}
@Override
public boolean isHidden( Path path ) throws IOException {
return checkPath( path ).getFileNameString().startsWith( "." );
}
@Override
public boolean isSameFile( Path path1, Path path2 ) throws IOException {
if ( path1.equals( path2 ) ) {
return true;
}
else if ( !isSameProvider( path1, path2 ) ) {
return false;
}
return new BasicFileAttributesImpl( path1 ).isSameFile(
new BasicFileAttributesImpl( path2 ) );
}
private boolean isSameProvider( Path path1, Path path2 ) {
return path1.getFileSystem().provider().equals( path2.getFileSystem().provider() );
}
@Override
public void move( Path from, Path to, CopyOption... copyOptions ) throws IOException {
copyOrMove( "mv", from, to, copyOptions );
}
@Override
public SeekableByteChannel newByteChannel( Path path, Set extends OpenOption> openOptions, FileAttribute>... fileAttributes ) throws IOException {
return new UnixSshSeekableByteChannel( checkPath( path ), openOptions, fileAttributes );
}
@Override
public DirectoryStream newDirectoryStream( Path path, Filter super Path> filter ) throws IOException {
UnixSshPath unixPath = checkPath( path );
String result = executeForStdout( unixPath, unixPath.getFileSystem().getCommand( "ls" )
+ " -1 " + unixPath.toAbsolutePath().quotedString() );
String[] items = result.split( "\n" );
if ( items.length == 1 && items[0].isEmpty() ) {
items = null;
}
return new StandardDirectoryStream( path, items, filter );
}
@Override
public FileSystem newFileSystem( URI uri, Map environment ) throws IOException {
URI baseUri = uri.resolve( PATH_SEPARATOR_STRING );
UnixSshFileSystem existing = fileSystemMap.get( baseUri );
if ( existing != null ) {
throw new RuntimeException( "filesystem already exists for " + uri.toString() + " at " + existing.toString() );
}
UnixSshFileSystem fileSystem = new UnixSshFileSystem( this, uri, environment );
fileSystemMap.put( baseUri, fileSystem );
return fileSystem;
}
@Override
public InputStream newInputStream( Path path, OpenOption... openOptions ) throws IOException {
UnixSshPath unixPath = checkPath( path ).toAbsolutePath();
try {
final ChannelExecWrapper channel = unixPath.getFileSystem()
.getCommandRunner()
.open( unixPath.getFileSystem().getCommand( "cat" )
+ " " + unixPath.toAbsolutePath().quotedString() );
return new InputStream() {
private InputStream inputStream = channel.getInputStream();
@Override
public void close() throws IOException {
int exitCode = channel.close();
logger.debug( "cat exited with {}", exitCode );
}
@Override
public int read() throws IOException {
return inputStream.read();
}
};
}
catch ( JSchException e ) {
throw new IOException( e );
}
}
@Override
public OutputStream newOutputStream( Path path, OpenOption... openOptions ) throws IOException {
UnixSshPath unixPath = checkPath( path ).toAbsolutePath();
Set options = null;
if ( openOptions == null || openOptions.length == 0 ) {
options = new HashSet( Arrays.asList( new StandardOpenOption[] {
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING,
StandardOpenOption.WRITE
} ) );
logger.debug( "no open options specified, so using CREATE, TRUNCATE_EXISTING, and WRITE" );
}
else {
options = new HashSet( Arrays.asList( openOptions ) );
}
if ( options.contains( StandardOpenOption.READ ) ) {
throw new IllegalArgumentException( "read not allowed on OutputStream, seriously..." );
}
if ( !options.contains( StandardOpenOption.WRITE ) ) {
throw new IllegalArgumentException( "what good is an OutputStream that you cant write to?" );
}
if ( options.contains( StandardOpenOption.DELETE_ON_CLOSE ) ) {
throw new UnsupportedOperationException( "not gonna implement" );
}
// dd has options for SYNC, DSYNC and SPARSE maybe...
try {
checkAccess( unixPath );
if ( options.contains( StandardOpenOption.CREATE_NEW ) ) {
throw new FileAlreadyExistsException( unixPath.toString() );
}
}
catch ( NoSuchFileException e ) {
if ( options.contains( StandardOpenOption.CREATE_NEW ) ) {
// this is as close to atomic create as i can get...
// TODO convert this to use `dd of=file conv=excl` and write 0
// bytes which will make the check for exists and create atomic
createFile( unixPath );
}
else if ( !options.contains( StandardOpenOption.CREATE ) ) {
throw e;
}
}
try {
StringBuilder commandBuilder = new StringBuilder( unixPath.getFileSystem().getCommand( "cat" ) )
.append( " " );
if ( options.contains( StandardOpenOption.APPEND )
&& !options.contains( StandardOpenOption.TRUNCATE_EXISTING ) ) {
commandBuilder.append( ">> " );
}
else {
commandBuilder.append( "> " );
}
commandBuilder.append( unixPath.toAbsolutePath().quotedString() );
final ChannelExecWrapper channel = unixPath.getFileSystem()
.getCommandRunner().open( commandBuilder.toString() );
return new OutputStream() {
private OutputStream outputStream = channel.getOutputStream();
@Override
public void close() throws IOException {
int exitCode = channel.close();
logger.debug( "cat exited with {}", exitCode );
}
@Override
public void write( int b ) throws IOException {
outputStream.write( b );
}
};
}
catch ( JSchException e ) {
throw new IOException( e );
}
}
int read( UnixSshPath path, long startIndex, ByteBuffer bytes ) throws IOException {
try {
int read = 0;
ChannelExecWrapper sshChannel = path.getFileSystem().getCommandRunner().open(
path.getFileSystem().getCommand( "dd" )
+ " bs=1 skip=" + startIndex + " if="
+ path.toAbsolutePath().quotedString() + " 2> /dev/null");
try (InputStream in = sshChannel.getInputStream()) {
ReadableByteChannel inChannel = Channels.newChannel( in );
int localRead;
while (bytes.hasRemaining() && (localRead = inChannel.read( bytes )) > 0) {
read += localRead;
}
}
return read;
}
catch ( JSchException e ) {
throw new IOException( e );
}
}
@Override
@SuppressWarnings("unchecked")
public A readAttributes( Path path, Class type, LinkOption... linkOptions ) throws IOException {
if ( type == BasicFileAttributes.class ) {
return (A)new BasicFileAttributesImpl( path, linkOptions );
}
if ( type == PosixFileAttributes.class ) {
return (A)new PosixFileAttributesImpl( path, linkOptions );
}
if ( type == null ) {
throw new NullPointerException();
}
return (A)null;
}
@Override
public Map readAttributes( Path path, String attributes, LinkOption... linkOptions ) throws IOException {
List attributeList = new ArrayList();
for ( String attributeName : attributes.split( "," ) ) {
attributeName = attributeName.trim();
if ( attributeName.equals( "*" ) ) {
return readAttributes( path, SupportedAttribute.values() );
}
SupportedAttribute attribute = SupportedAttribute.fromString( attributeName );
if ( attribute != null ) {
attributeList.add( attribute );
}
}
return readAttributes( path, attributeList.toArray( new SupportedAttribute[attributeList.size()] ), linkOptions );
}
private Map readAttributes( Path path, SupportedAttribute[] attributes, LinkOption... linkOptions ) throws IOException {
UnixSshPath unixPath = checkPath( path ).toAbsolutePath();
String command = statCommand( unixPath, attributes )
+ " " + unixPath.toAbsolutePath().quotedString();
String result = null;
try {
result = executeForStdout( unixPath, command );
}
catch ( UnixSshCommandFailedException e ) {
if ( exists( unixPath ) ) {
throw e;
}
else {
throw new NoSuchFileException( path.toString() );
}
}
return statParse( result, attributes );
}
void removeFileSystem( UnixSshFileSystem fileSystem ) {
fileSystemMap.remove( fileSystem.getUri().resolve( PATH_SEPARATOR_STRING ) );
}
@Override
public void setAttribute( Path path, String attribute, Object value, LinkOption... linkOptions ) throws IOException {
String viewName = null;
String attributeName = null;
int colonIndex = attribute.indexOf( ':' );
if ( colonIndex < 0 ) {
viewName = "basic";
attributeName = attribute;
}
else {
viewName = attribute.substring( 0, colonIndex );
attributeName = attribute.substring( colonIndex + 1 );
}
UnixSshBasicFileAttributeView view = getFileAttributeView( path, viewName, linkOptions );
if ( view == null ) {
throw new UnsupportedOperationException( "unsupported view " + viewName );
}
view.setAttribute( attributeName, value );
}
void setGroup( UnixSshPath path, GroupPrincipal group ) throws IOException {
String command = path.getFileSystem().getCommand( "chgrp" )
+ " " + group.getName() + " " + path.toAbsolutePath().quotedString();
executeForStdout( path, command );
}
void setOwner( UnixSshPath path, UserPrincipal owner ) throws IOException {
String command = path.getFileSystem().getCommand( "chown" )
+ " " + owner.getName() + " " + path.toAbsolutePath().quotedString();
executeForStdout( path, command );
}
void setPermissions( UnixSshPath path, Set permissions ) throws IOException {
String command = path.getFileSystem().getCommand( "chmod" )
+ " " + toMode( permissions ) + " " + path.toAbsolutePath().quotedString();
executeForStdout( path, command );
}
void setTimes( UnixSshPath path, FileTime lastModifiedTime, FileTime lastAccessTime ) throws IOException {
if ( lastModifiedTime != null && lastModifiedTime.equals( lastAccessTime ) ) {
String command = path.getFileSystem().getCommand( "touch" )
+ " -d " + toTouchTime( lastModifiedTime )
+ " " + path.toAbsolutePath().quotedString();
executeForStdout( path, command );
return;
}
if ( lastModifiedTime != null ) {
String command = path.getFileSystem().getCommand( "touch" )
+ " -m -d " + toTouchTime( lastModifiedTime )
+ " " + path.toAbsolutePath().quotedString();
executeForStdout( path, command );
}
if ( lastAccessTime != null ) {
String command = path.getFileSystem().getCommand( "touch" )
+ " -a -d " + toTouchTime( lastModifiedTime )
+ " " + path.toAbsolutePath().quotedString();
executeForStdout( path, command );
}
}
private String statCommand( UnixSshPath path, SupportedAttribute[] attributes ) {
return statCommand( path, attributes, false );
}
private String statCommand( UnixSshPath path, SupportedAttribute[] attributes, boolean newline ) {
final StringBuilder commandBuilder;
final Variant variant = path.getFileSystem().getVariant("stat");
switch(variant) {
case BSD:
commandBuilder = new StringBuilder(path.getFileSystem().getCommand("stat"))
.append(" -f \"");
break;
case GNU:
default:
commandBuilder = new StringBuilder( path.getFileSystem().getCommand( "stat" ) )
.append( " --printf \"" );
break;
}
// Default case
for ( int i = 0; i < attributes.length; i++ ) {
if ( i > 0 ) {
commandBuilder.append( ASCII_UNIT_SEPARATOR );
}
commandBuilder.append( attributes[i].option(variant) );
}
if ( newline ) {
commandBuilder.append( "\\n" );
}
return commandBuilder.append( "\"" ).toString();
}
private Map statParse( String result, SupportedAttribute... attributes ) {
String[] values = result.split( ASCII_UNIT_SEPARATOR );
Map map = new HashMap();
int index = 0;
for ( SupportedAttribute attribute : attributes ) {
map.put( attribute.name(), attribute.toObject( values[index++] ) );
}
return map;
}
Map statDirectory( UnixSshPath directoryPath ) throws IOException {
Map map = new HashMap<>();
SupportedAttribute[] allAttributes = SupportedAttribute.values();
String command = directoryPath.getFileSystem().getCommand( "find" ) + " "
+ directoryPath.toAbsolutePath().quotedString()
+ " -maxdepth 1 -type f -exec " + statCommand( directoryPath, allAttributes, true ) + " {} +";
String stdout = executeForStdout( directoryPath, command );
if ( stdout.length() > 0 ) {
String[] results = stdout.split( "\n" );
for ( String file : results ) {
logger.trace( "parsing stat response for {}", file );
Map fileAttributes = statParse( file, allAttributes );
UnixSshPath filePath = directoryPath.toAbsolutePath().relativize( directoryPath.resolve(
(String)fileAttributes.get( SupportedAttribute.name.toString() ) ) );
map.put( filePath, new PosixFileAttributesImpl( fileAttributes ) );
}
}
logger.trace( "returning map" );
return map;
}
private String toMode( Set permissions ) {
int[] values = new int[] { 4, 2, 1 };
int[] sections = new int[3];
String permissionsString = PosixFilePermissions.toString( permissions );
for ( int i = 0; i < 9; i++ ) {
if ( permissionsString.charAt( i ) != '-' ) {
sections[i / 3] += values[i % 3];
}
}
return "" + sections[0] + sections[1] + sections[2];
}
private String toTouchTime( FileTime fileTime ) {
return TOUCH_DATE_FORMAT.format( new Date( fileTime.toMillis() ) );
}
private static Set toSet( T[] array ) {
return new HashSet( Arrays.asList( array ) );
}
void truncateFile( UnixSshPath path, long size ) throws IOException {
String command = path.getFileSystem().getCommand( "truncate" )
+ " -s " + size + " " + path.toAbsolutePath().quotedString();
executeForStdout( path, command );
}
int write( UnixSshPath path, long startIndex, ByteBuffer bytes ) throws IOException {
try {
int bytesPosition = bytes.position();
// TODO cache this buffer for reuse
ByteBuffer temp = ByteBuffer.allocateDirect( bytes.limit() - bytesPosition );
temp.put( bytes );
bytes.position( bytesPosition );
String command = path.getFileSystem().getCommand( "dd" )
+ " conv=notrunc bs=1 seek=" + startIndex
+ " of=" + path.toAbsolutePath().quotedString();
ChannelExecWrapper sshChannel = null;
int written = 0;
try {
sshChannel = path.getFileSystem().getCommandRunner().open( command );
try (OutputStream out = sshChannel.getOutputStream()) {
WritableByteChannel outChannel = Channels.newChannel( out );
temp.flip();
written = outChannel.write( temp );
}
if ( written > 0 ) {
bytes.position( bytesPosition + written );
}
}
finally {
int exitCode = sshChannel.close();
if ( exitCode != 0 ) {
throw new IOException( "dd failed " + exitCode );
}
}
return written;
}
catch ( JSchException e ) {
throw new IOException( e );
}
}
private class BasicFileAttributesImpl implements BasicFileAttributes {
protected Map map;
private BasicFileAttributesImpl( Map attributesMap ) {
this.map = attributesMap;
}
private BasicFileAttributesImpl( Path path, LinkOption... linkOptions ) throws IOException {
this( path, null, linkOptions );
}
private BasicFileAttributesImpl( Path path, SupportedAttribute[] additionalAttributes, LinkOption... linkOptions ) throws IOException {
SupportedAttribute[] supportedAttributes = null;
if ( additionalAttributes == null ) {
supportedAttributes = BASIC_SUPPORTED_ATTRIBUTES;
}
else {
supportedAttributes = new SupportedAttribute[BASIC_SUPPORTED_ATTRIBUTES.length + additionalAttributes.length];
System.arraycopy( BASIC_SUPPORTED_ATTRIBUTES, 0, supportedAttributes, 0, BASIC_SUPPORTED_ATTRIBUTES.length );
System.arraycopy( additionalAttributes, 0, supportedAttributes, BASIC_SUPPORTED_ATTRIBUTES.length, additionalAttributes.length );
}
map = readAttributes( path, supportedAttributes );
}
public FileTime creationTime() {
return (FileTime)map.get( SupportedAttribute.creationTime.toString() );
}
public Object fileKey() {
return map.get( SupportedAttribute.fileKey.toString() );
}
public boolean isDirectory() {
return (Boolean)map.get( SupportedAttribute.isDirectory.toString() );
}
public boolean isOther() {
return (Boolean)map.get( SupportedAttribute.isOther.toString() );
}
public boolean isRegularFile() {
return (Boolean)map.get( SupportedAttribute.isRegularFile.toString() );
}
private boolean isSameFile( BasicFileAttributes other ) {
return fileKey().equals( other.fileKey() );
}
public boolean isSymbolicLink() {
return (Boolean)map.get( SupportedAttribute.isSymbolicLink.toString() );
}
public FileTime lastAccessTime() {
return (FileTime)map.get( SupportedAttribute.lastAccessTime.toString() );
}
public FileTime lastModifiedTime() {
return (FileTime)map.get( SupportedAttribute.lastModifiedTime.toString() );
}
public long size() {
return (Long)map.get( SupportedAttribute.size.toString() );
}
}
private class PosixFileAttributesImpl extends BasicFileAttributesImpl implements PosixFileAttributes {
private PosixFileAttributesImpl( Map attributeMap ) {
super( attributeMap );
}
private PosixFileAttributesImpl( Path path, LinkOption... linkOptions ) throws IOException {
super( path, POSIX_ADDITIONAL_SUPPORTED_ATTRIBUTES, linkOptions );
}
public GroupPrincipal group() {
return (GroupPrincipal)map.get( SupportedAttribute.group.toString() );
}
public UserPrincipal owner() {
return (UserPrincipal)map.get( SupportedAttribute.owner.toString() );
}
@SuppressWarnings("unchecked")
public Set permissions() {
return (Set)map.get( SupportedAttribute.permissions.toString() );
}
}
private enum SupportedAttribute {
creationTime("%W", "%B", FileTime.class),
group("%G", "%Sg", GroupPrincipal.class),
fileKey("%i", "%i", Long.TYPE),
lastAccessTime("%X", "%a", FileTime.class),
lastModifiedTime("%Y", "%m", FileTime.class),
lastChangedTime("%Z", "%c", FileTime.class),
name("%n", "%N", String.class),
owner("%U", "%Su", UserPrincipal.class),
permissions("%A", "%Sp", Set.class),
size("%s", "%z", Long.TYPE),
isRegularFile("%F", "%HT", Boolean.TYPE),
isDirectory("%F", "%HT", Boolean.TYPE),
isSymbolicLink("%F", "%HT", Boolean.TYPE),
isOther("%F", "%HT", Boolean.TYPE);
private static Map lookup;
private static final char[] allPermissions = new char[] { 'r', 'w', 'x', 'r', 'w', 'x', 'r', 'w', 'x' };
static {
lookup = new HashMap();
for ( SupportedAttribute attribute : values() ) {
lookup.put( attribute.name(), attribute );
}
}
private String gnuOption;
private final String bsdOption;
private Class> valueClass;
private SupportedAttribute( String gnuOption, String bsdOption, Class> valueClass ) {
this.gnuOption = gnuOption;
this.bsdOption = bsdOption;
this.valueClass = valueClass;
}
public static SupportedAttribute fromString( String attribute ) {
return lookup.get( attribute );
}
public String option(Variant variant) {
switch(variant) {
case BSD:
return bsdOption;
case GNU:
return gnuOption;
default:
throw new AssertionError("Unhandled variant: " + variant);
}
}
public Object toObject( String value ) {
if ( this == isRegularFile ) {
return "regular file".equals( value.toLowerCase() );
}
if ( this == isDirectory ) {
return "directory".equals( value.toLowerCase() );
}
if ( this == isSymbolicLink ) {
return "symbolic link".equals( value.toLowerCase() );
}
if ( this == isOther ) {
return "other".equals( value.toLowerCase() );
}
if ( this == owner ) {
return new StandardUserPrincipal( value );
}
if ( this == group ) {
return new StandardGroupPrincipal( value );
}
if ( this == permissions ) {
// need to remove leading 'd' and replace possible 's'
char[] permissions = value.substring( 1 ).toCharArray();
for ( int i = 0; i < 9; i++ ) {
if ( permissions[i] != '-' ) {
permissions[i] = allPermissions[i];
}
}
return PosixFilePermissions.fromString( new String( permissions ) );
}
if ( valueClass == Long.TYPE ) {
return Long.parseLong( value );
}
if ( valueClass == FileTime.class ) {
long seconds = 0;
try {
seconds = Long.parseLong( value );
} catch ( NumberFormatException e ) {
//Do nothing. Some stat versions don't support creation date and potentionally other times.
}
return FileTime.fromMillis( seconds * 1000 );
}
return value;
}
}
public static class UnixSshCommandFailedException extends IOException {
private static final long serialVersionUID = 2068524022254060541L;
private String command;
private ExecuteResult result;
public UnixSshCommandFailedException( String command, ExecuteResult result ) {
this.result = result;
}
@Override
public String getMessage() {
return "`" + command + "` failed with exit code "
+ result.getExitCode() + ": stdout='"
+ result.getStdout() + "', stderr='"
+ result.getStderr() + "'";
}
}
}