vfs: symlink support in file picker, test cleanup, boot mount fix
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing

- File picker (app.filepicker.pas): add otSYMLINK to directory case in
  fp_collect_cb so symlinks like /sys appear as navigable directories
- VFS: add RemoveVirtualTree() to recursively free vdir/symlink trees
- VFS UnitTest: clean up /utest_vfs tree after all tests
- STORTEST: clean up /st_test tree after tests, fix listing leak in
  cmd_stortest (GetDirectoryListingFrom result was never freed)
- Boot mount: auto-mount boot drive at /boot via mountVolume() instead
  of symlink; remove /boot and /cfg from init() vdirs
- mount.asr: remove boot line (now only 'mnt {device}/sys /sys')
This commit is contained in:
2026-03-08 19:00:47 +00:00
parent 91bc0e5ea2
commit c8c7c2983d
4 changed files with 363 additions and 108 deletions
Generated
-1
View File
@@ -1,2 +1 @@
mnt {device}/boot /boot
mnt {device}/sys /sys
+1 -1
View File
@@ -389,7 +389,7 @@ begin
obj := PVFSObject(data);
if (ctx = nil) or (obj = nil) then exit;
case obj^.ObjectType of
otVDIRECTORY, otDRIVE, otDIRECTORY, otMOUNT:
otVDIRECTORY, otDRIVE, otDIRECTORY, otMOUNT, otSYMLINK:
if ctx^.dir_count < ENTRY_MAX then begin
ctx^.dir_names^[ctx^.dir_count] := key;
ctx^.dir_count := ctx^.dir_count + 1;
+51 -11
View File
@@ -129,9 +129,6 @@ begin
res := driver.storage.vfs.ResolvePathFrom('mnt', '/');
Assert(res = pvDirectory, 'ResolvePathFrom: /mnt from /');
res := driver.storage.vfs.ResolvePathFrom('cfg', '/');
Assert(res = pvDirectory, 'ResolvePathFrom: /cfg from /');
{ Non-existent names resolve to pvInvalid }
res := driver.storage.vfs.ResolvePathFrom('zzznope', '/');
Assert(res = pvInvalid, 'ResolvePathFrom: nonexistent from /');
@@ -155,7 +152,7 @@ begin
Assert(map <> nil, 'GetDirectoryListingFrom(/) not nil');
driver.storage.vfs.FreeDirectoryListing(map);
{ /disk, /dev, /mnt, /cfg created at init — must appear in root listing }
{ /disk, /dev, /mnt created at init — must appear in root listing }
map := driver.storage.vfs.GetDirectoryListingFrom('/', '/');
Assert(map <> nil, 'GetDirectoryListingFrom(/) not nil (2)');
{ All maps are now caller-owned snapshots — always safe to free }
@@ -166,10 +163,6 @@ begin
Assert(map <> nil, 'GetDirectoryListingFrom(/dev) not nil');
driver.storage.vfs.FreeDirectoryListing(map);
map := driver.storage.vfs.GetDirectoryListingFrom('/cfg', '/');
Assert(map <> nil, 'GetDirectoryListingFrom(/cfg) not nil');
driver.storage.vfs.FreeDirectoryListing(map);
{ Non-existent path returns nil (no crash) }
map := driver.storage.vfs.GetDirectoryListingFrom('/zzznope', '/');
Assert(map = nil, 'GetDirectoryListingFrom(nonexistent) = nil');
@@ -213,6 +206,50 @@ begin
Assert(driver.storage.vfs.PathValid('/st_test/./') = pvDirectory,
'PathValid with . stays put');
{ ---- Symlink traversal through resolvePathFrom / ChangeDirectoryFrom ---- }
{ Create /st_test/inner, then symlink /st_test/slink -> /st_test/inner }
errCode := driver.storage.vfs.newVirtualDirectory('/st_test/inner');
Assert(errCode = eNone, 'newVDir /st_test/inner = eNone');
errCode := driver.storage.vfs.newVirtualDirectory('/st_test/inner/deep');
Assert(errCode = eNone, 'newVDir /st_test/inner/deep = eNone');
errCode := driver.storage.vfs.CreateSymlink('/st_test/slink', '/st_test/inner');
Assert(errCode = eNone, 'CreateSymlink slink -> inner = eNone');
{ resolvePathFrom through symlink }
res := driver.storage.vfs.ResolvePathFrom('slink', '/st_test');
Assert(res = pvDirectory, 'ResolvePathFrom: slink from /st_test = dir');
{ resolvePathFrom through symlink + subdir }
res := driver.storage.vfs.ResolvePathFrom('/st_test/slink/deep', '/');
Assert(res = pvDirectory, 'ResolvePathFrom: slink/deep = dir');
{ ChangeDirectoryFrom through symlink }
newDir := nil;
res := driver.storage.vfs.ChangeDirectoryFrom('slink', '/st_test', newDir);
Assert(res = pvDirectory, 'ChangeDirFrom slink: returns dir');
if newDir <> nil then kfree(void(newDir));
{ GetDirectoryListingFrom through symlink }
map := driver.storage.vfs.GetDirectoryListingFrom('/st_test/slink', '/');
Assert(map <> nil, 'GetDirListingFrom through slink not nil');
if map <> nil then
driver.storage.vfs.FreeDirectoryListing(map);
{ GetDirectoryListingFrom deeper through symlink }
map := driver.storage.vfs.GetDirectoryListingFrom('/st_test/slink/deep', '/');
Assert(map <> nil, 'GetDirListingFrom slink/deep not nil');
if map <> nil then
driver.storage.vfs.FreeDirectoryListing(map);
{ Nonexistent child after symlink }
res := driver.storage.vfs.ResolvePathFrom('/st_test/slink/nope', '/');
Assert(res = pvInvalid, 'ResolvePathFrom slink/nope = invalid');
{ --- Cleanup: remove all test objects from VFS tree --- }
driver.storage.vfs.RemoveVirtualTree('/st_test');
debug.tracer.pop_trace;
end;
@@ -379,6 +416,7 @@ procedure cmd_stortest(params: PParamList; stdin_buf, stdout_buf, stderr_buf: PO
var
passed, failed : uint32;
pStr, fStr, msg, tmp : pchar;
map : PHashMap;
begin
passed := 0;
failed := 0;
@@ -387,9 +425,11 @@ begin
run_tests(passed, failed, false, stdout_buf);
{ Runtime-only: test /disk listing (populated only after auto_mount_volumes) }
if driver.storage.vfs.GetDirectoryListingFrom('/disk', '/') <> nil then
io.stdio.bufWriteStrLn(stdout_buf, '[+] /disk listing available (volumes mounted)')
else
map := driver.storage.vfs.GetDirectoryListingFrom('/disk', '/');
if map <> nil then begin
io.stdio.bufWriteStrLn(stdout_buf, '[+] /disk listing available (volumes mounted)');
driver.storage.vfs.FreeDirectoryListing(map);
end else
io.stdio.bufWriteStrLn(stdout_buf, '[-] /disk listing nil (no volumes mounted)');
pStr := intToString(passed);
File diff suppressed because it is too large Load Diff