Prevent copying folders inside themselves

- contains now performs a case-insensitive comparison. While this is a
   little dubious, it's required for systems like Windows, where foo and
   Foo are the same folder.
 - Impose a depth limit on copyRecursive. If there are any other cases
   where we may try to copy a folder into itself, this should prevent
   the computer entirely crashing.
This commit is contained in:
SquidDev 2020-02-07 14:17:09 +00:00
parent 8eae02c037
commit be89fc25f9
4 changed files with 33 additions and 11 deletions

View File

@ -27,7 +27,7 @@ public FileOperationException( @Nullable String filename, @Nonnull String messag
this.filename = filename;
}
public FileOperationException( String message )
public FileOperationException( @Nonnull String message )
{
super( Objects.requireNonNull( message, "message cannot be null" ) );
this.filename = null;

View File

@ -29,6 +29,14 @@
public class FileSystem
{
/**
* Maximum depth that {@link #copyRecursive(String, MountWrapper, String, MountWrapper, int)} will descend into.
*
* This is a pretty arbitrary value, though hopefully it is large enough that it'll never be normally hit. This
* exists to prevent it overflowing if it ever gets into an infinite loop.
*/
private static final int MAX_COPY_DEPTH = 128;
private static class MountWrapper
{
private String m_label;
@ -611,15 +619,13 @@ public synchronized void copy( String sourcePath, String destPath ) throws FileS
{
throw new FileSystemException( "/" + sourcePath + ": Can't copy a directory inside itself" );
}
copyRecursive( sourcePath, getMount( sourcePath ), destPath, getMount( destPath ) );
copyRecursive( sourcePath, getMount( sourcePath ), destPath, getMount( destPath ), 0 );
}
private synchronized void copyRecursive( String sourcePath, MountWrapper sourceMount, String destinationPath, MountWrapper destinationMount ) throws FileSystemException
private synchronized void copyRecursive( String sourcePath, MountWrapper sourceMount, String destinationPath, MountWrapper destinationMount, int depth ) throws FileSystemException
{
if( !sourceMount.exists( sourcePath ) )
{
return;
}
if( !sourceMount.exists( sourcePath ) ) return;
if( depth >= MAX_COPY_DEPTH ) throw new FileSystemException( "Too many directories to copy" );
if( sourceMount.isDirectory( sourcePath ) )
{
@ -634,7 +640,8 @@ private synchronized void copyRecursive( String sourcePath, MountWrapper sourceM
{
copyRecursive(
combine( sourcePath, child ), sourceMount,
combine( destinationPath, child ), destinationMount
combine( destinationPath, child ), destinationMount,
depth + 1
);
}
}
@ -854,8 +861,8 @@ else if( part.length() >= 255 )
public static boolean contains( String pathA, String pathB )
{
pathA = sanitizePath( pathA );
pathB = sanitizePath( pathB );
pathA = sanitizePath( pathA ).toLowerCase( Locale.ROOT );
pathB = sanitizePath( pathB ).toLowerCase( Locale.ROOT );
if( pathB.equals( ".." ) )
{

View File

@ -116,7 +116,7 @@ public static IMount createMount( Class<?> klass, String path, String fallback )
while( baseFile != null && !wholeFile.exists() )
{
baseFile = baseFile.getParentFile();
wholeFile = new File( baseFile, "resources/" + fallback + "/" + path );
wholeFile = new File( baseFile, "src/" + fallback + "/resources/" + path );
}
if( !wholeFile.exists() ) throw new IllegalStateException( "Cannot find ROM mount at " + file );

View File

@ -125,6 +125,21 @@ describe("The fs library", function()
it("fails on read-only mounts", function()
expect.error(fs.copy, "rom", "rom/startup"):eq("/rom/startup: Access denied")
end)
it("fails to copy a folder inside itself", function()
fs.makeDir("some-folder")
expect.error(fs.copy, "some-folder", "some-folder/x"):eq("/some-folder: Can't copy a directory inside itself")
expect.error(fs.copy, "some-folder", "Some-Folder/x"):eq("/some-folder: Can't copy a directory inside itself")
end)
it("copies folders", function()
fs.delete("some-folder")
fs.delete("another-folder")
fs.makeDir("some-folder")
fs.copy("some-folder", "another-folder")
expect(fs.isDir("another-folder")):eq(true)
end)
end)
describe("fs.move", function()