mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-08-05 21:33:54 +00:00
Add fs.getCapacity and fs.attributes (#365)
- fs.getCapacity just returns the capacity of the current drive, if available. This will be nil on rom mounts. - fs.attributes returns an lfs like table of various file attributes. Currently, this contains: - access, modification, created: When this file was last accessed, modified and created. Time is measured in milliseconds since the epoch, same as os.epoch("utc") and what is accepted by os.date. - size: Same as fs.getSize - isDir: same as fs.isDir Closes #262
This commit is contained in:
parent
239bd769df
commit
0ffd5fcf85
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.filesystem;
|
||||||
|
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.nio.file.attribute.FileTime;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple version of {@link BasicFileAttributes}, which provides what information a {@link IMount} already exposes.
|
||||||
|
*/
|
||||||
|
final class FileAttributes implements BasicFileAttributes
|
||||||
|
{
|
||||||
|
private static final FileTime EPOCH = FileTime.from( Instant.EPOCH );
|
||||||
|
|
||||||
|
private final boolean isDirectory;
|
||||||
|
private final long size;
|
||||||
|
|
||||||
|
FileAttributes( boolean isDirectory, long size )
|
||||||
|
{
|
||||||
|
this.isDirectory = isDirectory;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileTime lastModifiedTime()
|
||||||
|
{
|
||||||
|
return EPOCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileTime lastAccessTime()
|
||||||
|
{
|
||||||
|
return EPOCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileTime creationTime()
|
||||||
|
{
|
||||||
|
return EPOCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRegularFile()
|
||||||
|
{
|
||||||
|
return !isDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDirectory()
|
||||||
|
{
|
||||||
|
return isDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSymbolicLink()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOther()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long size()
|
||||||
|
{
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object fileKey()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,4 +94,18 @@ public interface IMount
|
|||||||
{
|
{
|
||||||
return Channels.newChannel( openForRead( path ) );
|
return Channels.newChannel( openForRead( path ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get attributes about the given file.
|
||||||
|
*
|
||||||
|
* @param path The path to query.
|
||||||
|
* @return File attributes for the given file.
|
||||||
|
* @throws IOException If the file does not exist, or attributes could not be fetched.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
default BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException
|
||||||
|
{
|
||||||
|
if( !exists( path ) ) throw new FileOperationException( path, "No such file" );
|
||||||
|
return new FileAttributes( isDirectory( path ), getSize( path ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
import java.nio.channels.WritableByteChannel;
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
import java.util.OptionalLong;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a part of a virtual filesystem that can be mounted onto a computer using {@link IComputerAccess#mount(String, IMount)}
|
* Represents a part of a virtual filesystem that can be mounted onto a computer using {@link IComputerAccess#mount(String, IMount)}
|
||||||
@ -105,4 +106,16 @@ public interface IWritableMount extends IMount
|
|||||||
* @throws IOException If the remaining space could not be computed.
|
* @throws IOException If the remaining space could not be computed.
|
||||||
*/
|
*/
|
||||||
long getRemainingSpace() throws IOException;
|
long getRemainingSpace() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the capacity of this mount. This should be equal to the size of all files/directories on this mount, minus
|
||||||
|
* the {@link #getRemainingSpace()}.
|
||||||
|
*
|
||||||
|
* @return The capacity of this mount, in bytes.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
default OptionalLong getCapacity()
|
||||||
|
{
|
||||||
|
return OptionalLong.empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,10 @@ import java.io.BufferedReader;
|
|||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
import java.nio.channels.WritableByteChannel;
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.OptionalLong;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static dan200.computercraft.api.lua.ArgumentHelper.getString;
|
import static dan200.computercraft.api.lua.ArgumentHelper.getString;
|
||||||
@ -76,6 +80,8 @@ public class FSAPI implements ILuaAPI
|
|||||||
"getFreeSpace",
|
"getFreeSpace",
|
||||||
"find",
|
"find",
|
||||||
"getDir",
|
"getDir",
|
||||||
|
"getCapacity",
|
||||||
|
"attributes",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,9 +321,8 @@ public class FSAPI implements ILuaAPI
|
|||||||
throw new LuaException( e.getMessage() );
|
throw new LuaException( e.getMessage() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 14:
|
case 14: // find
|
||||||
{
|
{
|
||||||
// find
|
|
||||||
String path = getString( args, 0 );
|
String path = getString( args, 0 );
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -329,12 +334,42 @@ public class FSAPI implements ILuaAPI
|
|||||||
throw new LuaException( e.getMessage() );
|
throw new LuaException( e.getMessage() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 15:
|
case 15: // getDir
|
||||||
{
|
{
|
||||||
// getDir
|
|
||||||
String path = getString( args, 0 );
|
String path = getString( args, 0 );
|
||||||
return new Object[] { FileSystem.getDirectory( path ) };
|
return new Object[] { FileSystem.getDirectory( path ) };
|
||||||
}
|
}
|
||||||
|
case 16: // getCapacity
|
||||||
|
{
|
||||||
|
String path = getString( args, 0 );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OptionalLong capacity = m_fileSystem.getCapacity( path );
|
||||||
|
return new Object[] { capacity.isPresent() ? capacity.getAsLong() : null };
|
||||||
|
}
|
||||||
|
catch( FileSystemException e )
|
||||||
|
{
|
||||||
|
throw new LuaException( e.getMessage() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 17: // attributes
|
||||||
|
{
|
||||||
|
String path = getString( args, 0 );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BasicFileAttributes attributes = m_fileSystem.getAttributes( path );
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put( "modification", attributes.lastModifiedTime().toMillis() );
|
||||||
|
result.put( "created", attributes.creationTime().toMillis() );
|
||||||
|
result.put( "size", attributes.isDirectory() ? 0 : attributes.size() );
|
||||||
|
result.put( "isDir", attributes.isDirectory() );
|
||||||
|
return new Object[] { result };
|
||||||
|
}
|
||||||
|
catch( FileSystemException e )
|
||||||
|
{
|
||||||
|
throw new LuaException( e.getMessage() );
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
assert false;
|
assert false;
|
||||||
return null;
|
return null;
|
||||||
|
@ -12,6 +12,7 @@ import javax.annotation.Nonnull;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -143,4 +144,19 @@ public class ComboMount implements IMount
|
|||||||
}
|
}
|
||||||
throw new FileOperationException( path, "No such file" );
|
throw new FileOperationException( path, "No such file" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException
|
||||||
|
{
|
||||||
|
for( int i = m_parts.length - 1; i >= 0; --i )
|
||||||
|
{
|
||||||
|
IMount part = m_parts[i];
|
||||||
|
if( part.exists( path ) && !part.isDirectory( path ) )
|
||||||
|
{
|
||||||
|
return part.getAttributes( path );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new FileOperationException( path, "No such file" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,10 @@ import java.nio.channels.*;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.OpenOption;
|
import java.nio.file.OpenOption;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.OptionalLong;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class FileMount implements IWritableMount
|
public class FileMount implements IWritableMount
|
||||||
@ -224,6 +226,19 @@ public class FileMount implements IWritableMount
|
|||||||
throw new FileOperationException( path, "No such file" );
|
throw new FileOperationException( path, "No such file" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException
|
||||||
|
{
|
||||||
|
if( created() )
|
||||||
|
{
|
||||||
|
File file = getRealPath( path );
|
||||||
|
if( file.exists() ) return Files.readAttributes( file.toPath(), BasicFileAttributes.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new FileOperationException( path, "No such file" );
|
||||||
|
}
|
||||||
|
|
||||||
// IWritableMount implementation
|
// IWritableMount implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -360,6 +375,13 @@ public class FileMount implements IWritableMount
|
|||||||
return Math.max( m_capacity - m_usedSpace, 0 );
|
return Math.max( m_capacity - m_usedSpace, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public OptionalLong getCapacity()
|
||||||
|
{
|
||||||
|
return OptionalLong.of( m_capacity - MINIMUM_FILE_SIZE );
|
||||||
|
}
|
||||||
|
|
||||||
private File getRealPath( String path )
|
private File getRealPath( String path )
|
||||||
{
|
{
|
||||||
return new File( m_rootPath, path );
|
return new File( m_rootPath, path );
|
||||||
|
@ -7,7 +7,6 @@ package dan200.computercraft.core.filesystem;
|
|||||||
|
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.api.filesystem.FileOperationException;
|
|
||||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
import dan200.computercraft.api.filesystem.IFileSystem;
|
||||||
import dan200.computercraft.api.filesystem.IMount;
|
import dan200.computercraft.api.filesystem.IMount;
|
||||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||||
@ -23,6 +22,7 @@ import java.nio.channels.Channel;
|
|||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
import java.nio.channels.WritableByteChannel;
|
import java.nio.channels.WritableByteChannel;
|
||||||
import java.nio.file.AccessDeniedException;
|
import java.nio.file.AccessDeniedException;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -37,303 +37,8 @@ public class FileSystem
|
|||||||
*/
|
*/
|
||||||
private static final int MAX_COPY_DEPTH = 128;
|
private static final int MAX_COPY_DEPTH = 128;
|
||||||
|
|
||||||
private static class MountWrapper
|
|
||||||
{
|
|
||||||
private String m_label;
|
|
||||||
private String m_location;
|
|
||||||
|
|
||||||
private IMount m_mount;
|
|
||||||
private IWritableMount m_writableMount;
|
|
||||||
|
|
||||||
MountWrapper( String label, String location, IMount mount )
|
|
||||||
{
|
|
||||||
m_label = label;
|
|
||||||
m_location = location;
|
|
||||||
m_mount = mount;
|
|
||||||
m_writableMount = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 )
|
|
||||||
{
|
|
||||||
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 localExceptionOf( e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 localExceptionOf( path, "Not a directory" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( IOException e )
|
|
||||||
{
|
|
||||||
throw localExceptionOf( e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 localExceptionOf( path, "No such file" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( IOException e )
|
|
||||||
{
|
|
||||||
throw localExceptionOf( e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadableByteChannel openForRead( String path ) throws FileSystemException
|
|
||||||
{
|
|
||||||
path = toLocal( path );
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if( m_mount.exists( path ) && !m_mount.isDirectory( path ) )
|
|
||||||
{
|
|
||||||
return m_mount.openChannelForRead( path );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw localExceptionOf( path, "No such file" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( IOException e )
|
|
||||||
{
|
|
||||||
throw localExceptionOf( e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IWritableMount forwarders:
|
|
||||||
|
|
||||||
public void makeDirectory( String path ) throws FileSystemException
|
|
||||||
{
|
|
||||||
if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
|
|
||||||
|
|
||||||
path = toLocal( path );
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if( m_mount.exists( path ) )
|
|
||||||
{
|
|
||||||
if( !m_mount.isDirectory( path ) ) throw localExceptionOf( path, "File exists" );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_writableMount.makeDirectory( path );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( IOException e )
|
|
||||||
{
|
|
||||||
throw localExceptionOf( e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete( String path ) throws FileSystemException
|
|
||||||
{
|
|
||||||
if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
path = toLocal( path );
|
|
||||||
if( m_mount.exists( path ) )
|
|
||||||
{
|
|
||||||
m_writableMount.delete( path );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( AccessDeniedException e )
|
|
||||||
{
|
|
||||||
throw new FileSystemException( "Access denied" );
|
|
||||||
}
|
|
||||||
catch( IOException e )
|
|
||||||
{
|
|
||||||
throw localExceptionOf( e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public WritableByteChannel openForWrite( String path ) throws FileSystemException
|
|
||||||
{
|
|
||||||
if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
|
|
||||||
|
|
||||||
path = toLocal( path );
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if( m_mount.exists( path ) && m_mount.isDirectory( path ) )
|
|
||||||
{
|
|
||||||
throw localExceptionOf( path, "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.openChannelForWrite( path );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( AccessDeniedException e )
|
|
||||||
{
|
|
||||||
throw new FileSystemException( "Access denied" );
|
|
||||||
}
|
|
||||||
catch( IOException e )
|
|
||||||
{
|
|
||||||
throw localExceptionOf( e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public WritableByteChannel openForAppend( String path ) throws FileSystemException
|
|
||||||
{
|
|
||||||
if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
|
|
||||||
|
|
||||||
path = toLocal( path );
|
|
||||||
try
|
|
||||||
{
|
|
||||||
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.openChannelForWrite( path );
|
|
||||||
}
|
|
||||||
else if( m_mount.isDirectory( path ) )
|
|
||||||
{
|
|
||||||
throw localExceptionOf( path, "Cannot write to directory" );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return m_writableMount.openChannelForAppend( path );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( AccessDeniedException e )
|
|
||||||
{
|
|
||||||
throw new FileSystemException( "Access denied" );
|
|
||||||
}
|
|
||||||
catch( IOException e )
|
|
||||||
{
|
|
||||||
throw localExceptionOf( e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String toLocal( String path )
|
|
||||||
{
|
|
||||||
return FileSystem.toLocal( path, m_location );
|
|
||||||
}
|
|
||||||
|
|
||||||
private FileSystemException localExceptionOf( IOException e )
|
|
||||||
{
|
|
||||||
if( !m_location.isEmpty() && e instanceof FileOperationException )
|
|
||||||
{
|
|
||||||
FileOperationException ex = (FileOperationException) e;
|
|
||||||
if( ex.getFilename() != null ) return localExceptionOf( ex.getFilename(), ex.getMessage() );
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FileSystemException( e.getMessage() );
|
|
||||||
}
|
|
||||||
|
|
||||||
private FileSystemException localExceptionOf( String path, String message )
|
|
||||||
{
|
|
||||||
if( !m_location.isEmpty() ) path = path.isEmpty() ? m_location : m_location + "/" + path;
|
|
||||||
return exceptionOf( path, message );
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FileSystemException exceptionOf( String path, String message )
|
|
||||||
{
|
|
||||||
return new FileSystemException( "/" + path + ": " + message );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final FileSystemWrapperMount m_wrapper = new FileSystemWrapperMount( this );
|
private final FileSystemWrapperMount m_wrapper = new FileSystemWrapperMount( this );
|
||||||
private final Map<String, MountWrapper> m_mounts = new HashMap<>();
|
private final Map<String, MountWrapper> mounts = new HashMap<>();
|
||||||
|
|
||||||
private final HashMap<WeakReference<FileSystemWrapper<?>>, ChannelWrapper<?>> m_openFiles = new HashMap<>();
|
private final HashMap<WeakReference<FileSystemWrapper<?>>, ChannelWrapper<?>> m_openFiles = new HashMap<>();
|
||||||
private final ReferenceQueue<FileSystemWrapper<?>> m_openFileQueue = new ReferenceQueue<>();
|
private final ReferenceQueue<FileSystemWrapper<?>> m_openFileQueue = new ReferenceQueue<>();
|
||||||
@ -363,10 +68,7 @@ public class FileSystem
|
|||||||
{
|
{
|
||||||
if( mount == null ) throw new NullPointerException();
|
if( mount == null ) throw new NullPointerException();
|
||||||
location = sanitizePath( location );
|
location = sanitizePath( location );
|
||||||
if( location.contains( ".." ) )
|
if( location.contains( ".." ) ) throw new FileSystemException( "Cannot mount below the root" );
|
||||||
{
|
|
||||||
throw new FileSystemException( "Cannot mount below the root" );
|
|
||||||
}
|
|
||||||
mount( new MountWrapper( label, location, mount ) );
|
mount( new MountWrapper( label, location, mount ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,14 +89,13 @@ public class FileSystem
|
|||||||
private synchronized void mount( MountWrapper wrapper )
|
private synchronized void mount( MountWrapper wrapper )
|
||||||
{
|
{
|
||||||
String location = wrapper.getLocation();
|
String location = wrapper.getLocation();
|
||||||
m_mounts.remove( location );
|
mounts.remove( location );
|
||||||
m_mounts.put( location, wrapper );
|
mounts.put( location, wrapper );
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void unmount( String path )
|
public synchronized void unmount( String path )
|
||||||
{
|
{
|
||||||
path = sanitizePath( path );
|
mounts.remove( sanitizePath( path ) );
|
||||||
m_mounts.remove( path );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized String combine( String path, String childPath )
|
public synchronized String combine( String path, String childPath )
|
||||||
@ -438,27 +139,20 @@ public class FileSystem
|
|||||||
public static String getName( String path )
|
public static String getName( String path )
|
||||||
{
|
{
|
||||||
path = sanitizePath( path, true );
|
path = sanitizePath( path, true );
|
||||||
if( path.isEmpty() )
|
if( path.isEmpty() ) return "root";
|
||||||
{
|
|
||||||
return "root";
|
|
||||||
}
|
|
||||||
|
|
||||||
int lastSlash = path.lastIndexOf( '/' );
|
int lastSlash = path.lastIndexOf( '/' );
|
||||||
if( lastSlash >= 0 )
|
return lastSlash >= 0 ? path.substring( lastSlash + 1 ) : path;
|
||||||
{
|
|
||||||
return path.substring( lastSlash + 1 );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized long getSize( String path ) throws FileSystemException
|
public synchronized long getSize( String path ) throws FileSystemException
|
||||||
{
|
{
|
||||||
path = sanitizePath( path );
|
return getMount( sanitizePath( path ) ).getSize( sanitizePath( path ) );
|
||||||
MountWrapper mount = getMount( path );
|
}
|
||||||
return mount.getSize( path );
|
|
||||||
|
public synchronized BasicFileAttributes getAttributes( String path ) throws FileSystemException
|
||||||
|
{
|
||||||
|
return getMount( sanitizePath( path ) ).getAttributes( sanitizePath( path ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized String[] list( String path ) throws FileSystemException
|
public synchronized String[] list( String path ) throws FileSystemException
|
||||||
@ -471,7 +165,7 @@ public class FileSystem
|
|||||||
mount.list( path, list );
|
mount.list( path, list );
|
||||||
|
|
||||||
// Add any mounts that are mounted at this location
|
// Add any mounts that are mounted at this location
|
||||||
for( MountWrapper otherMount : m_mounts.values() )
|
for( MountWrapper otherMount : mounts.values() )
|
||||||
{
|
{
|
||||||
if( getDirectory( otherMount.getLocation() ).equals( path ) )
|
if( getDirectory( otherMount.getLocation() ).equals( path ) )
|
||||||
{
|
{
|
||||||
@ -733,17 +427,25 @@ public class FileSystem
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getFreeSpace( String path ) throws FileSystemException
|
public synchronized long getFreeSpace( String path ) throws FileSystemException
|
||||||
{
|
{
|
||||||
path = sanitizePath( path );
|
path = sanitizePath( path );
|
||||||
MountWrapper mount = getMount( path );
|
MountWrapper mount = getMount( path );
|
||||||
return mount.getFreeSpace();
|
return mount.getFreeSpace();
|
||||||
}
|
}
|
||||||
|
|
||||||
private MountWrapper getMount( String path ) throws FileSystemException
|
@Nonnull
|
||||||
|
public synchronized OptionalLong getCapacity( String path ) throws FileSystemException
|
||||||
|
{
|
||||||
|
path = sanitizePath( path );
|
||||||
|
MountWrapper mount = getMount( path );
|
||||||
|
return mount.getCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized MountWrapper getMount( String path ) throws FileSystemException
|
||||||
{
|
{
|
||||||
// Return the deepest mount that contains a given path
|
// Return the deepest mount that contains a given path
|
||||||
Iterator<MountWrapper> it = m_mounts.values().iterator();
|
Iterator<MountWrapper> it = mounts.values().iterator();
|
||||||
MountWrapper match = null;
|
MountWrapper match = null;
|
||||||
int matchLength = 999;
|
int matchLength = 999;
|
||||||
while( it.hasNext() )
|
while( it.hasNext() )
|
||||||
|
@ -23,6 +23,8 @@ import java.lang.ref.ReferenceQueue;
|
|||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.nio.file.attribute.FileTime;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -221,6 +223,20 @@ public class JarMount implements IMount
|
|||||||
throw new FileOperationException( path, "No such file" );
|
throw new FileOperationException( path, "No such file" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException
|
||||||
|
{
|
||||||
|
FileEntry file = get( path );
|
||||||
|
if( file != null )
|
||||||
|
{
|
||||||
|
ZipEntry entry = zip.getEntry( file.path );
|
||||||
|
if( entry != null ) return new ZipEntryAttributes( entry );
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new FileOperationException( path, "No such file" );
|
||||||
|
}
|
||||||
|
|
||||||
private static class FileEntry
|
private static class FileEntry
|
||||||
{
|
{
|
||||||
String path;
|
String path;
|
||||||
@ -261,4 +277,68 @@ public class JarMount implements IMount
|
|||||||
Reference<? extends JarMount> next;
|
Reference<? extends JarMount> next;
|
||||||
while( (next = MOUNT_QUEUE.poll()) != null ) IoUtil.closeQuietly( ((MountReference) next).file );
|
while( (next = MOUNT_QUEUE.poll()) != null ) IoUtil.closeQuietly( ((MountReference) next).file );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ZipEntryAttributes implements BasicFileAttributes
|
||||||
|
{
|
||||||
|
private final ZipEntry entry;
|
||||||
|
|
||||||
|
ZipEntryAttributes( ZipEntry entry )
|
||||||
|
{
|
||||||
|
this.entry = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileTime lastModifiedTime()
|
||||||
|
{
|
||||||
|
return entry.getLastModifiedTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileTime lastAccessTime()
|
||||||
|
{
|
||||||
|
return entry.getLastAccessTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileTime creationTime()
|
||||||
|
{
|
||||||
|
return entry.getCreationTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRegularFile()
|
||||||
|
{
|
||||||
|
return !entry.isDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDirectory()
|
||||||
|
{
|
||||||
|
return entry.isDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSymbolicLink()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOther()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long size()
|
||||||
|
{
|
||||||
|
return entry.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object fileKey()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,312 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.core.filesystem;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.filesystem.FileOperationException;
|
||||||
|
import dan200.computercraft.api.filesystem.IMount;
|
||||||
|
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
import java.nio.file.AccessDeniedException;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.OptionalLong;
|
||||||
|
|
||||||
|
class MountWrapper
|
||||||
|
{
|
||||||
|
private String label;
|
||||||
|
private String location;
|
||||||
|
|
||||||
|
private IMount mount;
|
||||||
|
private IWritableMount writableMount;
|
||||||
|
|
||||||
|
MountWrapper( String label, String location, IMount mount )
|
||||||
|
{
|
||||||
|
this.label = label;
|
||||||
|
this.location = location;
|
||||||
|
this.mount = mount;
|
||||||
|
writableMount = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MountWrapper( String label, String location, IWritableMount mount )
|
||||||
|
{
|
||||||
|
this( label, location, (IMount) mount );
|
||||||
|
writableMount = mount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLabel()
|
||||||
|
{
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocation()
|
||||||
|
{
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getFreeSpace()
|
||||||
|
{
|
||||||
|
if( writableMount == null ) return 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return writableMount.getRemainingSpace();
|
||||||
|
}
|
||||||
|
catch( IOException e )
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionalLong getCapacity()
|
||||||
|
{
|
||||||
|
return writableMount == null ? OptionalLong.empty() : writableMount.getCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReadOnly( String path )
|
||||||
|
{
|
||||||
|
return writableMount == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean exists( String path ) throws FileSystemException
|
||||||
|
{
|
||||||
|
path = toLocal( path );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return mount.exists( path );
|
||||||
|
}
|
||||||
|
catch( IOException e )
|
||||||
|
{
|
||||||
|
throw new FileSystemException( e.getMessage() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDirectory( String path ) throws FileSystemException
|
||||||
|
{
|
||||||
|
path = toLocal( path );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return mount.exists( path ) && mount.isDirectory( path );
|
||||||
|
}
|
||||||
|
catch( IOException e )
|
||||||
|
{
|
||||||
|
throw localExceptionOf( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void list( String path, List<String> contents ) throws FileSystemException
|
||||||
|
{
|
||||||
|
path = toLocal( path );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if( !mount.exists( path ) || !mount.isDirectory( path ) )
|
||||||
|
{
|
||||||
|
throw localExceptionOf( path, "Not a directory" );
|
||||||
|
}
|
||||||
|
|
||||||
|
mount.list( path, contents );
|
||||||
|
}
|
||||||
|
catch( IOException e )
|
||||||
|
{
|
||||||
|
throw localExceptionOf( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSize( String path ) throws FileSystemException
|
||||||
|
{
|
||||||
|
path = toLocal( path );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if( !mount.exists( path ) ) throw localExceptionOf( path, "No such file" );
|
||||||
|
return mount.isDirectory( path ) ? 0 : mount.getSize( path );
|
||||||
|
}
|
||||||
|
catch( IOException e )
|
||||||
|
{
|
||||||
|
throw localExceptionOf( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public BasicFileAttributes getAttributes( String path ) throws FileSystemException
|
||||||
|
{
|
||||||
|
path = toLocal( path );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if( !mount.exists( path ) ) throw localExceptionOf( path, "No such file" );
|
||||||
|
return mount.getAttributes( path );
|
||||||
|
}
|
||||||
|
catch( IOException e )
|
||||||
|
{
|
||||||
|
throw localExceptionOf( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadableByteChannel openForRead( String path ) throws FileSystemException
|
||||||
|
{
|
||||||
|
path = toLocal( path );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if( mount.exists( path ) && !mount.isDirectory( path ) )
|
||||||
|
{
|
||||||
|
return mount.openChannelForRead( path );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw localExceptionOf( path, "No such file" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch( IOException e )
|
||||||
|
{
|
||||||
|
throw localExceptionOf( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void makeDirectory( String path ) throws FileSystemException
|
||||||
|
{
|
||||||
|
if( writableMount == null ) throw exceptionOf( path, "Access denied" );
|
||||||
|
|
||||||
|
path = toLocal( path );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if( mount.exists( path ) )
|
||||||
|
{
|
||||||
|
if( !mount.isDirectory( path ) ) throw localExceptionOf( path, "File exists" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writableMount.makeDirectory( path );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch( IOException e )
|
||||||
|
{
|
||||||
|
throw localExceptionOf( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete( String path ) throws FileSystemException
|
||||||
|
{
|
||||||
|
if( writableMount == null ) throw exceptionOf( path, "Access denied" );
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
path = toLocal( path );
|
||||||
|
if( mount.exists( path ) )
|
||||||
|
{
|
||||||
|
writableMount.delete( path );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch( AccessDeniedException e )
|
||||||
|
{
|
||||||
|
throw new FileSystemException( "Access denied" );
|
||||||
|
}
|
||||||
|
catch( IOException e )
|
||||||
|
{
|
||||||
|
throw localExceptionOf( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public WritableByteChannel openForWrite( String path ) throws FileSystemException
|
||||||
|
{
|
||||||
|
if( writableMount == null ) throw exceptionOf( path, "Access denied" );
|
||||||
|
|
||||||
|
path = toLocal( path );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if( mount.exists( path ) && mount.isDirectory( path ) )
|
||||||
|
{
|
||||||
|
throw localExceptionOf( path, "Cannot write to directory" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( !path.isEmpty() )
|
||||||
|
{
|
||||||
|
String dir = FileSystem.getDirectory( path );
|
||||||
|
if( !dir.isEmpty() && !mount.exists( path ) )
|
||||||
|
{
|
||||||
|
writableMount.makeDirectory( dir );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return writableMount.openChannelForWrite( path );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch( AccessDeniedException e )
|
||||||
|
{
|
||||||
|
throw new FileSystemException( "Access denied" );
|
||||||
|
}
|
||||||
|
catch( IOException e )
|
||||||
|
{
|
||||||
|
throw localExceptionOf( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public WritableByteChannel openForAppend( String path ) throws FileSystemException
|
||||||
|
{
|
||||||
|
if( writableMount == null ) throw exceptionOf( path, "Access denied" );
|
||||||
|
|
||||||
|
path = toLocal( path );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if( !mount.exists( path ) )
|
||||||
|
{
|
||||||
|
if( !path.isEmpty() )
|
||||||
|
{
|
||||||
|
String dir = FileSystem.getDirectory( path );
|
||||||
|
if( !dir.isEmpty() && !mount.exists( path ) )
|
||||||
|
{
|
||||||
|
writableMount.makeDirectory( dir );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return writableMount.openChannelForWrite( path );
|
||||||
|
}
|
||||||
|
else if( mount.isDirectory( path ) )
|
||||||
|
{
|
||||||
|
throw localExceptionOf( path, "Cannot write to directory" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return writableMount.openChannelForAppend( path );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch( AccessDeniedException e )
|
||||||
|
{
|
||||||
|
throw new FileSystemException( "Access denied" );
|
||||||
|
}
|
||||||
|
catch( IOException e )
|
||||||
|
{
|
||||||
|
throw localExceptionOf( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toLocal( String path )
|
||||||
|
{
|
||||||
|
return FileSystem.toLocal( path, location );
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileSystemException localExceptionOf( IOException e )
|
||||||
|
{
|
||||||
|
if( !location.isEmpty() && e instanceof FileOperationException )
|
||||||
|
{
|
||||||
|
FileOperationException ex = (FileOperationException) e;
|
||||||
|
if( ex.getFilename() != null ) return localExceptionOf( ex.getFilename(), ex.getMessage() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FileSystemException( e.getMessage() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileSystemException localExceptionOf( String path, String message )
|
||||||
|
{
|
||||||
|
if( !location.isEmpty() ) path = path.isEmpty() ? location : location + "/" + path;
|
||||||
|
return exceptionOf( path, message );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FileSystemException exceptionOf( String path, String message )
|
||||||
|
{
|
||||||
|
return new FileSystemException( "/" + path + ": " + message );
|
||||||
|
}
|
||||||
|
}
|
@ -11,43 +11,42 @@ import javax.annotation.Nonnull;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class SubMount implements IMount
|
public class SubMount implements IMount
|
||||||
{
|
{
|
||||||
private IMount m_parent;
|
private IMount parent;
|
||||||
private String m_subPath;
|
private String subPath;
|
||||||
|
|
||||||
public SubMount( IMount parent, String subPath )
|
public SubMount( IMount parent, String subPath )
|
||||||
{
|
{
|
||||||
m_parent = parent;
|
this.parent = parent;
|
||||||
m_subPath = subPath;
|
this.subPath = subPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMount implementation
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists( @Nonnull String path ) throws IOException
|
public boolean exists( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
return m_parent.exists( getFullPath( path ) );
|
return parent.exists( getFullPath( path ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirectory( @Nonnull String path ) throws IOException
|
public boolean isDirectory( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
return m_parent.isDirectory( getFullPath( path ) );
|
return parent.isDirectory( getFullPath( path ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
||||||
{
|
{
|
||||||
m_parent.list( getFullPath( path ), contents );
|
parent.list( getFullPath( path ), contents );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getSize( @Nonnull String path ) throws IOException
|
public long getSize( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
return m_parent.getSize( getFullPath( path ) );
|
return parent.getSize( getFullPath( path ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@ -55,25 +54,25 @@ public class SubMount implements IMount
|
|||||||
@Deprecated
|
@Deprecated
|
||||||
public InputStream openForRead( @Nonnull String path ) throws IOException
|
public InputStream openForRead( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
return m_parent.openForRead( getFullPath( path ) );
|
return parent.openForRead( getFullPath( path ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
|
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
return m_parent.openChannelForRead( getFullPath( path ) );
|
return parent.openChannelForRead( getFullPath( path ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException
|
||||||
|
{
|
||||||
|
return parent.getAttributes( getFullPath( path ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getFullPath( String path )
|
private String getFullPath( String path )
|
||||||
{
|
{
|
||||||
if( path.isEmpty() )
|
return path.isEmpty() ? subPath : subPath + "/" + path;
|
||||||
{
|
|
||||||
return m_subPath;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return m_subPath + "/" + path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ public class ComputerTestDelegate
|
|||||||
ComputerCraft.log = LogManager.getLogger( ComputerCraft.MOD_ID );
|
ComputerCraft.log = LogManager.getLogger( ComputerCraft.MOD_ID );
|
||||||
|
|
||||||
Terminal term = new Terminal( 78, 20 );
|
Terminal term = new Terminal( 78, 20 );
|
||||||
IWritableMount mount = new FileMount( new File( "test-files/mount" ), Long.MAX_VALUE );
|
IWritableMount mount = new FileMount( new File( "test-files/mount" ), 10_000_000 );
|
||||||
|
|
||||||
// Remove any existing files
|
// Remove any existing files
|
||||||
List<String> children = new ArrayList<>();
|
List<String> children = new ArrayList<>();
|
||||||
|
@ -149,4 +149,44 @@ describe("The fs library", function()
|
|||||||
expect.error(fs.move, "rom", "test-files"):eq("Access denied")
|
expect.error(fs.move, "rom", "test-files"):eq("Access denied")
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe("fs.getCapacity", function()
|
||||||
|
it("returns nil on read-only mounts", function()
|
||||||
|
expect(fs.getCapacity("rom")):eq(nil)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("returns the capacity on the root mount", function()
|
||||||
|
expect(fs.getCapacity("")):eq(10000000)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("fs.attributes", function()
|
||||||
|
it("errors on non-existent files", function()
|
||||||
|
expect.error(fs.attributes, "xuxu_nao_existe"):eq("/xuxu_nao_existe: No such file")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("returns information about read-only mounts", function()
|
||||||
|
expect(fs.attributes("rom")):matches { isDir = true, size = 0 }
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("returns information about files", function()
|
||||||
|
local now = os.epoch("utc")
|
||||||
|
|
||||||
|
fs.delete("/tmp/basic-file")
|
||||||
|
local h = fs.open("/tmp/basic-file", "w")
|
||||||
|
h.write("A reasonably sized string")
|
||||||
|
h.close()
|
||||||
|
|
||||||
|
local attributes = fs.attributes("tmp/basic-file")
|
||||||
|
expect(attributes):matches { isDir = false, size = 25 }
|
||||||
|
|
||||||
|
if attributes.created - now >= 1000 then
|
||||||
|
fail(("Expected created time (%d) to be within 1000ms of now (%d"):format(attributes.created, now))
|
||||||
|
end
|
||||||
|
|
||||||
|
if attributes.modification - now >= 1000 then
|
||||||
|
fail(("Expected modification time (%d) to be within 1000ms of now (%d"):format(attributes.modification, now))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user