@@ -411,6 +411,7 @@ export class OpenDirectory extends Fd {
411411 inode . parent = parent_entry ;
412412 }
413413 parent_entry . contents . set ( filename , inode ) ;
414+ parent_entry . resolvePendingLinkTarget ( filename , inode ) ;
414415
415416 return wasi . ERRNO_SUCCESS ;
416417 }
@@ -423,10 +424,10 @@ export class OpenDirectory extends Fd {
423424
424425 const {
425426 ret : parent_ret ,
426- parent_entry,
427- filename,
427+ parent_entry : link_parent ,
428+ filename : link_filename ,
428429 } = this . dir . get_parent_dir_and_entry_for_path ( new_path , true ) ;
429- if ( parent_entry == null || filename == null ) {
430+ if ( link_parent == null || link_filename == null ) {
430431 return parent_ret ;
431432 }
432433
@@ -435,12 +436,32 @@ export class OpenDirectory extends Fd {
435436 return old_ret ;
436437 }
437438
438- const { ret : entry_ret , entry } = parent_entry . get_entry_for_path ( old_path ) ;
439- if ( entry == null ) {
440- return entry_ret ;
439+ const {
440+ ret : target_parent_ret ,
441+ parent_entry : target_parent ,
442+ filename : target_filename ,
443+ entry : target_entry ,
444+ } = link_parent . get_parent_dir_and_entry_for_path ( old_path , true ) ;
445+ if ( target_parent == null || target_filename == null ) {
446+ return target_parent_ret ;
447+ }
448+
449+ if ( target_entry != null ) {
450+ return this . path_link ( new_path_str , target_entry , true ) ;
441451 }
442452
443- return this . path_link ( new_path_str , entry , true ) ;
453+ const placeholder = new PendingSymlink (
454+ link_parent ,
455+ link_filename ,
456+ target_parent ,
457+ target_filename ,
458+ ) ;
459+ const link_ret = this . path_link ( new_path_str , placeholder , true ) ;
460+ if ( link_ret != wasi . ERRNO_SUCCESS ) {
461+ return link_ret ;
462+ }
463+ target_parent . registerPendingLinkTarget ( target_filename , placeholder ) ;
464+ return wasi . ERRNO_SUCCESS ;
444465 }
445466
446467 path_unlink ( path_str : string ) : { ret : number ; inode_obj : InodeMem | null } {
@@ -463,6 +484,7 @@ export class OpenDirectory extends Fd {
463484 return { ret : wasi . ERRNO_NOENT , inode_obj : null } ;
464485 }
465486
487+ detachPendingSymlink ( entry ) ;
466488 parent_entry . contents . delete ( filename ) ;
467489
468490 return { ret : wasi . ERRNO_SUCCESS , inode_obj : entry } ;
@@ -486,6 +508,7 @@ export class OpenDirectory extends Fd {
486508 if ( entry . stat ( ) . filetype === wasi . FILETYPE_DIRECTORY ) {
487509 return wasi . ERRNO_ISDIR ;
488510 }
511+ detachPendingSymlink ( entry ) ;
489512 parent_entry . contents . delete ( filename ) ;
490513 return wasi . ERRNO_SUCCESS ;
491514 }
@@ -713,6 +736,7 @@ class Path {
713736export class Directory extends InodeMem {
714737 contents : Map < string , InodeMem > ;
715738 parent : Directory | null = null ;
739+ private pendingSymlinkTargets ?: Map < string , PendingSymlink [ ] > ;
716740
717741 constructor ( contents : Map < string , InodeMem > | [ string , InodeMem ] [ ] ) {
718742 super ( ) ;
@@ -879,9 +903,112 @@ export class Directory extends InodeMem {
879903 }
880904 parent_entry . contents . set ( filename , new_child ) ;
881905 entry = new_child ;
906+ parent_entry . resolvePendingLinkTarget ( filename , entry ) ;
882907
883908 return { ret : wasi . ERRNO_SUCCESS , entry } ;
884909 }
910+
911+ registerPendingLinkTarget ( name : string , pending : PendingSymlink ) : void {
912+ if ( this . pendingSymlinkTargets == undefined ) {
913+ this . pendingSymlinkTargets = new Map ( ) ;
914+ }
915+ const pendingList = this . pendingSymlinkTargets . get ( name ) ;
916+ if ( pendingList == undefined ) {
917+ this . pendingSymlinkTargets . set ( name , [ pending ] ) ;
918+ } else {
919+ pendingList . push ( pending ) ;
920+ }
921+ }
922+
923+ removePendingLinkTarget ( name : string , pending : PendingSymlink ) : void {
924+ const pendingList = this . pendingSymlinkTargets ?. get ( name ) ;
925+ if ( pendingList == undefined ) {
926+ return ;
927+ }
928+ const idx = pendingList . indexOf ( pending ) ;
929+ if ( idx != - 1 ) {
930+ pendingList . splice ( idx , 1 ) ;
931+ }
932+ if ( pendingList . length == 0 ) {
933+ this . pendingSymlinkTargets ?. delete ( name ) ;
934+ }
935+ }
936+
937+ resolvePendingLinkTarget ( name : string , entry : InodeMem ) : void {
938+ const pendingList = this . pendingSymlinkTargets ?. get ( name ) ;
939+ if ( pendingList == undefined ) {
940+ return ;
941+ }
942+ this . pendingSymlinkTargets ?. delete ( name ) ;
943+ for ( const pending of pendingList ) {
944+ pending . resolve ( entry ) ;
945+ }
946+ }
947+ }
948+
949+ class PendingSymlink extends InodeMem {
950+ private readonly parent : Directory ;
951+ private readonly filename : string ;
952+ private targetDir : Directory | null ;
953+ private targetName : string | null ;
954+
955+ constructor (
956+ parent : Directory ,
957+ filename : string ,
958+ targetDir : Directory ,
959+ targetName : string ,
960+ ) {
961+ super ( ) ;
962+ this . parent = parent ;
963+ this . filename = filename ;
964+ this . targetDir = targetDir ;
965+ this . targetName = targetName ;
966+ }
967+
968+ resolve ( target : InodeMem ) : void {
969+ if ( this . parent . contents . get ( this . filename ) !== this ) {
970+ this . detach ( ) ;
971+ return ;
972+ }
973+ if ( target instanceof Directory ) {
974+ target . parent = this . parent ;
975+ }
976+ this . parent . contents . set ( this . filename , target ) ;
977+ this . detach ( ) ;
978+ }
979+
980+ path_open (
981+ _oflags : number ,
982+ _fs_rights_base : bigint ,
983+ _fd_flags : number ,
984+ ) : { ret : number ; fd_obj : Fd | null } {
985+ return { ret : wasi . ERRNO_NOENT , fd_obj : null } ;
986+ }
987+
988+ stat ( ) : wasi . Filestat {
989+ return new wasi . Filestat (
990+ this . ino ,
991+ wasi . FILETYPE_SYMBOLIC_LINK ,
992+ 0n ,
993+ this . atim ,
994+ this . mtim ,
995+ this . ctim ,
996+ ) ;
997+ }
998+
999+ detach ( ) : void {
1000+ if ( this . targetDir != null && this . targetName != null ) {
1001+ this . targetDir . removePendingLinkTarget ( this . targetName , this ) ;
1002+ this . targetDir = null ;
1003+ this . targetName = null ;
1004+ }
1005+ }
1006+ }
1007+
1008+ function detachPendingSymlink ( entry : InodeMem ) : void {
1009+ if ( entry instanceof PendingSymlink ) {
1010+ entry . detach ( ) ;
1011+ }
8851012}
8861013
8871014export class ConsoleStdout extends Fd {
0 commit comments