1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-12 11:10:29 +00:00

Un-localise paths in mount error messages

This is probably a little more complex than it needs to be, as we try to
handle the mounts as well (which generally don't error).

Fixes #229
This commit is contained in:
SquidDev 2019-06-03 19:58:16 +01:00
parent 3406ba3ebf
commit 6f1b740c8f
7 changed files with 211 additions and 61 deletions

View File

@ -0,0 +1,41 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.api.filesystem;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Objects;
/**
* An {@link IOException} which occurred on a specific file.
*
* This may be thrown from a {@link IMount} or {@link IWritableMount} to give more information about a failure.
*/
public class FileOperationException extends IOException
{
private static final long serialVersionUID = -8809108200853029849L;
private String filename;
public FileOperationException( @Nullable String filename, @Nonnull String message )
{
super( Objects.requireNonNull( message, "message cannot be null" ) );
this.filename = filename;
}
public FileOperationException( String message )
{
super( Objects.requireNonNull( message, "message cannot be null" ) );
}
@Nullable
public String getFilename()
{
return filename;
}
}

View File

@ -6,6 +6,7 @@
package dan200.computercraft.core.filesystem; package dan200.computercraft.core.filesystem;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -95,7 +96,7 @@ public class ComboMount implements IMount
} }
else else
{ {
throw new IOException( "/" + path + ": Not a directory" ); throw new FileOperationException( path, "Not a directory" );
} }
} }
@ -110,7 +111,7 @@ public class ComboMount implements IMount
return part.getSize( path ); return part.getSize( path );
} }
} }
throw new IOException( "/" + path + ": No such file" ); throw new FileOperationException( path, "No such file" );
} }
@Nonnull @Nonnull
@ -126,7 +127,7 @@ public class ComboMount implements IMount
return part.openForRead( path ); return part.openForRead( path );
} }
} }
throw new IOException( "/" + path + ": No such file" ); throw new FileOperationException( path, "No such file" );
} }
@Nonnull @Nonnull
@ -141,6 +142,6 @@ public class ComboMount implements IMount
return part.openChannelForRead( path ); return part.openChannelForRead( path );
} }
} }
throw new IOException( "/" + path + ": No such file" ); throw new FileOperationException( path, "No such file" );
} }
} }

View File

@ -6,6 +6,7 @@
package dan200.computercraft.core.filesystem; package dan200.computercraft.core.filesystem;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -44,7 +45,7 @@ public class EmptyMount implements IMount
@Deprecated @Deprecated
public InputStream openForRead( @Nonnull String path ) throws IOException public InputStream openForRead( @Nonnull String path ) throws IOException
{ {
throw new IOException( "/" + path + ": No such file" ); throw new FileOperationException( path, "No such file" );
} }
@Nonnull @Nonnull
@ -52,6 +53,6 @@ public class EmptyMount implements IMount
@Deprecated @Deprecated
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
{ {
throw new IOException( "/" + path + ": No such file" ); throw new FileOperationException( path, "No such file" );
} }
} }

View File

