mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-06-16 18:19:55 +00:00
367773e173
- Separate FileMount into separate FileMount and WritableFileMount classes. This separates the (relatively simple) read-only code from the (soon to be even more complex) read/write code. It also allows you to create read-only mounts which don't bother with filesystem accounting, which is nice. - Make openForWrite/openForAppend always return a SeekableFileHandle. Appendable files still cannot be seeked within, but that check is now done on the FS side. - Refactor the various mount tests to live in test contract interfaces, allowing us to reuse them between mounts. - Clean up our error handling a little better. (Most) file-specific code has been moved to FileMount, and ArchiveMount-derived classes now throw correct path-localised exceptions.
252 lines
8.4 KiB
Java
252 lines
8.4 KiB
Java
/*
|
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
|
* Copyright Daniel Ratcliffe, 2011-2022. 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.Mount;
|
|
import dan200.computercraft.api.filesystem.WritableMount;
|
|
|
|
import javax.annotation.Nullable;
|
|
import java.io.IOException;
|
|
import java.nio.channels.SeekableByteChannel;
|
|
import java.nio.file.attribute.BasicFileAttributes;
|
|
import java.util.List;
|
|
import java.util.OptionalLong;
|
|
|
|
class MountWrapper {
|
|
private final String label;
|
|
private final String location;
|
|
|
|
private final Mount mount;
|
|
private final @Nullable WritableMount writableMount;
|
|
|
|
MountWrapper(String label, String location, Mount mount) {
|
|
this.label = label;
|
|
this.location = location;
|
|
this.mount = mount;
|
|
writableMount = null;
|
|
}
|
|
|
|
MountWrapper(String label, String location, WritableMount mount) {
|
|
this.label = label;
|
|
this.location = location;
|
|
this.mount = 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() : OptionalLong.of(writableMount.getCapacity());
|
|
}
|
|
|
|
public boolean isReadOnly(String path) throws FileSystemException {
|
|
try {
|
|
return writableMount == null || writableMount.isReadOnly(path);
|
|
} catch (IOException e) {
|
|
throw localExceptionOf(path, e);
|
|
}
|
|
}
|
|
|
|
public boolean exists(String path) throws FileSystemException {
|
|
path = toLocal(path);
|
|
try {
|
|
return mount.exists(path);
|
|
} catch (IOException e) {
|
|
throw localExceptionOf(path, e);
|
|
}
|
|
}
|
|
|
|
public boolean isDirectory(String path) throws FileSystemException {
|
|
path = toLocal(path);
|
|
try {
|
|
return mount.exists(path) && mount.isDirectory(path);
|
|
} catch (IOException e) {
|
|
throw localExceptionOf(path, 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(path, 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(path, e);
|
|
}
|
|
}
|
|
|
|
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(path, e);
|
|
}
|
|
}
|
|
|
|
public SeekableByteChannel openForRead(String path) throws FileSystemException {
|
|
path = toLocal(path);
|
|
try {
|
|
if (mount.exists(path) && !mount.isDirectory(path)) {
|
|
return mount.openForRead(path);
|
|
} else {
|
|
throw localExceptionOf(path, "No such file");
|
|
}
|
|
} catch (IOException e) {
|
|
throw localExceptionOf(path, 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(path, e);
|
|
}
|
|
}
|
|
|
|
public void delete(String path) throws FileSystemException {
|
|
if (writableMount == null) throw exceptionOf(path, "Access denied");
|
|
|
|
path = toLocal(path);
|
|
try {
|
|
if (mount.exists(path)) {
|
|
writableMount.delete(path);
|
|
}
|
|
} catch (IOException e) {
|
|
throw localExceptionOf(path, e);
|
|
}
|
|
}
|
|
|
|
public void rename(String source, String dest) throws FileSystemException {
|
|
if (writableMount == null) throw exceptionOf(source, "Access denied");
|
|
|
|
source = toLocal(source);
|
|
dest = toLocal(dest);
|
|
try {
|
|
if (!dest.isEmpty()) {
|
|
var destParent = FileSystem.getDirectory(dest);
|
|
if (!destParent.isEmpty() && !mount.exists(destParent)) writableMount.makeDirectory(destParent);
|
|
}
|
|
|
|
writableMount.rename(source, dest);
|
|
} catch (IOException e) {
|
|
throw localExceptionOf(source, e);
|
|
}
|
|
}
|
|
|
|
public SeekableByteChannel 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()) {
|
|
var dir = FileSystem.getDirectory(path);
|
|
if (!dir.isEmpty() && !mount.exists(path)) {
|
|
writableMount.makeDirectory(dir);
|
|
}
|
|
}
|
|
return writableMount.openForWrite(path);
|
|
}
|
|
} catch (IOException e) {
|
|
throw localExceptionOf(path, e);
|
|
}
|
|
}
|
|
|
|
public SeekableByteChannel openForAppend(String path) throws FileSystemException {
|
|
if (writableMount == null) throw exceptionOf(path, "Access denied");
|
|
|
|
path = toLocal(path);
|
|
try {
|
|
if (!mount.exists(path)) {
|
|
if (!path.isEmpty()) {
|
|
var dir = FileSystem.getDirectory(path);
|
|
if (!dir.isEmpty() && !mount.exists(path)) {
|
|
writableMount.makeDirectory(dir);
|
|
}
|
|
}
|
|
return writableMount.openForWrite(path);
|
|
} else if (mount.isDirectory(path)) {
|
|
throw localExceptionOf(path, "Cannot write to directory");
|
|
} else {
|
|
return writableMount.openForAppend(path);
|
|
}
|
|
} catch (IOException e) {
|
|
throw localExceptionOf(path, e);
|
|
}
|
|
}
|
|
|
|
private String toLocal(String path) {
|
|
return FileSystem.toLocal(path, location);
|
|
}
|
|
|
|
private FileSystemException localExceptionOf(@Nullable String localPath, IOException e) {
|
|
if (!location.isEmpty() && e instanceof FileOperationException ex) {
|
|
if (ex.getFilename() != null) return localExceptionOf(ex.getFilename(), FileSystemException.getMessage(ex));
|
|
}
|
|
|
|
if (e instanceof java.nio.file.FileSystemException ex) {
|
|
// This error will contain the absolute path, leaking information about where MC is installed. We drop that,
|
|
// just taking the reason. We assume that the error refers to the input path.
|
|
var message = ex.getReason();
|
|
if (message == null) message = "Failed";
|
|
return localPath == null ? new FileSystemException(message) : localExceptionOf(localPath, message);
|
|
}
|
|
|
|
return FileSystemException.of(e);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|