@@ -370,6 +370,187 @@ impl Cage {
370
370
371
371
}
372
372
373
+ //------------------------------------READLINK and READLINKAT SYSCALL------------------------------------
374
+ /*
375
+ * The return value of the readlink syscall indicates the number of bytes written into the buf and -1 if
376
+ * error. The contents of the buf represent the file path that the symbolic link points to. Since the file
377
+ * path perspectives differ between the user application and the host Linux, the readlink implementation
378
+ * requires handling the paths for both the input passed to the Rust kernel libc and the output buffer
379
+ * returned by the kernel libc.
380
+ *
381
+ * For the input path, the transformation is straightforward: we prepend the LIND_ROOT prefix to convert
382
+ * the user's relative path into a host-compatible absolute path.
383
+ * However, for the output buffer, we need to first verify whether the path written to buf is an absolute
384
+ * path. If it is not, we prepend the current working directory to make it absolute. Next, we remove the
385
+ * LIND_ROOT prefix to adjust the path to the user's perspective. Finally, we truncate the adjusted result
386
+ * to fit within the user-provided buflen, ensuring compliance with the behavior described in the Linux
387
+ * readlink man page, which states that truncation is performed silently if the buffer is too small.
388
+ */
389
+ pub fn readlink_syscall (
390
+ & self ,
391
+ path : & str ,
392
+ buf : * mut u8 ,
393
+ buflen : usize ,
394
+ ) -> i32 {
395
+ // Convert the path from relative path (lind-wasm perspective) to real kernel path (host kernel
396
+ // perspective)
397
+ let relpath = normpath ( convpath ( path) , self ) ;
398
+ let relative_path = relpath. to_str ( ) . unwrap ( ) ;
399
+ let full_path = format ! ( "{}{}" , LIND_ROOT , relative_path) ;
400
+ let c_path = CString :: new ( full_path) . unwrap ( ) ;
401
+
402
+ // Call libc::readlink to get the original symlink target
403
+ let libc_buflen = buflen + LIND_ROOT . len ( ) ;
404
+ let mut libc_buf = vec ! [ 0u8 ; libc_buflen] ;
405
+ let libcret = unsafe {
406
+ libc:: readlink ( c_path. as_ptr ( ) , libc_buf. as_mut_ptr ( ) as * mut c_char , libc_buflen)
407
+ } ;
408
+
409
+ if libcret < 0 {
410
+ let errno = get_errno ( ) ;
411
+ return handle_errno ( errno, "readlink" ) ;
412
+ }
413
+
414
+ // Convert the result from readlink to a Rust string
415
+ let libcbuf_str = unsafe {
416
+ CStr :: from_ptr ( libc_buf. as_ptr ( ) as * const c_char )
417
+ } . to_str ( ) . unwrap ( ) ;
418
+
419
+ // Use libc::getcwd to get the current working directory
420
+ let mut cwd_buf = vec ! [ 0u8 ; 4096 ] ;
421
+ let cwd_ptr = unsafe { libc:: getcwd ( cwd_buf. as_mut_ptr ( ) as * mut c_char , cwd_buf. len ( ) ) } ;
422
+ if cwd_ptr. is_null ( ) {
423
+ let errno = get_errno ( ) ;
424
+ return handle_errno ( errno, "getcwd" ) ;
425
+ }
426
+
427
+ let pwd = unsafe { CStr :: from_ptr ( cwd_buf. as_ptr ( ) as * const c_char ) }
428
+ . to_str ( )
429
+ . unwrap ( ) ;
430
+
431
+ // Adjust the result to user perspective
432
+ // Verify if libcbuf_str starts with the current working directory (pwd)
433
+ let adjusted_result = if libcbuf_str. starts_with ( pwd) {
434
+ libcbuf_str. to_string ( )
435
+ } else {
436
+ format ! ( "{}/{}" , pwd, libcbuf_str)
437
+ } ;
438
+ let new_root = format ! ( "{}/" , LIND_ROOT ) ;
439
+ let final_result = adjusted_result. strip_prefix ( & new_root) . unwrap_or ( & adjusted_result) ;
440
+
441
+ // Check the length and copy the appropriate amount of data to buf
442
+ let bytes_to_copy = std:: cmp:: min ( buflen, final_result. len ( ) ) ;
443
+ unsafe {
444
+ std:: ptr:: copy_nonoverlapping ( final_result. as_ptr ( ) , buf, bytes_to_copy) ;
445
+ }
446
+
447
+ bytes_to_copy as i32
448
+ }
449
+
450
+ /*
451
+ * The readlinkat syscall builds upon the readlink syscall, with additional handling for the provided fd.
452
+ * There are two main cases to consider:
453
+ *
454
+ * When fd is the special value AT_FDCWD:
455
+ * In this case, we first retrieve the current working directory path. We then append the user-provided path
456
+ * to this directory path to create a complete path. After this, the handling is identical to the readlink
457
+ * syscall. Therefore, the implementation delegates the underlying work to the readlink syscall.
458
+ *
459
+ * One notable point is that when fd = AT_FDCWD, there is no need to convert the virtual fd. Due to Rust's
460
+ * variable scoping rules and for safety considerations (we must use the predefined fdtable API). This results
461
+ * in approximately four lines of repetitive code during the path conversion step. If we plan to optimize
462
+ * the implementation in the future, we can consider abstracting this step into a reusable function to avoid
463
+ * redundancy.
464
+ *
465
+ * When fd is a directory fd:
466
+ * Handling this case is difficult without access to kernel-space code. In Linux, there is no syscall that
467
+ * provides a method to resolve the directory path corresponding to a given dirfd. The Linux kernel handles
468
+ * this step by utilizing its internal dentry data structure, which is not accessible from user space.
469
+ * Therefore, in the RawPOSIX implementation, we assume that all paths are absolute to simplify the resolution
470
+ * process.
471
+ *
472
+ */
473
+ pub fn readlinkat_syscall (
474
+ & self ,
475
+ virtual_fd : i32 ,
476
+ path : & str ,
477
+ buf : * mut u8 ,
478
+ buflen : usize ,
479
+ ) -> i32 {
480
+ let mut libcret;
481
+ let mut path = path. to_string ( ) ;
482
+ let libc_buflen = buflen + LIND_ROOT . len ( ) ;
483
+ let mut libc_buf = vec ! [ 0u8 ; libc_buflen] ;
484
+ if virtual_fd == libc:: AT_FDCWD {
485
+ // Check if the fd is AT_FDCWD
486
+ let cwd_container = self . cwd . read ( ) ;
487
+ path = format ! ( "{}/{}" , cwd_container. to_str( ) . unwrap( ) , path) ;
488
+ // Convert the path from relative path (lind-wasm perspective) to real kernel path (host kernel
489
+ // perspective)
490
+ let relpath = normpath ( convpath ( & path) , self ) ;
491
+ let relative_path = relpath. to_str ( ) . unwrap ( ) ;
492
+ let full_path = format ! ( "{}{}" , LIND_ROOT , relative_path) ;
493
+ let c_path = CString :: new ( full_path) . unwrap ( ) ;
494
+
495
+ libcret = unsafe {
496
+ libc:: readlink ( c_path. as_ptr ( ) , libc_buf. as_mut_ptr ( ) as * mut c_char , libc_buflen)
497
+ } ;
498
+
499
+ } else {
500
+ // Convert the virtual fd into real kernel fd and handle the error case
501
+ let wrappedvfd = fdtables:: translate_virtual_fd ( self . cageid , virtual_fd as u64 ) ;
502
+ if wrappedvfd. is_err ( ) {
503
+ return syscall_error ( Errno :: EBADF , "readlinkat" , "Bad File Descriptor" ) ;
504
+ }
505
+ let vfd = wrappedvfd. unwrap ( ) ;
506
+ // Convert the path from relative path (lind-wasm perspective) to real kernel path (host kernel
507
+ // perspective)
508
+ let relpath = normpath ( convpath ( & path) , self ) ;
509
+ let relative_path = relpath. to_str ( ) . unwrap ( ) ;
510
+ let full_path = format ! ( "{}{}" , LIND_ROOT , relative_path) ;
511
+ let c_path = CString :: new ( full_path) . unwrap ( ) ;
512
+
513
+ libcret = unsafe {
514
+ libc:: readlinkat ( vfd. underfd as i32 , c_path. as_ptr ( ) , libc_buf. as_mut_ptr ( ) as * mut c_char , libc_buflen)
515
+ } ;
516
+ }
517
+
518
+ if libcret < 0 {
519
+ let errno = get_errno ( ) ;
520
+ return handle_errno ( errno, "readlinkat" ) ;
521
+ }
522
+
523
+ // Convert the result from readlink to a Rust string
524
+ let libcbuf_str = unsafe {
525
+ CStr :: from_ptr ( libc_buf. as_ptr ( ) as * const c_char )
526
+ } . to_str ( ) . unwrap ( ) ;
527
+
528
+ // Use libc::getcwd to get the current working directory
529
+ let mut cwd_buf = vec ! [ 0u8 ; 4096 ] ;
530
+ let cwd_ptr = unsafe { libc:: getcwd ( cwd_buf. as_mut_ptr ( ) as * mut c_char , cwd_buf. len ( ) ) } ;
531
+ if cwd_ptr. is_null ( ) {
532
+ let errno = get_errno ( ) ;
533
+ return handle_errno ( errno, "getcwd" ) ;
534
+ }
535
+
536
+ let pwd = unsafe { CStr :: from_ptr ( cwd_buf. as_ptr ( ) as * const c_char ) }
537
+ . to_str ( )
538
+ . unwrap ( ) ;
539
+
540
+ // Adjust the result to user perspective
541
+ let adjusted_result = format ! ( "{}/{}" , pwd, libcbuf_str) ;
542
+ let new_root = format ! ( "{}/" , LIND_ROOT ) ;
543
+ let final_result = adjusted_result. strip_prefix ( & new_root) . unwrap_or ( & adjusted_result) ;
544
+
545
+ // Check the length and copy the appropriate amount of data to buf
546
+ let bytes_to_copy = std:: cmp:: min ( buflen, final_result. len ( ) ) ;
547
+ unsafe {
548
+ std:: ptr:: copy_nonoverlapping ( final_result. as_ptr ( ) , buf, bytes_to_copy) ;
549
+ }
550
+
551
+ bytes_to_copy as i32
552
+ }
553
+
373
554
//------------------------------------WRITE SYSCALL------------------------------------
374
555
/*
375
556
* Get the kernel fd with provided virtual fd first
0 commit comments