Pārlūkot izejas kodu

+ implemented ChDir() for WASI

Nikolay Nikolov 3 gadi atpakaļ
vecāks
revīzija
0d6b5338d0
2 mainītis faili ar 156 papildinājumiem un 17 dzēšanām
  1. 138 1
      rtl/wasi/sysdir.inc
  2. 18 16
      rtl/wasi/system.pp

+ 138 - 1
rtl/wasi/sysdir.inc

@@ -48,9 +48,146 @@ begin
     InOutRes:=Errno2InoutRes(res);
 end;
 
+procedure do_ChDir_internal(s: rawbytestring; SymLinkFollowCount: longint);
+
+  function GetNextPart: ansistring;
+    var
+      slashpos,backslashpos: longint;
+    begin
+      slashpos:=Pos('/',s);
+      backslashpos:=Pos('\',s);
+      if (slashpos<>0) and ((slashpos<backslashpos) or (backslashpos=0)) then
+      begin
+        result:=Copy(s,1,slashpos-1);
+        delete(s,1,slashpos);
+      end
+      else if backslashpos<>0 then
+      begin
+        result:=Copy(s,1,backslashpos-1);
+        delete(s,1,backslashpos);
+      end
+      else
+      begin
+        result:=s;
+        s:='';
+      end;
+      while (s<>'') and (s[1] in ['/','\']) do
+        delete(s,1,1);
+    end;
+
+const
+  MaxSymLinkSize = 4096;
+var
+  new_drive_nr: longint;
+  new_dir,new_dir_save,next_dir_part: ansistring;
+  fd: __wasi_fd_t;
+  pr: ansistring;
+  st: __wasi_filestat_t;
+  res: __wasi_errno_t;
+  symlink: ansistring;
+  symlink_len: __wasi_size_t;
+begin
+  if SymLinkFollowCount<0 then
+  begin
+    InOutRes:=40;
+    exit;
+  end;
+  if HasDriveLetter(s) then
+  begin
+    new_drive_nr:=Ord(UpCase(s[1]))-(Ord('A')-1);
+    delete(s,1,2);
+  end
+  else
+    new_drive_nr:=current_drive;
+  if (new_drive_nr>drives_count) or (current_dirs[new_drive_nr].dir_name='') then
+  begin
+    InoutRes:=15;
+    exit;
+  end;
+  new_dir:=current_dirs[new_drive_nr].dir_name;
+  if s<>'' then
+  begin
+    if s[1] in ['/','\'] then
+    begin
+      delete(s,1,1);
+      if new_drive_nr>0 then
+        new_dir:=Copy(new_dir,1,2)+'/'
+      else
+        new_dir:='/';
+    end;
+    while s<>'' do
+    begin
+      next_dir_part:=GetNextPart;
+      if next_dir_part='.' then
+        {nothing to do}
+      else if next_dir_part='..' then
+      begin
+        if (new_dir<>'') and not (new_dir[Length(new_dir)] in ['/','\']) then
+        begin
+          while (new_dir<>'') and not (new_dir[Length(new_dir)] in ['/','\']) do
+            delete(new_dir,Length(new_dir),1);
+          while (new_dir<>'') and (new_dir[Length(new_dir)] in ['/','\']) do
+            delete(new_dir,Length(new_dir),1);
+          if (Pos('/',new_dir)=0) and (Pos('\',new_dir)=0) then
+            new_dir:=new_dir+'/';
+        end;
+      end
+      else
+      begin
+        new_dir_save:=new_dir;
+        if (new_dir<>'') and (new_dir[Length(new_dir)] in ['/','\']) then
+          new_dir:=new_dir+next_dir_part
+        else
+          new_dir:=new_dir+'/'+next_dir_part;
+        if not ConvertToFdRelativePath(new_dir,fd,pr) then
+        begin
+          {...}
+          InOutRes:=3;
+          exit;
+        end;
+        res:=__wasi_path_filestat_get(fd,0,PChar(pr),Length(pr),@st);
+        if res<>__WASI_ERRNO_SUCCESS then
+        begin
+          if res=__WASI_ERRNO_NOENT then
+            InOutRes:=3
+          else
+            InOutRes:=Errno2InoutRes(res);
+          exit;
+        end;
+        if st.filetype=__WASI_FILETYPE_SYMBOLIC_LINK then
+        begin
+          SetLength(symlink,MaxSymLinkSize);
+          res:=__wasi_path_readlink(fd,PChar(pr),Length(pr),@symlink[1],Length(symlink),@symlink_len);
+          if res<>__WASI_ERRNO_SUCCESS then
+          begin
+            InOutRes:=Errno2InoutRes(res);
+            exit;
+          end;
+          SetLength(symlink,symlink_len);
+          if (symlink<>'') and (symlink[1] in ['/', '\']) then
+            do_ChDir_internal(symlink,SymLinkFollowCount-1)
+          else if (new_dir_save<>'') and (new_dir_save[1] in ['/', '\']) then
+            do_ChDir_internal(new_dir_save+symlink,SymLinkFollowCount-1)
+          else
+            do_ChDir_internal(new_dir_save+'/'+symlink,SymLinkFollowCount-1);
+          exit;
+        end
+        else if st.filetype<>__WASI_FILETYPE_DIRECTORY then
+        begin
+          InOutRes:=5;
+          exit;
+        end;
+      end;
+    end;
+  end;
+  current_drive:=new_drive_nr;
+  current_dirs[new_drive_nr].dir_name:=new_dir;
+  InOutRes:=0;
+end;
+
 procedure do_ChDir(s: rawbytestring);
 begin
-  DebugWriteLn('do_ChDir');
+  do_ChDir_internal(s, 40);
 end;
 
 procedure do_getdir(drivenr : byte;var dir : rawbytestring);

+ 18 - 16
rtl/wasi/system.pp

@@ -84,6 +84,11 @@ implementation
 {$I wasitypes.inc}
 {$I wasiprocs.inc}
 
+function HasDriveLetter(const path: rawbytestring): Boolean;
+begin
+  HasDriveLetter:=(path<>'') and (UpCase(path[1]) in ['A'..'Z']) and (path[2] = ':');
+end;
+
 {$I system.inc}
 
 var
@@ -107,14 +112,9 @@ begin
   __wasi_proc_exit(ExitCode);
 End;
 
-function HasDriveLetter(const path: rawbytestring): Boolean;
-begin
-  HasDriveLetter:=(path<>'') and (UpCase(path[1]) in ['A'..'Z']) and (path[2] = ':');
-end;
-
 function ConvertToFdRelativePath(path: ansistring; out fd: LongInt; out relfd_path: ansistring): Boolean;
 var
-  drive_nr,I,pdir_drive,longest_match,pdir_length,chridx: longint;
+  drive_nr,I,pdir_drive,longest_match,chridx: longint;
   IsAbsolutePath: Boolean;
   pdir: ansistring;
 begin
@@ -129,7 +129,7 @@ begin
   else
     drive_nr:=current_drive;
   { path is relative to a current directory? }
-  if not (path[1] in ['/','\']) then
+  if (path='') or not (path[1] in ['/','\']) then
   begin
     { if so, convert to absolute }
     if (drive_nr>=drives_count) or (current_dirs[drive_nr].dir_name='') then
@@ -146,7 +146,6 @@ begin
       delete(path,1,2);
   end;
   { path is now absolute. Try to find it in the preopened dirs array }
-  InOutRes:=3;
   ConvertToFdRelativePath:=false;
   longest_match:=0;
   for I:=0 to preopened_dirs_count-1 do
@@ -161,25 +160,28 @@ begin
       pdir_drive:=0;
     if pdir_drive<>drive_nr then
       continue;
-    pdir_length:=Length(pdir);
-    if pdir_length>Length(path) then
+    if Length(pdir)>Length(path) then
       continue;
-    if Copy(path,1,pdir_length)<>Copy(pdir,1,pdir_length) then
+    if Copy(path,1,Length(pdir))<>pdir then
       continue;
-    chridx:=pdir_length+1;
-    if (chridx>Length(path)) or not (path[chridx] in ['/','\']) then
+    chridx:=Length(pdir)+1;
+    if ((pdir<>'/') and (pdir<>'\')) and
+       ((chridx>Length(path)) or not (path[chridx] in ['/','\'])) then
       continue;
-    if pdir_length>longest_match then
+    if Length(pdir)>longest_match then
     begin
-      longest_match:=pdir_length;
+      longest_match:=Length(pdir);
       while (chridx<=Length(path)) and (path[chridx] in ['/','\']) do
         Inc(chridx);
       fd:=preopened_dirs[I].fd;
       relfd_path:=Copy(path,chridx,Length(path)-chridx+1);
-      InOutRes:=0;
       ConvertToFdRelativePath:=true;
     end;
   end;
+  if longest_match>0 then
+    InOutRes:=0
+  else
+    InOutRes:=3;
 end;
 
 procedure Setup_PreopenedDirs;