1002 lines
31 KiB
Java
1002 lines
31 KiB
Java
/**
|
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
|
* Copyright Daniel Ratcliffe, 2011-2016. Do not distribute without permission.
|
|
* Send enquiries to dratcliffe@gmail.com
|
|
*/
|
|
|
|
package dan200.computercraft.core.filesystem;
|
|
|
|
import dan200.computercraft.api.filesystem.IMount;
|
|
import dan200.computercraft.api.filesystem.IWritableMount;
|
|
|
|
import java.io.*;
|
|
import java.nio.charset.Charset;
|
|
import java.util.*;
|
|
import java.util.regex.Pattern;
|
|
|
|
public class FileSystem
|
|
{
|
|
private class MountWrapper
|
|
{
|
|
private String m_label;
|
|
private String m_location;
|
|
|
|
private IMount m_mount;
|
|
private IWritableMount m_writableMount;
|
|
|
|
public MountWrapper( String label, String location, IMount mount )
|
|
{
|
|
m_label = label;
|
|
m_location = location;
|
|
m_mount = mount;
|
|
m_writableMount = null;
|
|
}
|
|
|
|
public MountWrapper( String label, String location, IWritableMount mount )
|
|
{
|
|
this( label, location, (IMount)mount );
|
|
m_writableMount = mount;
|
|
}
|
|
|
|
public String getLabel()
|
|
{
|
|
return m_label;
|
|
}
|
|
|
|
public String getLocation()
|
|
{
|
|
return m_location;
|
|
}
|
|
|
|
public long getFreeSpace()
|
|
{
|
|
if( m_writableMount == null )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
try
|
|
{
|
|
return m_writableMount.getRemainingSpace();
|
|
}
|
|
catch( IOException e )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public boolean isReadOnly( String path ) throws FileSystemException
|
|
{
|
|
return (m_writableMount == null);
|
|
}
|
|
|
|
// IMount forwarders:
|
|
|
|
public boolean exists( String path ) throws FileSystemException
|
|
{
|
|
path = toLocal( path );
|
|
try
|
|
{
|
|
return m_mount.exists( path );
|
|
}
|
|
catch( IOException e )
|
|
{
|
|
throw new FileSystemException( e.getMessage() );
|
|
}
|
|
}
|
|
|
|
public boolean isDirectory( String path ) throws FileSystemException
|
|
{
|
|
path = toLocal( path );
|
|
try
|
|
{
|
|
return m_mount.exists( path ) && m_mount.isDirectory( path );
|
|
}
|
|
catch( IOException e )
|
|
{
|
|
throw new FileSystemException( e.getMessage() );
|
|
}
|
|
}
|
|
|
|
public void list( String path, List<String> contents ) throws FileSystemException
|
|
{
|
|
path = toLocal( path );
|
|
try
|
|
{
|
|
if( m_mount.exists( path ) && m_mount.isDirectory( path ) )
|
|
{
|
|
m_mount.list( path, contents );
|
|
}
|
|
else
|
|
{
|
|
throw new FileSystemException( "Not a directory" );
|
|
}
|
|
}
|
|
catch( IOException e )
|
|
{
|
|
throw new FileSystemException( e.getMessage() );
|
|
}
|
|
}
|
|
|
|
public long getSize( String path ) throws FileSystemException
|
|
{
|
|
path = toLocal( path );
|
|
try
|
|
{
|
|
if( m_mount.exists( path ) )
|
|
{
|
|
if( m_mount.isDirectory( path ) )
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return m_mount.getSize( path );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new FileSystemException( "No such file" );
|
|
}
|
|
}
|
|
catch( IOException e )
|
|
{
|
|
throw new FileSystemException( e.getMessage() );
|
|
}
|
|
}
|
|
|
|
public InputStream openForRead( String path ) throws FileSystemException
|
|
{
|
|
path = toLocal( path );
|
|
try
|
|
{
|
|
if( m_mount.exists( path ) && !m_mount.isDirectory( path ) )
|
|
{
|
|
return m_mount.openForRead( path );
|
|
}
|
|
else
|
|
{
|
|
throw new FileSystemException( "No such file" );
|
|
}
|
|
}
|
|
catch( IOException e )
|
|
{
|
|
throw new FileSystemException( e.getMessage() );
|
|
}
|
|
}
|
|
|
|
// IWritableMount forwarders:
|
|
|
|
public void makeDirectory( String path ) throws FileSystemException
|
|
{
|
|
if( m_writableMount == null )
|
|
{
|
|
throw new FileSystemException( "Access Denied" );
|
|
}
|
|
try
|
|
{
|
|
path = toLocal( path );
|
|
if( m_mount.exists( path ) )
|
|
{
|
|
if( !m_mount.isDirectory( path ) )
|
|
{
|
|
throw new FileSystemException( "File exists" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_writableMount.makeDirectory( path );
|
|
}
|
|
}
|
|
catch( IOException e )
|
|
{
|
|
throw new FileSystemException( e.getMessage() );
|
|
}
|
|
}
|
|
|
|
public void delete( String path ) throws FileSystemException
|
|
{
|
|
if( m_writableMount == null )
|
|
{
|
|
throw new FileSystemException( "Access Denied" );
|
|
}
|
|
try
|
|
{
|
|
path = toLocal( path );
|
|
if( m_mount.exists( path ) )
|
|
{
|
|
m_writableMount.delete( path );
|
|
}
|
|
}
|
|
catch( IOException e )
|
|
{
|
|
throw new FileSystemException( e.getMessage() );
|
|
}
|
|
}
|
|
|
|
public OutputStream openForWrite( String path ) throws FileSystemException
|
|
{
|
|
if( m_writableMount == null )
|
|
{
|
|
throw new FileSystemException( "Access Denied" );
|
|
}
|
|
try
|
|
{
|
|
path = toLocal( path );
|
|
if( m_mount.exists( path ) && m_mount.isDirectory( path ) )
|
|
{
|
|
throw new FileSystemException( "Cannot write to directory" );
|
|
}
|
|
else
|
|
{
|
|
if( !path.isEmpty() )
|
|
{
|
|
String dir = getDirectory( path );
|
|
if( !dir.isEmpty() && !m_mount.exists( path ) )
|
|
{
|
|
m_writableMount.makeDirectory( dir );
|
|
}
|
|
}
|
|
return m_writableMount.openForWrite( path );
|
|
}
|
|
}
|
|
catch( IOException e )
|
|
{
|
|
throw new FileSystemException( e.getMessage() );
|
|
}
|
|
}
|
|
|
|
public OutputStream openForAppend( String path ) throws FileSystemException
|
|
{
|
|
if( m_writableMount == null )
|
|
{
|
|
throw new FileSystemException( "Access Denied" );
|
|
}
|
|
try
|
|
{
|
|
path = toLocal( path );
|
|
if( !m_mount.exists( path ) )
|
|
{
|
|
if( !path.isEmpty() )
|
|
{
|
|
String dir = getDirectory( path );
|
|
if( !dir.isEmpty() && !m_mount.exists( path ) )
|
|
{
|
|
m_writableMount.makeDirectory( dir );
|
|
}
|
|
}
|
|
return m_writableMount.openForWrite( path );
|
|
}
|
|
else if( m_mount.isDirectory( path ) )
|
|
{
|
|
throw new FileSystemException( "Cannot write to directory" );
|
|
}
|
|
else
|
|
{
|
|
return m_writableMount.openForAppend( path );
|
|
}
|
|
}
|
|
catch( IOException e )
|
|
{
|
|
throw new FileSystemException( e.getMessage() );
|
|
}
|
|
}
|
|
|
|
// private members
|
|
|
|
private String toLocal( String path )
|
|
{
|
|
return FileSystem.toLocal( path, m_location );
|
|
}
|
|
}
|
|
|
|
private Map<String, MountWrapper> m_mounts = new HashMap<String, MountWrapper>();
|
|
private Set<IMountedFile> m_openFiles = new HashSet<IMountedFile>();
|
|
|
|
public FileSystem( String rootLabel, IMount rootMount ) throws FileSystemException
|
|
{
|
|
mount( rootLabel, "", rootMount );
|
|
}
|
|
|
|
public FileSystem( String rootLabel, IWritableMount rootMount ) throws FileSystemException
|
|
{
|
|
mountWritable( rootLabel, "", rootMount );
|
|
}
|
|
|
|
public void unload()
|
|
{
|
|
// Close all dangling open files
|
|
synchronized( m_openFiles )
|
|
{
|
|
while( m_openFiles.size() > 0 )
|
|
{
|
|
IMountedFile file = m_openFiles.iterator().next();
|
|
try
|
|
{
|
|
file.close();
|
|
}
|
|
catch( IOException e )
|
|
{
|
|
m_openFiles.remove( file );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public synchronized void mount( String label, String location, IMount mount ) throws FileSystemException
|
|
{
|
|
if( mount == null )
|
|
{
|
|
throw new NullPointerException();
|
|
}
|
|
location = sanitizePath( location );
|
|
if( location.indexOf( ".." ) != -1 ) {
|
|
throw new FileSystemException( "Cannot mount below the root" );
|
|
}
|
|
mount( new MountWrapper( label, location, mount ) );
|
|
}
|
|
|
|
public synchronized void mountWritable( String label, String location, IWritableMount mount ) throws FileSystemException
|
|
{
|
|
if( mount == null )
|
|
{
|
|
throw new NullPointerException();
|
|
}
|
|
location = sanitizePath( location );
|
|
if( location.contains( ".." ) )
|
|
{
|
|
throw new FileSystemException( "Cannot mount below the root" );
|
|
}
|
|
mount( new MountWrapper( label, location, mount ) );
|
|
}
|
|
|
|
private synchronized void mount( MountWrapper wrapper ) throws FileSystemException
|
|
{
|
|
String location = wrapper.getLocation();
|
|
if( m_mounts.containsKey( location ) )
|
|
{
|
|
m_mounts.remove( location );
|
|
}
|
|
m_mounts.put( location, wrapper );
|
|
}
|
|
|
|
public synchronized void unmount( String path )
|
|
{
|
|
path = sanitizePath( path );
|
|
if( m_mounts.containsKey( path ) )
|
|
{
|
|
m_mounts.remove( path );
|
|
}
|
|
}
|
|
|
|
public synchronized String combine( String path, String childPath )
|
|
{
|
|
path = sanitizePath( path, true );
|
|
childPath = sanitizePath( childPath, true );
|
|
|
|
if( path.isEmpty() ) {
|
|
return childPath;
|
|
} else if( childPath.isEmpty() ) {
|
|
return path;
|
|
} else {
|
|
return sanitizePath( path + '/' + childPath, true );
|
|
}
|
|
}
|
|
|
|
public static String getDirectory( String path )
|
|
{
|
|
path = sanitizePath( path, true );
|
|
if( path.isEmpty() ) {
|
|
return "..";
|
|
}
|
|
|
|
int lastSlash = path.lastIndexOf('/');
|
|
if( lastSlash >= 0 ) {
|
|
return path.substring( 0, lastSlash );
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
public static String getName( String path )
|
|
{
|
|
path = sanitizePath( path, true );
|
|
if( path.isEmpty() ) {
|
|
return "root";
|
|
}
|
|
|
|
int lastSlash = path.lastIndexOf('/');
|
|
if( lastSlash >= 0 ) {
|
|
return path.substring( lastSlash + 1 );
|
|
} else {
|
|
return path;
|
|
}
|
|
}
|
|
|
|
public synchronized long getSize( String path ) throws FileSystemException
|
|
{
|
|
path = sanitizePath( path );
|
|
MountWrapper mount = getMount( path );
|
|
return mount.getSize( path );
|
|
}
|
|
|
|
public synchronized String[] list( String path ) throws FileSystemException
|
|
{
|
|
path = sanitizePath( path );
|
|
MountWrapper mount = getMount( path );
|
|
|
|
// Gets a list of the files in the mount
|
|
List<String> list = new ArrayList<String>();
|
|
mount.list( path, list );
|
|
|
|
// Add any mounts that are mounted at this location
|
|
Iterator<MountWrapper> it = m_mounts.values().iterator();
|
|
while( it.hasNext() ) {
|
|
MountWrapper otherMount = it.next();
|
|
if( getDirectory( otherMount.getLocation() ).equals( path ) ) {
|
|
list.add( getName( otherMount.getLocation() ) );
|
|
}
|
|
}
|
|
|
|
// Return list
|
|
String[] array = new String[ list.size() ];
|
|
list.toArray(array);
|
|
return array;
|
|
}
|
|
|
|
private void findIn( String dir, List<String> matches, Pattern wildPattern ) throws FileSystemException
|
|
{
|
|
String[] list = list( dir );
|
|
for( int i=0; i<list.length; ++i )
|
|
{
|
|
String entry = list[i];
|
|
String entryPath = dir.isEmpty() ? entry : (dir + "/" + entry);
|
|
if( wildPattern.matcher( entryPath ).matches() )
|
|
{
|
|
matches.add( entryPath );
|
|
}
|
|
if( isDir( entryPath ) )
|
|
{
|
|
findIn( entryPath, matches, wildPattern );
|
|
}
|
|
}
|
|
}
|
|
|
|
public synchronized String[] find( String wildPath ) throws FileSystemException
|
|
{
|
|
// Match all the files on the system
|
|
wildPath = sanitizePath( wildPath, true );
|
|
|
|
// If we don't have a wildcard at all just check the file exists
|
|
int starIndex = wildPath.indexOf( '*' );
|
|
if( starIndex == -1 )
|
|
{
|
|
return exists( wildPath ) ? new String[]{wildPath} : new String[0];
|
|
}
|
|
|
|
// Find the all non-wildcarded directories. For instance foo/bar/baz* -> foo/bar
|
|
int prevDir = wildPath.substring( 0, starIndex ).lastIndexOf( '/' );
|
|
String startDir = prevDir == -1 ? "" : wildPath.substring( 0, prevDir );
|
|
|
|
// If this isn't a directory then just abort
|
|
if( !isDir( startDir ) ) return new String[0];
|
|
|
|
// Scan as normal, starting from this directory
|
|
Pattern wildPattern = Pattern.compile( "^\\Q" + wildPath.replaceAll( "\\*", "\\\\E[^\\\\/]*\\\\Q" ) + "\\E$" );
|
|
List<String> matches = new ArrayList<String>();
|
|
findIn( startDir, matches, wildPattern );
|
|
|
|
// Return matches
|
|
String[] array = new String[ matches.size() ];
|
|
matches.toArray(array);
|
|
return array;
|
|
}
|
|
|
|
public synchronized boolean exists( String path ) throws FileSystemException
|
|
{
|
|
path = sanitizePath( path );
|
|
MountWrapper mount = getMount( path );
|
|
return mount.exists( path );
|
|
}
|
|
|
|
public synchronized boolean isDir( String path ) throws FileSystemException
|
|
{
|
|
path = sanitizePath( path );
|
|
MountWrapper mount = getMount( path );
|
|
return mount.isDirectory( path );
|
|
}
|
|
|
|
public synchronized boolean isReadOnly( String path ) throws FileSystemException
|
|
{
|
|
path = sanitizePath( path );
|
|
MountWrapper mount = getMount( path );
|
|
return mount.isReadOnly( path );
|
|
}
|
|
|
|
public synchronized String getMountLabel( String path ) throws FileSystemException
|
|
{
|
|
path = sanitizePath( path );
|
|
MountWrapper mount = getMount( path );
|
|
return mount.getLabel();
|
|
}
|
|
|
|
public synchronized void makeDir( String path ) throws FileSystemException
|
|
{
|
|
path = sanitizePath( path );
|
|
MountWrapper mount = getMount( path );
|
|
mount.makeDirectory( path );
|
|
}
|
|
|
|
public synchronized void delete( String path ) throws FileSystemException
|
|
{
|
|
path = sanitizePath( path );
|
|
MountWrapper mount = getMount( path );
|
|
mount.delete( path );
|
|
}
|
|
|
|
public synchronized void move( String sourcePath, String destPath ) throws FileSystemException
|
|
{
|
|
sourcePath = sanitizePath( sourcePath );
|
|
destPath = sanitizePath( destPath );
|
|
if( isReadOnly( sourcePath ) || isReadOnly( destPath ) ) {
|
|
throw new FileSystemException( "Access denied" );
|
|
}
|
|
if( !exists( sourcePath ) ) {
|
|
throw new FileSystemException( "No such file" );
|
|
}
|
|
if( exists( destPath ) ) {
|
|
throw new FileSystemException( "File exists" );
|
|
}
|
|
if( contains( sourcePath, destPath ) ) {
|
|
throw new FileSystemException( "Can't move a directory inside itself" );
|
|
}
|
|
copy( sourcePath, destPath );
|
|
delete( sourcePath );
|
|
}
|
|
|
|
public synchronized void copy( String sourcePath, String destPath ) throws FileSystemException
|
|
{
|
|
sourcePath = sanitizePath( sourcePath );
|
|
destPath = sanitizePath( destPath );
|
|
if( isReadOnly( destPath ) ) {
|
|
throw new FileSystemException( "Access denied" );
|
|
}
|
|
if( !exists( sourcePath ) ) {
|
|
throw new FileSystemException( "No such file" );
|
|
}
|
|
if( exists( destPath ) ) {
|
|
throw new FileSystemException( "File exists" );
|
|
}
|
|
if( contains( sourcePath, destPath ) ) {
|
|
throw new FileSystemException( "Can't copy a directory inside itself" );
|
|
}
|
|
copyRecursive( sourcePath, getMount( sourcePath ), destPath, getMount( destPath ) );
|
|
}
|
|
|
|
private synchronized void copyRecursive( String sourcePath, MountWrapper sourceMount, String destinationPath, MountWrapper destinationMount ) throws FileSystemException
|
|
{
|
|
if( !sourceMount.exists( sourcePath ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( sourceMount.isDirectory( sourcePath ) )
|
|
{
|
|
// Copy a directory:
|
|
// Make the new directory
|
|
destinationMount.makeDirectory( destinationPath );
|
|
|
|
// Copy the source contents into it
|
|
List<String> sourceChildren = new ArrayList<String>();
|
|
sourceMount.list( sourcePath, sourceChildren );
|
|
for( String child : sourceChildren )
|
|
{
|
|
copyRecursive(
|
|
combine( sourcePath, child ), sourceMount,
|
|
combine( destinationPath, child ), destinationMount
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Copy a file:
|
|
InputStream source = null;
|
|
OutputStream destination = null;
|
|
try
|
|
{
|
|
// Open both files
|
|
source = sourceMount.openForRead( sourcePath );
|
|
destination = destinationMount.openForWrite( destinationPath );
|
|
|
|
// Copy bytes as fast as we can
|
|
byte[] buffer = new byte[1024];
|
|
while( true )
|
|
{
|
|
int bytesRead = source.read( buffer );
|
|
if( bytesRead >= 0 )
|
|
{
|
|
destination.write( buffer, 0, bytesRead );
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch( IOException e )
|
|
{
|
|
throw new FileSystemException( e.getMessage() );
|
|
}
|
|
finally
|
|
{
|
|
// Close both files
|
|
if( source != null )
|
|
{
|
|
try {
|
|
source.close();
|
|
} catch( IOException e ) {
|
|
// nobody cares
|
|
}
|
|
}
|
|
if( destination != null )
|
|
{
|
|
try {
|
|
destination.close();
|
|
} catch( IOException e ) {
|
|
// nobody cares
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public synchronized IMountedFileNormal openForRead( String path ) throws FileSystemException
|
|
{
|
|
path = sanitizePath ( path );
|
|
MountWrapper mount = getMount( path );
|
|
InputStream stream = mount.openForRead( path );
|
|
if( stream != null )
|
|
{
|
|
InputStreamReader isr;
|
|
try
|
|
{
|
|
isr = new InputStreamReader( stream, "UTF-8" );
|
|
}
|
|
catch( UnsupportedEncodingException e )
|
|
{
|
|
isr = new InputStreamReader( stream );
|
|
}
|
|
final BufferedReader reader = new BufferedReader( isr );
|
|
IMountedFileNormal file = new IMountedFileNormal()
|
|
{
|
|
@Override
|
|
public String readLine() throws IOException
|
|
{
|
|
return reader.readLine();
|
|
}
|
|
|
|
@Override
|
|
public void write(String s, int off, int len, boolean newLine) throws IOException
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
@Override
|
|
public void close() throws IOException
|
|
{
|
|
synchronized( m_openFiles )
|
|
{
|
|
m_openFiles.remove( this );
|
|
reader.close();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void flush() throws IOException
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
};
|
|
synchronized( m_openFiles )
|
|
{
|
|
m_openFiles.add( file );
|
|
}
|
|
return file;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public synchronized IMountedFileNormal openForWrite( String path, boolean append ) throws FileSystemException
|
|
{
|
|
path = sanitizePath ( path );
|
|
MountWrapper mount = getMount( path );
|
|
OutputStream stream = append ? mount.openForAppend( path ) : mount.openForWrite( path );
|
|
if( stream != null )
|
|
{
|
|
OutputStreamWriter osw;
|
|
try
|
|
{
|
|
osw = new OutputStreamWriter( stream, "UTF-8" );
|
|
}
|
|
catch( UnsupportedEncodingException e )
|
|
{
|
|
osw = new OutputStreamWriter( stream );
|
|
}
|
|
final BufferedWriter writer = new BufferedWriter( osw );
|
|
IMountedFileNormal file = new IMountedFileNormal()
|
|
{
|
|
@Override
|
|
public String readLine() throws IOException
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
@Override
|
|
public void write( String s, int off, int len, boolean newLine ) throws IOException
|
|
{
|
|
writer.write( s, off, len );
|
|
if( newLine )
|
|
{
|
|
writer.newLine();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void close() throws IOException
|
|
{
|
|
synchronized( m_openFiles )
|
|
{
|
|
m_openFiles.remove( this );
|
|
writer.close();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void flush() throws IOException
|
|
{
|
|
writer.flush();
|
|
}
|
|
};
|
|
synchronized( m_openFiles )
|
|
{
|
|
m_openFiles.add( file );
|
|
}
|
|
return file;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public synchronized IMountedFileBinary openForBinaryRead( String path ) throws FileSystemException
|
|
{
|
|
path = sanitizePath ( path );
|
|
MountWrapper mount = getMount( path );
|
|
final InputStream stream = mount.openForRead( path );
|
|
if( stream != null )
|
|
{
|
|
IMountedFileBinary file = new IMountedFileBinary()
|
|
{
|
|
@Override
|
|
public int read() throws IOException
|
|
{
|
|
return stream.read();
|
|
}
|
|
|
|
@Override
|
|
public void write(int i) throws IOException
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
@Override
|
|
public void close() throws IOException
|
|
{
|
|
synchronized( m_openFiles )
|
|
{
|
|
m_openFiles.remove( this );
|
|
stream.close();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void flush() throws IOException
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
};
|
|
synchronized( m_openFiles )
|
|
{
|
|
m_openFiles.add( file );
|
|
}
|
|
return file;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public synchronized IMountedFileBinary openForBinaryWrite( String path, boolean append ) throws FileSystemException
|
|
{
|
|
path = sanitizePath ( path );
|
|
MountWrapper mount = getMount( path );
|
|
final OutputStream stream = append ? mount.openForAppend( path ) : mount.openForWrite( path );
|
|
if( stream != null )
|
|
{
|
|
IMountedFileBinary file = new IMountedFileBinary()
|
|
{
|
|
@Override
|
|
public int read() throws IOException
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
@Override
|
|
public void write(int i) throws IOException
|
|
{
|
|
stream.write(i);
|
|
}
|
|
|
|
@Override
|
|
public void close() throws IOException
|
|
{
|
|
synchronized( m_openFiles )
|
|
{
|
|
m_openFiles.remove( this );
|
|
stream.close();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void flush() throws IOException
|
|
{
|
|
stream.flush();
|
|
}
|
|
};
|
|
synchronized( m_openFiles )
|
|
{
|
|
m_openFiles.add( file );
|
|
}
|
|
return file;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public long getFreeSpace( String path ) throws FileSystemException
|
|
{
|
|
path = sanitizePath( path );
|
|
MountWrapper mount = getMount( path );
|
|
return mount.getFreeSpace();
|
|
}
|
|
|
|
private MountWrapper getMount( String path ) throws FileSystemException
|
|
{
|
|
// Return the deepest mount that contains a given path
|
|
Iterator<MountWrapper> it = m_mounts.values().iterator();
|
|
MountWrapper match = null;
|
|
int matchLength = 999;
|
|
while( it.hasNext() )
|
|
{
|
|
MountWrapper mount = it.next();
|
|
if( contains( mount.getLocation(), path ) ) {
|
|
int len = toLocal( path, mount.getLocation() ).length();
|
|
if( match == null || len < matchLength ) {
|
|
match = mount;
|
|
matchLength = len;
|
|
}
|
|
}
|
|
}
|
|
if( match == null )
|
|
{
|
|
throw new FileSystemException( "Invalid Path" );
|
|
}
|
|
return match;
|
|
}
|
|
|
|
private static String sanitizePath( String path )
|
|
{
|
|
return sanitizePath( path, false );
|
|
}
|
|
|
|
private static String sanitizePath( String path, boolean allowWildcards )
|
|
{
|
|
// Allow windowsy slashes
|
|
path = path.replace( '\\', '/' );
|
|
|
|
// Clean the path or illegal characters.
|
|
final char[] specialChars = {
|
|
'"', ':', '<', '>', '?', '|' // Sorted by ascii value (important)
|
|
};
|
|
|
|
StringBuilder cleanName = new StringBuilder();
|
|
for( int i = 0; i < path.length(); i++ ) {
|
|
char c = path.charAt(i);
|
|
if( c >= 32 && Arrays.binarySearch( specialChars, c ) < 0 && (allowWildcards || c != '*') )
|
|
{
|
|
cleanName.append((char)c);
|
|
}
|
|
}
|
|
path = cleanName.toString();
|
|
|
|
// Collapse the string into its component parts, removing ..'s
|
|
String[] parts = path.split("/");
|
|
Stack<String> outputParts = new Stack<String>();
|
|
for( int n=0; n<parts.length; ++n ) {
|
|
String part = parts[n];
|
|
if( part.length() == 0 || part.equals(".") )
|
|
{
|
|
// . is redundant
|
|
continue;
|
|
} else if( part.equals("..") || part.equals( "..." ) ) {
|
|
// .. or ... can cancel out the last folder entered
|
|
if( !outputParts.empty() ) {
|
|
String top = outputParts.peek();
|
|
if( !top.equals("..") ) {
|
|
outputParts.pop();
|
|
} else {
|
|
outputParts.push("..");
|
|
}
|
|
} else {
|
|
outputParts.push("..");
|
|
}
|
|
} else if (part.length() >= 255) {
|
|
// If part length > 255 and it is the last part
|
|
outputParts.push( part.substring(0, 255) );
|
|
} else {
|
|
// Anything else we add to the stack
|
|
outputParts.push(part);
|
|
}
|
|
}
|
|
|
|
// Recombine the output parts into a new string
|
|
StringBuilder result = new StringBuilder( "" );
|
|
Iterator<String> it = outputParts.iterator();
|
|
while( it.hasNext() ) {
|
|
String part = it.next();
|
|
result.append( part );
|
|
if( it.hasNext() ) {
|
|
result.append( '/' );
|
|
}
|
|
}
|
|
|
|
return result.toString();
|
|
}
|
|
|
|
public static boolean contains( String pathA, String pathB )
|
|
{
|
|
pathA = sanitizePath( pathA );
|
|
pathB = sanitizePath( pathB );
|
|
|
|
if( pathB.equals("..") )
|
|
{
|
|
return false;
|
|
}
|
|
else if ( pathB.startsWith("../") )
|
|
{
|
|
return false;
|
|
}
|
|
else if( pathB.equals( pathA ) )
|
|
{
|
|
return true;
|
|
}
|
|
else if( pathA.isEmpty() )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return pathB.startsWith( pathA + "/" );
|
|
}
|
|
}
|
|
|
|
public static String toLocal( String path, String location )
|
|
{
|
|
path = sanitizePath( path );
|
|
location = sanitizePath( location );
|
|
|
|
assert( contains( location, path ) );
|
|
String local = path.substring( location.length() );
|
|
if( local.startsWith("/") ) {
|
|
return local.substring( 1 );
|
|
} else {
|
|
return local;
|
|
}
|
|
}
|
|
}
|