@ -7,6 +7,7 @@
package dan200.computercraft.core.filesystem; package dan200.computercraft.core.filesystem;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.filesystem.IWritableMount;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -167,12 +168,12 @@ public class FileMount implements IWritableMount
{ {
if( !created() ) if( !created() )
{ {
if( !path.isEmpty() ) throw new IOException( "/" + path + ": Not a directory" ); if( !path.isEmpty() ) throw new FileOperationException( path, "Not a directory" );
return; return;
} }
File file = getRealPath( path ); File file = getRealPath( path );
if( !file.exists() || !file.isDirectory() ) throw new IOException( "/" + path + ": Not a directory" ); if( !file.exists() || !file.isDirectory() ) throw new FileOperationException( path, "Not a directory" );
String[] paths = file.list(); String[] paths = file.list();
for( String subPath : paths ) for( String subPath : paths )
@ -194,7 +195,7 @@ public class FileMount implements IWritableMount
if( file.exists() ) return file.isDirectory() ? 0 : file.length(); if( file.exists() ) return file.isDirectory() ? 0 : file.length();
} }
throw new IOException( "/" + path + ": No such file" ); throw new FileOperationException( path, "No such file" );
} }
@Nonnull @Nonnull
@ -208,7 +209,7 @@ public class FileMount implements IWritableMount
if( file.exists() && !file.isDirectory() ) return new FileInputStream( file ); if( file.exists() && !file.isDirectory() ) return new FileInputStream( file );
} }
throw new IOException( "/" + path + ": No such file" ); throw new FileOperationException( path, "No such file" );
} }
@Nonnull @Nonnull
@ -221,7 +222,7 @@ public class FileMount implements IWritableMount
if( file.exists() && !file.isDirectory() ) return FileChannel.open( file.toPath(), READ_OPTIONS ); if( file.exists() && !file.isDirectory() ) return FileChannel.open( file.toPath(), READ_OPTIONS );
} }
throw new IOException( "/" + path + ": No such file" ); throw new FileOperationException( path, "No such file" );
} }
// IWritableMount implementation // IWritableMount implementation
@ -233,7 +234,7 @@ public class FileMount implements IWritableMount
File file = getRealPath( path ); File file = getRealPath( path );
if( file.exists() ) if( file.exists() )
{ {
if( !file.isDirectory() ) throw new IOException( "/" + path + ": File exists" ); if( !file.isDirectory() ) throw new FileOperationException( path, "File exists" );
return; return;
} }
@ -247,7 +248,7 @@ public class FileMount implements IWritableMount
if( getRemainingSpace() < dirsToCreate * MINIMUM_FILE_SIZE ) if( getRemainingSpace() < dirsToCreate * MINIMUM_FILE_SIZE )
{ {
throw new IOException( "/" + path + ": Out of space" ); throw new FileOperationException( path, "Out of space" );
} }
if( file.mkdirs() ) if( file.mkdirs() )
@ -256,14 +257,14 @@ public class FileMount implements IWritableMount
} }
else else
{ {
throw new IOException( "/" + path + ": Access denied" ); throw new FileOperationException( path, "Access denied" );
} }
} }
@Override @Override
public void delete( @Nonnull String path ) throws IOException public void delete( @Nonnull String path ) throws IOException
{ {
if( path.isEmpty() ) throw new IOException( "/" + path + ": Access denied" ); if( path.isEmpty() ) throw new FileOperationException( path, "Access denied" );
if( created() ) if( created() )
{ {
@ -319,7 +320,7 @@ public class FileMount implements IWritableMount
{ {
create(); create();
File file = getRealPath( path ); File file = getRealPath( path );
if( file.exists() && file.isDirectory() ) throw new IOException( "/" + path + ": Cannot write to directory" ); if( file.exists() && file.isDirectory() ) throw new FileOperationException( path, "Cannot write to directory" );
if( file.exists() ) if( file.exists() )
{ {
@ -327,7 +328,7 @@ public class FileMount implements IWritableMount
} }
else if( getRemainingSpace() < MINIMUM_FILE_SIZE ) else if( getRemainingSpace() < MINIMUM_FILE_SIZE )
{ {
throw new IOException( "/" + path + ": Out of space" ); throw new FileOperationException( path, "Out of space" );
} }
m_usedSpace += MINIMUM_FILE_SIZE; m_usedSpace += MINIMUM_FILE_SIZE;
@ -340,12 +341,12 @@ public class FileMount implements IWritableMount
{ {
if( !created() ) if( !created() )
{ {
throw new IOException( "/" + path + ": No such file" ); throw new FileOperationException( path, "No such file" );
} }
File file = getRealPath( path ); File file = getRealPath( path );
if( !file.exists() ) throw new IOException( "/" + path + ": No such file" ); if( !file.exists() ) throw new FileOperationException( path, "No such file" );
if( file.isDirectory() ) throw new IOException( "/" + path + ": Cannot write to directory" ); if( file.isDirectory() ) throw new FileOperationException( path, "Cannot write to directory" );
// Allowing seeking when appending is not recommended, so we use a separate channel. // Allowing seeking when appending is not recommended, so we use a separate channel.
return new WritableCountingChannel( return new WritableCountingChannel(

View File

@ -8,6 +8,7 @@ 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;
@ -107,7 +108,7 @@ public class FileSystem
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
@ -122,12 +123,12 @@ public class FileSystem
} }
else else
{ {
throw new FileSystemException( "/" + path + ": Not a directory" ); throw localExceptionOf( path, "Not a directory" );
} }
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
@ -149,12 +150,12 @@ public class FileSystem
} }
else else
{ {
throw new FileSystemException( "/" + path + ": No such file" ); throw localExceptionOf( path, "No such file" );
} }
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
@ -169,12 +170,12 @@ public class FileSystem
} }
else else
{ {
throw new FileSystemException( "/" + path + ": No such file" ); throw localExceptionOf( path, "No such file" );
} }
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
@ -182,19 +183,14 @@ public class FileSystem
public void makeDirectory( String path ) throws FileSystemException public void makeDirectory( String path ) throws FileSystemException
{ {
if( m_writableMount == null ) if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
{
throw new FileSystemException( "/" + path + ": Access denied" ); path = toLocal( path );
}
try try
{ {
path = toLocal( path );
if( m_mount.exists( path ) ) if( m_mount.exists( path ) )
{ {
if( !m_mount.isDirectory( path ) ) if( !m_mount.isDirectory( path ) ) throw localExceptionOf( path, "File exists" );
{
throw new FileSystemException( "/" + path + ": File exists" );
}
} }
else else
{ {
@ -203,16 +199,14 @@ public class FileSystem
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
public void delete( String path ) throws FileSystemException public void delete( String path ) throws FileSystemException
{ {
if( m_writableMount == null ) if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
{
throw new FileSystemException( "/" + path + ": Access denied" );
}
try try
{ {
path = toLocal( path ); path = toLocal( path );
@ -227,22 +221,20 @@ public class FileSystem
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
public WritableByteChannel openForWrite( String path ) throws FileSystemException public WritableByteChannel openForWrite( String path ) throws FileSystemException
{ {
if( m_writableMount == null ) if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
{
throw new FileSystemException( "/" + path + ": Access denied" ); path = toLocal( path );
}
try try
{ {
path = toLocal( path );
if( m_mount.exists( path ) && m_mount.isDirectory( path ) ) if( m_mount.exists( path ) && m_mount.isDirectory( path ) )
{ {
throw new FileSystemException( "/" + path + ": Cannot write to directory" ); throw localExceptionOf( path, "Cannot write to directory" );
} }
else else
{ {
@ -263,19 +255,17 @@ public class FileSystem
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
public WritableByteChannel openForAppend( String path ) throws FileSystemException public WritableByteChannel openForAppend( String path ) throws FileSystemException
{ {
if( m_writableMount == null ) if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
{
throw new FileSystemException( "/" + path + ": Access denied" ); path = toLocal( path );
}
try try
{ {
path = toLocal( path );
if( !m_mount.exists( path ) ) if( !m_mount.exists( path ) )
{ {
if( !path.isEmpty() ) if( !path.isEmpty() )
@ -290,7 +280,7 @@ public class FileSystem
} }
else if( m_mount.isDirectory( path ) ) else if( m_mount.isDirectory( path ) )
{ {
throw new FileSystemException( "/" + path + ": Cannot write to directory" ); throw localExceptionOf( path, "Cannot write to directory" );
} }
else else
{ {
@ -303,16 +293,36 @@ public class FileSystem
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
// private members
private String toLocal( String path ) private String toLocal( String path )
{ {
return FileSystem.toLocal( path, m_location ); 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 );

View File

@ -9,6 +9,7 @@ package dan200.computercraft.core.filesystem;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.core.apis.handles.ArrayByteChannel; import dan200.computercraft.core.apis.handles.ArrayByteChannel;
import dan200.computercraft.shared.util.IoUtil; import dan200.computercraft.shared.util.IoUtil;
@ -166,7 +167,7 @@ public class JarMount implements IMount
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
{ {
FileEntry file = get( path ); FileEntry file = get( path );
if( file == null || !file.isDirectory() ) throw new IOException( "/" + path + ": Not a directory" ); if( file == null || !file.isDirectory() ) throw new FileOperationException( path, "Not a directory" );
file.list( contents ); file.list( contents );
} }
@ -176,7 +177,7 @@ public class JarMount implements IMount
{ {
FileEntry file = get( path ); FileEntry file = get( path );
if( file != null ) return file.size; if( file != null ) return file.size;
throw new IOException( "/" + path + ": No such file" ); throw new FileOperationException( path, "No such file" );
} }
@Nonnull @Nonnull
@ -218,7 +219,7 @@ public class JarMount implements IMount
} }
} }
throw new IOException( "/" + path + ": No such file" ); throw new FileOperationException( path, "No such file" );
} }
private static class FileEntry private static class FileEntry

View File

@ -11,4 +11,99 @@ describe("The fs library", function()
expect.error(fs.complete, "", "", true, 1):eq("bad argument #4 (expected boolean, got number)") expect.error(fs.complete, "", "", true, 1):eq("bad argument #4 (expected boolean, got number)")
end) end)
end) end)
describe("fs.list", function()
it("fails on files", function()
expect.error(fs.list, "rom/startup.lua"):eq("/rom/startup.lua: Not a directory")
expect.error(fs.list, "startup.lua"):eq("/startup.lua: Not a directory")
end)
it("fails on non-existent nodes", function()
expect.error(fs.list, "rom/x"):eq("/rom/x: Not a directory")
expect.error(fs.list, "x"):eq("/x: Not a directory")
end)
end)
describe("fs.list", function()
it("fails on files", function()
expect.error(fs.list, "rom/startup.lua"):eq("/rom/startup.lua: Not a directory")
expect.error(fs.list, "startup.lua"):eq("/startup.lua: Not a directory")
end)
it("fails on non-existent nodes", function()
expect.error(fs.list, "rom/x"):eq("/rom/x: Not a directory")
expect.error(fs.list, "x"):eq("/x: Not a directory")
end)
end)
describe("fs.getSize", function()
it("fails on non-existent nodes", function()
expect.error(fs.getSize, "rom/x"):eq("/rom/x: No such file")
expect.error(fs.getSize, "x"):eq("/x: No such file")
end)
end)
describe("fs.open", function()
describe("reading", function()
it("fails on directories", function()
expect { fs.open("rom", "r") }:same { nil, "/rom: No such file" }
expect { fs.open("", "r") }:same { nil, "/: No such file" }
end)
it("fails on non-existent nodes", function()
expect { fs.open("rom/x", "r") }:same { nil, "/rom/x: No such file" }
expect { fs.open("x", "r") }:same { nil, "/x: No such file" }
end)
end)
describe("writing", function()
it("fails on directories", function()
expect { fs.open("", "w") }:same { nil, "/: Cannot write to directory" }
end)
it("fails on read-only mounts", function()
expect { fs.open("rom/x", "w") }:same { nil, "/rom/x: Access denied" }
end)
end)
describe("appending", function()
it("fails on directories", function()
expect { fs.open("", "a") }:same { nil, "/: Cannot write to directory" }
end)
it("fails on read-only mounts", function()
expect { fs.open("rom/x", "a") }:same { nil, "/rom/x: Access denied" }
end)
end)
end)
describe("fs.makeDir", function()
it("fails on files", function()
expect.error(fs.makeDir, "startup.lua"):eq("/startup.lua: File exists")
end)
it("fails on read-only mounts", function()
expect.error(fs.makeDir, "rom/x"):eq("/rom/x: Access denied")
end)
end)
describe("fs.delete", function()
it("fails on read-only mounts", function()
expect.error(fs.delete, "rom/x"):eq("/rom/x: Access denied")
end)
end)
describe("fs.copy", function()
it("fails on read-only mounts", function()
expect.error(fs.copy, "rom", "rom/startup"):eq("/rom/startup: Access denied")
end)
end)
describe("fs.move", function()
it("fails on read-only mounts", function()
expect.error(fs.move, "rom", "rom/move"):eq("Access denied")
expect.error(fs.move, "test-files", "rom/move"):eq("Access denied")
expect.error(fs.move, "rom", "test-files"):eq("Access denied")
end)
end)
end) end)