2020-02-08 21:04:58 +00:00
|
|
|
/*
|
|
|
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
2022-01-01 00:07:26 +00:00
|
|
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
2020-02-08 21:04:58 +00:00
|
|
|
* Send enquiries to dratcliffe@gmail.com
|
|
|
|
*/
|
|
|
|
package dan200.computercraft.core.filesystem;
|
|
|
|
|
|
|
|
import dan200.computercraft.api.filesystem.FileOperationException;
|
2022-12-03 15:02:00 +00:00
|
|
|
import dan200.computercraft.api.filesystem.Mount;
|
|
|
|
import dan200.computercraft.api.filesystem.WritableMount;
|
2020-02-08 21:04:58 +00:00
|
|
|
|
2020-07-18 09:06:23 +00:00
|
|
|
import javax.annotation.Nullable;
|
2020-02-08 21:04:58 +00:00
|
|
|
import java.io.IOException;
|
2022-12-03 18:20:50 +00:00
|
|
|
import java.nio.channels.SeekableByteChannel;
|
2020-02-08 21:04:58 +00:00
|
|
|
import java.nio.file.attribute.BasicFileAttributes;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.OptionalLong;
|
|
|
|
|
|
|
|
class MountWrapper {
|
2020-04-22 10:04:29 +00:00
|
|
|
private final String label;
|
|
|
|
private final String location;
|
2020-02-08 21:04:58 +00:00
|
|
|
|
2022-12-03 15:02:00 +00:00
|
|
|
private final Mount mount;
|
|
|
|
private final @Nullable WritableMount writableMount;
|
2020-02-08 21:04:58 +00:00
|
|
|
|
2022-12-03 15:02:00 +00:00
|
|
|
MountWrapper(String label, String location, Mount mount) {
|
2020-02-08 21:04:58 +00:00
|
|
|
this.label = label;
|
|
|
|
this.location = location;
|
|
|
|
this.mount = mount;
|
|
|
|
writableMount = null;
|
|
|
|
}
|
|
|
|
|
2022-12-03 15:02:00 +00:00
|
|
|
MountWrapper(String label, String location, WritableMount mount) {
|
2020-04-22 10:04:29 +00:00
|
|
|
this.label = label;
|
|
|
|
this.location = location;
|
|
|
|
this.mount = mount;
|
2020-02-08 21:04:58 +00:00
|
|
|
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() {
|
2022-12-04 21:59:30 +00:00
|
|
|
return writableMount == null ? OptionalLong.empty() : OptionalLong.of(writableMount.getCapacity());
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
|
2022-11-25 20:40:40 +00:00
|
|
|
public boolean isReadOnly(String path) throws FileSystemException {
|
|
|
|
try {
|
|
|
|
return writableMount == null || writableMount.isReadOnly(path);
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw localExceptionOf(path, e);
|
|
|
|
}
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean exists(String path) throws FileSystemException {
|
|
|
|
path = toLocal(path);
|
|
|
|
try {
|
|
|
|
return mount.exists(path);
|
|
|
|
} catch (IOException e) {
|
2020-07-18 09:06:23 +00:00
|
|
|
throw localExceptionOf(path, e);
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isDirectory(String path) throws FileSystemException {
|
|
|
|
path = toLocal(path);
|
|
|
|
try {
|
|
|
|
return mount.exists(path) && mount.isDirectory(path);
|
|
|
|
} catch (IOException e) {
|
2020-07-18 09:06:23 +00:00
|
|
|
throw localExceptionOf(path, e);
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2020-07-18 09:06:23 +00:00
|
|
|
throw localExceptionOf(path, e);
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2020-07-18 09:06:23 +00:00
|
|
|
throw localExceptionOf(path, e);
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2020-07-18 09:06:23 +00:00
|
|
|
throw localExceptionOf(path, e);
|
2022-11-03 23:43:14 +00:00
|
|
|
}
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
2022-11-03 23:43:14 +00:00
|
|
|
|
2022-12-03 18:20:50 +00:00
|
|
|
public SeekableByteChannel openForRead(String path) throws FileSystemException {
|
2020-02-08 21:04:58 +00:00
|
|
|
path = toLocal(path);
|
|
|
|
try {
|
|
|
|
if (mount.exists(path) && !mount.isDirectory(path)) {
|
2020-04-22 10:04:29 +00:00
|
|
|
return mount.openForRead(path);
|
2022-11-03 23:43:14 +00:00
|
|
|
} else {
|
2020-04-22 10:04:29 +00:00
|
|
|
throw localExceptionOf(path, "No such file");
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
2020-07-18 09:06:23 +00:00
|
|
|
throw localExceptionOf(path, e);
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2020-07-18 09:06:23 +00:00
|
|
|
throw localExceptionOf(path, e);
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void delete(String path) throws FileSystemException {
|
|
|
|
if (writableMount == null) throw exceptionOf(path, "Access denied");
|
|
|
|
|
2020-07-18 09:06:23 +00:00
|
|
|
path = toLocal(path);
|
2020-02-08 21:04:58 +00:00
|
|
|
try {
|
|
|
|
if (mount.exists(path)) {
|
|
|
|
writableMount.delete(path);
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
2020-07-18 09:06:23 +00:00
|
|
|
throw localExceptionOf(path, e);
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-04 21:59:30 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-09 22:01:01 +00:00
|
|
|
public SeekableByteChannel openForWrite(String path) throws FileSystemException {
|
2020-02-08 21:04:58 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2020-04-22 10:04:29 +00:00
|
|
|
return writableMount.openForWrite(path);
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
2020-07-18 09:06:23 +00:00
|
|
|
throw localExceptionOf(path, e);
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-09 22:01:01 +00:00
|
|
|
public SeekableByteChannel openForAppend(String path) throws FileSystemException {
|
2020-02-08 21:04:58 +00:00
|
|
|
if (writableMount == null) throw exceptionOf(path, "Access denied");
|
2022-11-03 23:43:14 +00:00
|
|
|
|
2020-02-08 21:04:58 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2020-04-22 10:04:29 +00:00
|
|
|
return writableMount.openForWrite(path);
|
2020-02-08 21:04:58 +00:00
|
|
|
} else if (mount.isDirectory(path)) {
|
|
|
|
throw localExceptionOf(path, "Cannot write to directory");
|
|
|
|
} else {
|
2020-04-22 10:04:29 +00:00
|
|
|
return writableMount.openForAppend(path);
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
2020-07-18 09:06:23 +00:00
|
|
|
throw localExceptionOf(path, e);
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private String toLocal(String path) {
|
|
|
|
return FileSystem.toLocal(path, location);
|
|
|
|
}
|
|
|
|
|
2022-11-06 11:55:26 +00:00
|
|
|
private FileSystemException localExceptionOf(@Nullable String localPath, IOException e) {
|
2021-08-08 11:44:56 +00:00
|
|
|
if (!location.isEmpty() && e instanceof FileOperationException ex) {
|
2022-11-06 11:55:26 +00:00
|
|
|
if (ex.getFilename() != null) return localExceptionOf(ex.getFilename(), FileSystemException.getMessage(ex));
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
|
2022-10-25 21:58:31 +00:00
|
|
|
if (e instanceof java.nio.file.FileSystemException ex) {
|
2020-07-18 09:06:23 +00:00
|
|
|
// 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.
|
2022-12-09 22:01:01 +00:00
|
|
|
var message = ex.getReason();
|
|
|
|
if (message == null) message = "Failed";
|
2020-07-18 09:06:23 +00:00
|
|
|
return localPath == null ? new FileSystemException(message) : localExceptionOf(localPath, message);
|
|
|
|
}
|
|
|
|
|
2022-11-06 11:55:26 +00:00
|
|
|
return FileSystemException.of(e);
|
2020-02-08 21:04:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|