Wasi: fix bugs in IO implementation

This commit is contained in:
Alexey Andreev 2022-11-12 21:52:29 +01:00
parent d9fb2bc159
commit 70e37dfed9
10 changed files with 68 additions and 40 deletions

View File

@ -396,7 +396,7 @@ public class TFile implements Serializable, Comparable<TFile> {
public boolean createNewFile() throws IOException {
VirtualFile parentVirtualFile = findParentFile();
if (parentVirtualFile == null) {
if (parentVirtualFile == null || !parentVirtualFile.isDirectory()) {
throw new IOException("Can't create file " + getPath() + " since parent directory does not exist");
}
@ -405,7 +405,7 @@ public class TFile implements Serializable, Comparable<TFile> {
public boolean mkdir() {
VirtualFile virtualFile = findParentFile();
return virtualFile != null && virtualFile.createDirectory(getName());
return virtualFile != null && virtualFile.isDirectory() && virtualFile.createDirectory(getName());
}
public boolean mkdirs() {
@ -416,7 +416,7 @@ public class TFile implements Serializable, Comparable<TFile> {
}
int i = path.length();
do {
while (true) {
VirtualFile file = fs().getFile(path.substring(0, i));
if (file.isDirectory()) {
break;
@ -424,13 +424,18 @@ public class TFile implements Serializable, Comparable<TFile> {
return false;
}
i = path.lastIndexOf(separatorChar, i - 1);
} while (i >= 0);
i++;
int next = path.lastIndexOf(separatorChar, i - 1);
if (next < 0) {
break;
}
i = next;
if (i == 0) {
break;
}
}
while (i < path.length()) {
int next = path.indexOf(separatorChar, i);
int next = path.indexOf(separatorChar, i + 1);
if (next < 0) {
next = path.length();
}
@ -438,11 +443,12 @@ public class TFile implements Serializable, Comparable<TFile> {
break;
}
VirtualFile file = fs().getFile(path.substring(0, i));
if (!file.createDirectory(path.substring(i, next))) {
String dirPath = i == 0 && path.startsWith("/") ? "/" : path.substring(0, i);
VirtualFile file = fs().getFile(dirPath);
if (!file.createDirectory(path.substring(i + 1, next))) {
return false;
}
i = next + 1;
i = next;
}
return true;
@ -515,7 +521,9 @@ public class TFile implements Serializable, Comparable<TFile> {
if (directory == null) {
String tmpDir = System.getProperty("java.io.tmpdir", ".");
tmpDirFile = new TFile(tmpDir);
tmpDirFile.mkdirs();
if (!tmpDirFile.mkdirs()) {
throw new IOException("Could not access temp dir");
}
} else {
tmpDirFile = directory;
}

View File

@ -28,7 +28,7 @@ public class TFileInputStream extends InputStream {
public TFileInputStream(TFile file) throws FileNotFoundException {
VirtualFile virtualFile = file.findVirtualFile();
if (virtualFile == null || virtualFile.isDirectory()) {
if (virtualFile == null || !virtualFile.isFile()) {
throw new FileNotFoundException();
}

View File

@ -44,7 +44,7 @@ public class TFileOutputStream extends OutputStream {
throw new FileNotFoundException("Invalid file name");
}
VirtualFile parentVirtualFile = file.findParentFile();
if (parentVirtualFile != null) {
if (parentVirtualFile != null && parentVirtualFile.isDirectory()) {
try {
parentVirtualFile.createFile(file.getName());
} catch (IOException e) {
@ -53,6 +53,9 @@ public class TFileOutputStream extends OutputStream {
}
VirtualFile virtualFile = file.findVirtualFile();
if (virtualFile == null || !virtualFile.isFile()) {
throw new FileNotFoundException("Could not create file");
}
accessor = virtualFile.createAccessor(false, true, append);
if (accessor == null) {
throw new FileNotFoundException();

View File

@ -55,7 +55,7 @@ public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
}
VirtualFile virtualFile = file.findVirtualFile();
if (virtualFile == null || virtualFile.isDirectory()) {
if (virtualFile == null || !virtualFile.isFile()) {
throw new FileNotFoundException();
}

View File

@ -1527,6 +1527,7 @@ public class TArrays extends TObject {
@SafeVarargs
public static <T> TList<T> asList(final T... a) {
Objects.requireNonNull(a);
return new ArrayAsList<>(a);
}

View File

@ -47,7 +47,7 @@ public class WasiFileSystem implements VirtualFileSystem {
}
void findBestPreopened(String path) {
if (path.endsWith("/")) {
if (path.endsWith("/") && path.length() > 1) {
path = path.substring(0, path.length() - 1);
}
@ -81,12 +81,11 @@ public class WasiFileSystem implements VirtualFileSystem {
}
private static int getPrefixLength(String name, String dir) {
if (dir.equals("/")) {
return 0;
} else {
return name.startsWith(dir) && name.length() > dir.length() && name.charAt(dir.length()) == '/'
? dir.length() + 1 : -1;
if (dir.equals("/") && name.startsWith("/")) {
return 1;
}
return name.startsWith(dir) && name.length() > dir.length() && name.charAt(dir.length()) == '/'
? dir.length() + 1 : -1;
}
@Override

View File

@ -27,6 +27,7 @@ import static org.teavm.backend.wasm.wasi.Wasi.OFLAGS_DIRECTORY;
import static org.teavm.backend.wasm.wasi.Wasi.OFLAGS_EXCLUSIVE;
import static org.teavm.backend.wasm.wasi.Wasi.RIGHTS_FD_FILESTAT_GET;
import static org.teavm.backend.wasm.wasi.Wasi.RIGHTS_FD_FILESTAT_SET_SIZE;
import static org.teavm.backend.wasm.wasi.Wasi.RIGHTS_FD_READDIR;
import static org.teavm.backend.wasm.wasi.Wasi.RIGHTS_READ;
import static org.teavm.backend.wasm.wasi.Wasi.RIGHTS_SEEK;
import static org.teavm.backend.wasm.wasi.Wasi.RIGHTS_SYNC;
@ -39,7 +40,7 @@ import org.teavm.backend.wasm.runtime.WasiBuffer;
import org.teavm.backend.wasm.wasi.Dirent;
import org.teavm.backend.wasm.wasi.FdResult;
import org.teavm.backend.wasm.wasi.Filestat;
import org.teavm.backend.wasm.wasi.SizeResult;
import org.teavm.backend.wasm.wasi.IntResult;
import org.teavm.backend.wasm.wasi.Wasi;
import org.teavm.interop.Address;
import org.teavm.interop.Structure;
@ -109,12 +110,12 @@ public class WasiVirtualFile implements VirtualFile {
}
private String[] listFiles(int fd) {
SizeResult sizePtr = WasiBuffer.getBuffer().toStructure();
IntResult sizePtr = WasiBuffer.getBuffer().toStructure();
ArrayList<String> list = new ArrayList<>();
final int direntSize = Structure.sizeOf(Dirent.class);
byte[] direntArray = fs.buffer;
Address direntBuffer = Address.align(Address.ofData(direntArray), 8);
int direntArrayOffset = (int) direntBuffer.diff(Address.ofData(direntArray).add(8));
int direntArrayOffset = (int) direntBuffer.diff(Address.ofData(direntArray));
int bufferSize = direntArray.length - direntArrayOffset;
long cookie = 0;
@ -124,27 +125,34 @@ public class WasiVirtualFile implements VirtualFile {
if (errno != ERRNO_SUCCESS) {
return null;
}
int size = sizePtr.value;
int remainingSize = bufferSize;
int entryOffset = 0;
while (true) {
if (remainingSize < direntSize) {
break;
}
Address direntPtr = direntBuffer.add(direntArrayOffset);
Address direntPtr = direntBuffer.add(entryOffset);
Dirent dirent = direntPtr.toStructure();
int entryLength = direntSize + (int) dirent.nameLength;
int entryLength = direntSize + dirent.nameLength;
if (entryLength > bufferSize) {
direntArray = new byte[entryLength * 3 / 2 + 8];
direntBuffer = Address.align(Address.ofData(direntArray), 8);
direntArrayOffset = (int) direntBuffer.diff(Address.ofData(direntArray));
bufferSize = direntArray.length - direntArrayOffset;
break;
} else if (entryOffset + entryLength > bufferSize) {
break;
}
cookie = dirent.next;
list.add(new String(direntArray, direntArrayOffset + direntSize, (int) dirent.nameLength,
StandardCharsets.UTF_8));
String name = new String(direntArray, direntArrayOffset + entryOffset + direntSize, dirent.nameLength,
StandardCharsets.UTF_8);
if (!name.equals(".") && !name.equals("..")) {
list.add(name);
}
remainingSize -= entryLength;
direntArrayOffset += entryLength;
if (direntArrayOffset == sizePtr.value) {
entryOffset += entryLength;
if (entryOffset == size) {
break outer;
}
}
@ -159,7 +167,7 @@ public class WasiVirtualFile implements VirtualFile {
if (baseFd < 0) {
return null;
}
int fd = path != null ? open(OFLAGS_DIRECTORY, RIGHTS_READ, (short) 0) : baseFd;
int fd = path != null ? open(OFLAGS_DIRECTORY, RIGHTS_READ | RIGHTS_FD_READDIR, (short) 0) : baseFd;
if (fd < 0) {
return null;
}
@ -214,7 +222,7 @@ public class WasiVirtualFile implements VirtualFile {
public boolean createFile(String fileName) throws IOException {
init();
if (baseFd < 0) {
return false;
throw new IOException("Can't create file: access to directory not granted by WASI runtime");
}
int fd = open(baseFd, constructPath(path, fileName), (short) (OFLAGS_CREATE | OFLAGS_EXCLUSIVE), 0, (short) 0);
if (fs.errno == ERRNO_EXIST) {
@ -241,12 +249,17 @@ public class WasiVirtualFile implements VirtualFile {
@Override
public boolean delete() {
init();
if (fullPath == null || baseFd < 0) {
if (path == null || baseFd < 0) {
return false;
}
byte[] bytes = path.getBytes(StandardCharsets.UTF_8);
short errno = Wasi.pathUnlinkFile(baseFd, Address.ofData(bytes), bytes.length);
return errno == ERRNO_SUCCESS;
if (isFile()) {
byte[] bytes = path.getBytes(StandardCharsets.UTF_8);
return Wasi.pathUnlinkFile(baseFd, Address.ofData(bytes), bytes.length) == ERRNO_SUCCESS;
} else if (isDirectory()) {
byte[] bytes = path.getBytes(StandardCharsets.UTF_8);
return Wasi.pathRemoveDirectory(baseFd, Address.ofData(bytes), bytes.length) == ERRNO_SUCCESS;
}
return false;
}
private static boolean adopt(int fd, WasiVirtualFile file, String fileName) {

View File

@ -20,6 +20,6 @@ import org.teavm.interop.Structure;
public class Dirent extends Structure {
public long next;
public long inode;
public long nameLength;
public long fileType;
public int nameLength;
public int fileType;
}

View File

@ -44,6 +44,7 @@ public final class Wasi {
public static final long RIGHTS_SYNC = 1L << 4;
public static final long RIGHTS_CREATE_DIRECTORY = 1L << 9;
public static final long RIGHTS_CREATE_FILE = 1L << 10;
public static final long RIGHTS_FD_READDIR = 1L << 14;
public static final long RIGHTS_FD_FILESTAT_GET = 1L << 21;
public static final long RIGHTS_FD_FILESTAT_SET_SIZE = 1L << 22;
@ -93,7 +94,7 @@ public final class Wasi {
Filestat filestat);
@Import(name = "fd_readdir", module = "wasi_snapshot_preview1")
public static native short fdReaddir(int fd, Address dirent, int direntSize, long cookie, SizeResult size);
public static native short fdReaddir(int fd, Address dirent, int direntSize, long cookie, IntResult size);
@Import(name = "path_open", module = "wasi_snapshot_preview1")
public static native short pathOpen(int dirFd, int lookupFlags, Address path, int pathLength, short oflags,
@ -108,6 +109,9 @@ public final class Wasi {
@Import(name = "path_unlink_file", module = "wasi_snapshot_preview1")
public static native short pathUnlinkFile(int fd, Address path, int pathLength);
@Import(name = "path_remove_directory", module = "wasi_snapshot_preview1")
public static native short pathRemoveDirectory(int fd, Address path, int pathLength);
@Import(name = "path_rename", module = "wasi_snapshot_preview1")
public static native short pathRename(int oldFd, Address oldPath, int oldPathLength, int newFd, Address newPath,
int newPathLength);

View File

@ -1 +1 @@
~/.wasmtime/bin/wasmtime run --dir target/wasi-testdir --mapdir /::target/wasi-testdir $1 $2
~/.wasmtime/bin/wasmtime run --mapdir /::target/wasi-testdir $1 $2