diff --git a/demo/wasienv/filesystem/filesystemhost.lpi b/demo/wasienv/filesystem/filesystemhost.lpi
index 9365036..a049d98 100644
--- a/demo/wasienv/filesystem/filesystemhost.lpi
+++ b/demo/wasienv/filesystem/filesystemhost.lpi
@@ -43,14 +43,6 @@
-
-
-
-
-
-
-
-
diff --git a/demo/wasienv/filesystem/filesystemhost.lpr b/demo/wasienv/filesystem/filesystemhost.lpr
index afadc8a..f13a670 100644
--- a/demo/wasienv/filesystem/filesystemhost.lpr
+++ b/demo/wasienv/filesystem/filesystemhost.lpr
@@ -23,8 +23,8 @@ end;
procedure TMyApplication.RunWasm;
begin
- // Writeln('Enabling logging');
- // WasiEnvironment.LogAPI:=True;
+// Writeln('Enabling logging');
+// WasiEnvironment.LogAPI:=True;
await(tjsobject, ZenFS.configure(
new(
['mounts', new([
@@ -33,6 +33,10 @@ begin
])
)
);
+ if not ZenFS.existsSync('/tmp') then
+ begin
+ ZenFS.mkdirSync('/tmp',777);
+ end;
FS:=TWASIZenFS.Create;
WasiEnvironment.FS:=FS;
StartWebAssembly('fsdemo.wasm');
diff --git a/demo/wasienv/filesystem/fsdemo.lpr b/demo/wasienv/filesystem/fsdemo.lpr
index 6241ebd..af57315 100644
--- a/demo/wasienv/filesystem/fsdemo.lpr
+++ b/demo/wasienv/filesystem/fsdemo.lpr
@@ -10,6 +10,34 @@ Const
{$Endif}
OurFile = OurDir+'/test.txt';
+Procedure ShowDir(aDir : String);
+
+var
+ Info : TSearchRec;
+ aFileCount : Integer;
+ TotalSize : Int64;
+ S : String;
+begin
+ TotalSize:=0;
+ aFileCount:=0;
+ If FindFirst(aDir+'*',faAnyFile,Info)=0 then
+ try
+ Repeat
+ S:=Info.Name;
+ if (Info.Attr and faDirectory)<>0 then
+ S:=S+'/';
+ if (Info.Attr and faSymLink)<>0 then
+ S:=S+'@';
+ Writeln(FormatDateTime('yyyy-mm-dd"T"hh:nn:ss',Info.TimeStamp),' ',Info.Size:10,' '+S);
+ TotalSize:=TotalSize+Info.Size;
+ inc(aFileCount);
+ until FindNext(Info)<>0;
+ finally
+ FindClose(Info)
+ end;
+ Writeln('Total: ',TotalSize,' bytes in ',aFileCount,' files');
+end;
+
var
HasDir : Boolean;
HasFile: Boolean;
@@ -17,7 +45,6 @@ var
aSize,FD,byteCount : Integer;
begin
- S:='Hello, WebAssembly World!';
HasDir:=DirectoryExists(OurDir);
if HasDir then
Writeln('Directory already exists: ',OurDir)
@@ -25,6 +52,8 @@ begin
Writeln('Created new directory: ',OurDir)
else
Writeln('Failed to create directory: ',OurDir);
+ Writeln('Contents of root:');
+ ShowDir('/');
HasFile:=FileExists(OurFile);
If HasFile then
Writeln('File exists: ',OurFile)
@@ -37,12 +66,15 @@ begin
else
begin
Writeln('Got fileHandle: ',FD);
+ S:='Hello, WebAssembly World!';
ByteCount:=FileWrite(FD,S[1],Length(S));
Writeln('Wrote ',byteCount,' bytes to file. Expected: ',Length(S));
FileClose(FD);
Writeln('Closed file');
end;
end;
+ Writeln('Contents of ',OurDir,':');
+ ShowDir(ourdir+'/');
If FileExists(OurFile) then
begin
Writeln('Opening file: ',OurFile);
diff --git a/packages/wasi/src/wasienv.pas b/packages/wasi/src/wasienv.pas
index 54e1c0e..8dcea0c 100644
--- a/packages/wasi/src/wasienv.pas
+++ b/packages/wasi/src/wasienv.pas
@@ -143,7 +143,7 @@ type
function fd_prestat_get(fd: NativeInt; bufPtr: TWasmMemoryLocation) : NativeInt; virtual;
function fd_pwrite(fd, iovs, iovsLen, offset, nwritten : NativeInt) : NativeInt;virtual;
function fd_read(fd: NativeInt; iovs : TWasmMemoryLocation; iovsLen: NativeInt; nread : TWasmMemoryLocation) : NativeInt; virtual;
- function fd_readdir(fd, bufPtr, bufLen, cookie, bufusedPtr : NativeInt) : NativeInt; virtual;
+ function fd_readdir(fd : NativeInt; bufPtr: TWasmMemoryLocation; bufLen, cookie: NativeInt; bufusedPtr : TWasmMemoryLocation) : NativeInt; virtual;
function fd_renumber(afrom,ato : NativeInt) : NativeInt; virtual;
function fd_seek(fd, offset, whence : NativeInt; newOffsetPtr : TWasmMemoryLocation) : NativeInt; virtual;
function fd_sync(fd : NativeInt) : NativeInt; virtual;
@@ -1434,7 +1434,6 @@ begin
if LogAPI then
DoLog('TPas2JSWASIEnvironment.fd_seek(%d,%d,%d,[%x])',[fd,offset,whence,newOffsetPtr]);
{$ENDIF}
- console.log('Unimplemented: TPas2JSWASIEnvironment.fd_seek');
if not Assigned(FS) then
Result:=WASI_ENOSYS
else
@@ -1633,15 +1632,51 @@ begin
Result:=TJSUint8Array.New(0);
end;
-function TPas2JSWASIEnvironment.fd_readdir(fd, bufPtr, bufLen, cookie,
- bufusedPtr: NativeInt): NativeInt;
+function TPas2JSWASIEnvironment.fd_readdir(fd: NativeInt; bufPtr: TWasmMemoryLocation; bufLen, cookie: NativeInt;
+ bufusedPtr: TWasmMemoryLocation): NativeInt;
+
+var
+ Dirent : TWasiFSDirent;
+ NameArray : TJSUint8Array;
+ NameLen : integer;
+ Ptr : TWasmMemoryLocation;
+ Res,Used : Integer;
+
+
begin
{$IFNDEF NO_WASI_DEBUG}
if LogAPI then
DoLog('TPas2JSWASIEnvironment.fd_readdir(%d,[%x],%d,%d,[%x])',[fd,bufPtr,buflen,cookie,bufusedptr]);
{$ENDIF}
- console.log('Unimplemented: TPas2JSWASIEnvironment.fd_readdir');
- Result:= WASI_ENOSYS;
+ if not Assigned(FS) then
+ Result:=WASI_ENOSYS
+ else
+ try
+ Res:=FS.ReadDir(FD,AsIntNumber(Cookie),Dirent);
+ Result:=WASI_ESUCCESS;
+ Ptr:=BufPtr;
+ While ((Ptr-BufPtr)-1 then
+ begin
+ Ptr:=Ptr+NameLen;
+ Cookie:=Dirent.Next;
+ Res:=FS.ReadDir(FD,AsIntNumber(Cookie),Dirent)
+ end
+ else
+ Res:=WASI_ENOMEM;
+ end;
+ SetMemInfoInt32(bufusedPtr,Ptr-BufPtr);
+ except
+ On E : Exception do
+ Result:=ErrorToCode(E);
+ end;
end;
function TPas2JSWASIEnvironment.fd_renumber(afrom, ato: NativeInt): NativeInt;
@@ -1811,12 +1846,12 @@ begin
Loc:=BufPtr;
Loc:=SetMemInfoInt64(Loc,Info.dev);
Loc:=SetMemInfoUInt64(Loc,Info.Ino);
- Loc:=SetMemInfoInt8(Loc,Info.filetype);
+ Loc:=SetMemInfoUInt64(Loc,Info.filetype);
Loc:=SetMemInfoUInt64(Loc,Info.nLink);
Loc:=SetMemInfoUInt64(Loc,Info.size);
- Loc:=SetMemInfoUInt64(Loc,Info.atim);
- Loc:=SetMemInfoUInt64(Loc,Info.mtim);
- Loc:=SetMemInfoUInt64(Loc,Info.ctim);
+ Loc:=SetMemInfoUInt64(Loc,Info.atim*1000*1000);
+ Loc:=SetMemInfoUInt64(Loc,Info.mtim*1000*1000);
+ Loc:=SetMemInfoUInt64(Loc,Info.ctim*1000*1000);
end;
function TPas2JSWASIEnvironment.path_filestat_get(fd, flags: NativeInt;
@@ -2064,7 +2099,7 @@ Var
begin
view:=getModuleMemoryDataView();
view.setint16(aLoc,aValue, IsLittleEndian);
- Result:=aValue+SizeInt16;
+ Result:=aLoc+SizeInt16;
end;
function TPas2JSWASIEnvironment.SetMemInfoInt32(aLoc: TWasmMemoryLocation;
@@ -2076,7 +2111,7 @@ Var
begin
view:=getModuleMemoryDataView();
view.setInt32(aLoc,aValue,IsLittleEndian);
- Result:=aValue+SizeInt32;
+ Result:=aLoc+SizeInt32;
end;
function TPas2JSWASIEnvironment.SetMemInfoInt64(aLoc: TWasmMemoryLocation;
diff --git a/packages/wasi/src/wasitypes.pas b/packages/wasi/src/wasitypes.pas
index 52296aa..4868fec 100644
--- a/packages/wasi/src/wasitypes.pas
+++ b/packages/wasi/src/wasitypes.pas
@@ -34,7 +34,7 @@ Type
function fd_prestat_get(fd: NativeInt; bufPtr: TWasmMemoryLocation) : NativeInt;
function fd_pwrite(fd, iovs, iovsLen, offset, nwritten : NativeInt) : NativeInt;
function fd_read(fd: NativeInt; iovs : TWasmMemoryLocation; iovsLen: NativeInt; nread : TWasmMemoryLocation) : NativeInt;
- function fd_readdir(fd, bufPtr, bufLen, cookie, bufusedPtr : NativeInt) : NativeInt;
+ function fd_readdir(fd : NativeInt; bufPtr: TWasmMemoryLocation; bufLen, cookie: NativeInt; bufusedPtr : TWasmMemoryLocation) : NativeInt;
function fd_renumber(afrom,ato : NativeInt) : NativeInt;
function fd_seek(fd, offset, whence : NativeInt; newOffsetPtr : TWasmMemoryLocation) : NativeInt;
function fd_sync(fd : NativeInt) : NativeInt;
@@ -646,6 +646,13 @@ type
end;
TWasiPreStat = __wasi_prestat_t;
+ TDirentType = (dtUnknown,dtFile,dtDirectory,dtSymlink,dtSocket,dtBlockDevice,dtCharacterDevice,dtFIFO);
+ TWasiFSDirent = record
+ ino: NativeInt;
+ name : String;
+ EntryType: TDirentType;
+ next : NativeInt;
+ end;
{ EWasiFSError }
@@ -673,9 +680,21 @@ type
function DataSync(FD : Integer) : NativeInt;
function Seek(FD : integer; Offset : Integer; Whence : TSeekWhence; out NewPos : Integer) : NativeInt;
Function Read(FD : Integer; Data : TJSUint8Array; AtPos : Integer; Out BytesRead : Integer) : NativeInt;
+ function ReadDir(FD: Integer; Cookie: NativeInt; out DirEnt: TWasiFSDirent): NativeInt;
Function GetPrestat(FD: Integer) : String;
end;
+Const
+ DirentMap : Array [TDirentType] of Integer =
+ (__WASI_FILETYPE_UNKNOWN,
+ __WASI_FILETYPE_REGULAR_FILE,
+ __WASI_FILETYPE_DIRECTORY,
+ __WASI_FILETYPE_SYMBOLIC_LINK,
+ __WASI_FILETYPE_SOCKET_STREAM,
+ __WASI_FILETYPE_BLOCK_DEVICE,
+ __WASI_FILETYPE_CHARACTER_DEVICE,
+ __WASI_FILETYPE_UNKNOWN);
+
implementation
end.
diff --git a/packages/wasi/src/wasizenfs.pas b/packages/wasi/src/wasizenfs.pas
index d03a25a..d7ceaa8 100644
--- a/packages/wasi/src/wasizenfs.pas
+++ b/packages/wasi/src/wasizenfs.pas
@@ -5,21 +5,26 @@ unit wasizenfs;
interface
uses
- JS, libzenfs, Wasitypes;
+ SysUtils, JS, libzenfs, Wasitypes;
Type
+ EWASIZenFS = class(Exception);
{ TWASIZenFS }
-
TWASIZenFS = class (TObject,IWasiFS)
private
FRoot : TZenFSDir;
+ FDirMap : TJSMap;
protected
+ function AllocateDirFD(aDir: TZenFSDirentArray): Integer;
+ function IsDirEnt(FD : Integer) : TZenFSDirentArray;
+ Procedure RemoveDirent(FD : integer);
function PrependFD(FD: Integer; aPath: String): string;
class function ExceptToError(E : TJSObject) : Integer;
class function ZenFSDateToWasiTimeStamp(aDate: TJSDate): Nativeint;
class function ZenStatToWasiStat(ZSTat: TZenFSStats): TWasiFileStat;
Public
+ constructor create;
Function MkDirAt(FD : Integer; const aPath : String) : NativeInt;
Function RmDirAt(FD : Integer; const aPath : String) : NativeInt;
function StatAt(FD : Integer; const aPath : String; var stat: TWasiFileStat) : NativeInt;
@@ -38,6 +43,7 @@ Type
function DataSync(FD : Integer) : NativeInt;
function Seek(FD : integer; Offset : Integer; Whence : TSeekWhence; out NewPos : Integer) : NativeInt;
Function Read(FD : Integer; Data : TJSUint8Array; AtPos : Integer; Out BytesRead : Integer) : NativeInt;
+ function ReadDir(FD: Integer; Cookie: NativeInt; out DirEnt: TWasiFSDirent): NativeInt;
Function GetPrestat(FD: Integer) : String;
end;
@@ -48,6 +54,33 @@ const
{ TWASIZenFS }
+function TWASIZenFS.AllocateDirFD(aDir : TZenFSDirentArray): Integer;
+var
+ I : integer;
+
+begin
+ I:=4;
+ While (I<100) and (FDirMap.has(i)) do
+ Inc(I);
+ if I=100 then
+ Raise EWASIZenFS.Create('Too many directories');
+ FDirMap.&set(I,aDir);
+ Result:=I;
+end;
+
+function TWASIZenFS.IsDirEnt(FD: Integer): TZenFSDirentArray;
+begin
+ if FDirMap.has(FD) then
+ Result:=TZenFSDirentArray(FDirMap.get(FD))
+ else
+ Result:=Nil;
+end;
+
+procedure TWASIZenFS.RemoveDirent(FD: integer);
+begin
+ FDirMap.delete(FD);
+end;
+
function TWASIZenFS.PrependFD(FD: Integer; aPath: String) : string;
begin
@@ -116,6 +149,11 @@ begin
Result.ctim:=ZenFSDateToWasiTimeStamp(ZStat.cTime);
end;
+constructor TWASIZenFS.create;
+begin
+ FDirMap:=TJSMap.new;
+end;
+
function TWASIZenFS.StatAt(FD: Integer; const aPath: String;
var stat: TWasiFileStat): NativeInt;
@@ -231,9 +269,16 @@ end;
function TWASIZenFS.Close(FD : Integer): NativeInt;
+
begin
try
- ZenFS.closeSync(fd);
+ if IsDirEnt(FD)<>Nil then
+ begin
+ RemoveDirent(FD);
+ Result:=WASI_ESUCCESS;
+ end
+ else
+ ZenFS.closeSync(fd);
Result:=resOK;
except
on E : TJSObject do
@@ -344,6 +389,38 @@ begin
end;
end;
+function TWASIZenFS.ReadDir(FD: Integer; Cookie : NativeInt; out DirEnt: TWasiFSDirent): NativeInt;
+
+var
+ DirEnts : TZenFSDirentArray;
+ ZDirEntry : TZenFSDirent;
+
+begin
+ DirEnts:=IsDirEnt(FD);
+ if Not Assigned(DirEnts) then
+ Exit(WASI_EBADF);
+ if (Cookie<0) or (Cookie>=Length(Dirents)) then
+ Exit(WASI_ENOENT);
+ ZDirEntry:=Dirents[Cookie];
+ DirEnt.name:=ZDirEntry.path;
+ if ZDirEntry.isFile() then
+ Dirent.EntryType:=dtFile
+ else if ZDirEntry.isDirectory() then
+ Dirent.EntryType:=dtDirectory
+ else if ZDirEntry.isSymbolicLink() then
+ Dirent.EntryType:=dtSymlink
+ else if ZDirEntry.isFIFO() then
+ Dirent.EntryType:=dtFIFO
+ else if ZDirEntry.isBlockDevice() then
+ Dirent.EntryType:=dtBlockDevice
+ else if ZDirEntry.isCharacterDevice() then
+ Dirent.EntryType:=dtCharacterDevice
+ else if ZDirEntry.isSocket() then
+ Dirent.EntryType:=dtSocket;
+ Dirent.Next:=Cookie+1;
+ Result:=ResOK;
+end;
+
function TWASIZenFS.GetPrestat(FD: Integer): String;
begin
if (FD=3) then
@@ -376,6 +453,8 @@ var
lFlags : String;
Rights : NativeInt;
Reading,Writing : Boolean;
+ Dir : TZenFSDirentArray;
+ Opts : TZenFSReadDirOptions;
Function HasFlag(aFlag : Integer) : Boolean;
begin
@@ -397,26 +476,37 @@ var
begin
if (fdFlags<>0) and (fsFlags<>0) then ;
lPath:=PrependFD(FD,aPath);
- Rights:=AsIntNumber(fsRightsBase);
- Writing:=HasFlag(__WASI_OFLAGS_CREAT) or HasRight(__WASI_RIGHTS_FD_WRITE);
- Reading:=HasRight(__WASI_RIGHTS_FD_READ);
- if Writing then
+ if Not HasFlag(__WASI_OFLAGS_DIRECTORY) then
begin
- if HasFlag(__WASI_OFLAGS_TRUNC) then
- lFLags:='w'
+ Rights:=AsIntNumber(fsRightsBase);
+ Writing:=HasFlag(__WASI_OFLAGS_CREAT) or HasRight(__WASI_RIGHTS_FD_WRITE);
+ Reading:=HasRight(__WASI_RIGHTS_FD_READ);
+ if Writing then
+ begin
+ if HasFlag(__WASI_OFLAGS_TRUNC) then
+ lFLags:='w'
+ else
+ lFLags:='a';
+ if HasFlag(__WASI_OFLAGS_EXCL) then
+ lFLags:=lFLags+'x';
+ if Reading then
+ lFLags:=lFLags+'+';
+ end
else
- lFLags:='a';
- if HasFlag(__WASI_OFLAGS_EXCL) then
- lFLags:=lFLags+'x';
- if Reading then
- lFLags:=lFLags+'+';
- end
- else
- begin
- lFlags:='r';
+ begin
+ lFlags:='r';
+ end;
end;
try
- OpenFD:=ZenFS.openSync(lPath,lFlags);
+ if HasFlag(__WASI_OFLAGS_DIRECTORY) then
+ begin
+ Opts:=TZenFSReadDirOptions.New;
+ Opts.withFileTypes:=True;
+ Dir:=ZenFS.readdirSyncDirent(lpath,Opts);
+ OpenFD:=AllocateDirFD(Dir);
+ end
+ else
+ OpenFD:=ZenFS.openSync(lPath,lFlags);
Result:=resOK;
except
on E : TJSObject do
diff --git a/packages/zenfs/src/libzenfs.pas b/packages/zenfs/src/libzenfs.pas
index 1e973ec..4427291 100644
--- a/packages/zenfs/src/libzenfs.pas
+++ b/packages/zenfs/src/libzenfs.pas
@@ -162,6 +162,7 @@ Type
function isSocket() : Boolean;
property path : string read FPath;
end;
+ TZenFSDirentArray = Array of TZenFSDirent;
TJSDirentCallback = reference to procedure(aDirent : TZenFSDirent);
TJSDirCloseCallback = reference to procedure;
@@ -741,6 +742,8 @@ Type
Function readdirSync(path : String; options : TZenFSReadDirOptions): TStringDynArray; overload;
Function readdirSync(path : String): TStringDynArray; overload;
+ Function readdirSyncDirent(path : String; options : TZenFSReadDirOptions): TZenFSDirentArray; external name 'readdirSync';
+
Procedure readlink(path : String; options : String; callback : TStringCallBack); overload;
Procedure readlink(path : String; callback : TStringCallBack); overload;