* Add FindFirst/FindNext support

This commit is contained in:
Michaël Van Canneyt 2024-05-19 15:37:22 +02:00
parent 72b7d29497
commit 5d3abdcc5d
7 changed files with 218 additions and 43 deletions

View File

@ -43,14 +43,6 @@
<Item0 Name="PasJSIsProjectHTMLFile" Value="1"/>
</CustomData>
</Unit>
<Unit>
<Filename Value="wasitypes.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="wasizenfs.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
</Units>
</ProjectOptions>
<CompilerOptions>

View File

@ -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');

View File

@ -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);

View File

@ -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)<BufLen) and (Res=WASI_ESUCCESS) do
begin
NameArray:=UTF8TextEncoder.encode(Dirent.name);
NameLen:=NameArray.byteLength;
Ptr:=SetMemInfoUInt64(Ptr,Dirent.Next);
Ptr:=SetMemInfoUInt64(Ptr,Dirent.ino);
Ptr:=SetMemInfoInt32(Ptr,NameLen);
Ptr:=SetMemInfoInt32(Ptr,DirentMap[Dirent.EntryType]);
if SetUTF8StringInMem(Ptr,BufLen-18,Dirent.Name)<>-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;

View File

@ -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.

View File

@ -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

View File

@ -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;