@@ -7,6 +7,7 @@ import system.memory.virt;
77import system.chrono;
88import system.vfs;
99import system.vfs.pipe;
10+ import system.vfs.dev;
1011import magic_enum;
1112import arch;
1213import lib;
@@ -43,13 +44,16 @@ namespace syscall::vfs
4344 return fd->file ->path ;
4445 }
4546
46- std::optional<resolve_res> resolve_from (sched::process *proc, int dirfd, lib::path_view path)
47+ std::optional<resolve_res> resolve_from (
48+ sched::process *proc, int dirfd,
49+ lib::path_view path, bool automount = true
50+ )
4751 {
4852 auto parent = get_parent (proc, dirfd, path);
4953 if (!parent.has_value ())
5054 return std::nullopt ;
5155
52- auto res = resolve (std::move (parent), path);
56+ auto res = resolve (std::move (parent), path, automount );
5357 if (!res.has_value ())
5458 return (errno = lib::map_error (res.error ()), std::nullopt );
5559
@@ -79,25 +83,39 @@ namespace syscall::vfs
7983 return path;
8084 }
8185
82- std::optional<path> get_target (sched::process *proc, int dirfd, const char __user *pathname, bool follow_links, bool empty_path)
86+ std::optional<path> get_target (
87+ sched::process *proc, int dirfd, const char __user *pathname,
88+ bool follow_links, bool empty_path, bool automount)
8389 {
8490 if (empty_path)
8591 {
86- if (dirfd == at_fdcwd)
87- return proc->cwd ;
92+ bool use_dirfd = (pathname == nullptr );
93+ if (!use_dirfd)
94+ {
95+ const auto pathname_len = lib::strnlen_user (pathname, 1 );
96+ if (pathname_len < 0 )
97+ return (errno = EFAULT, std::nullopt );
98+ use_dirfd = (pathname_len == 0 );
99+ }
88100
89- auto fd = get_fd (proc, dirfd);
90- if (fd == nullptr )
91- return std::nullopt ;
101+ if (use_dirfd)
102+ {
103+ if (dirfd == at_fdcwd)
104+ return proc->cwd ;
92105
93- return fd->file ->path ;
106+ auto fd = get_fd (proc, dirfd);
107+ if (fd == nullptr )
108+ return std::nullopt ;
109+
110+ return fd->file ->path ;
111+ }
94112 }
95113
96114 auto val = get_path (pathname);
97115 if (!val.has_value ())
98116 return std::nullopt ;
99117
100- auto res = resolve_from (proc, dirfd, std::move (*val));
118+ auto res = resolve_from (proc, dirfd, std::move (*val), automount );
101119 if (!res.has_value ())
102120 return std::nullopt ;
103121
@@ -106,7 +124,7 @@ namespace syscall::vfs
106124
107125 if (follow_links)
108126 {
109- auto reduced = reduce (std::move (res->parent ), std::move (target));
127+ auto reduced = reduce (std::move (res->parent ), std::move (target), automount );
110128 if (!reduced.has_value ())
111129 return (errno = lib::map_error (reduced.error ()), std::nullopt );
112130 target = std::move (*reduced);
@@ -738,10 +756,14 @@ namespace syscall::vfs
738756 {
739757 const auto proc = sched::this_thread ()->parent ;
740758
759+ if (flags & ~(at_symlink_nofollow | at_no_automount | at_empty_path))
760+ return (errno = EINVAL, -1 );
761+
741762 const bool follow_links = (flags & at_symlink_nofollow) == 0 ;
742763 const bool empty_path = (flags & at_empty_path) != 0 ;
764+ const bool automount = true ; // (flags & at_no_automount) == 0;
743765
744- const auto target = get_target (proc, dirfd, pathname, follow_links, empty_path);
766+ const auto target = get_target (proc, dirfd, pathname, follow_links, empty_path, automount );
745767 if (!target.has_value ())
746768 return -1 ;
747769
@@ -752,17 +774,94 @@ namespace syscall::vfs
752774
753775 int stat (const char __user *pathname, struct stat __user *statbuf)
754776 {
755- return fstatat (at_fdcwd, pathname, statbuf, 0 );
777+ return fstatat (at_fdcwd, pathname, statbuf, at_no_automount );
756778 }
757779
758780 int fstat (int fd, struct stat __user *statbuf)
759781 {
760- return fstatat (fd, nullptr , statbuf, at_empty_path);
782+ return fstatat (fd, nullptr , statbuf, at_empty_path | at_no_automount );
761783 }
762784
763785 int lstat (const char __user *pathname, struct stat __user *statbuf)
764786 {
765- return fstatat (at_fdcwd, pathname, statbuf, at_symlink_nofollow);
787+ return fstatat (at_fdcwd, pathname, statbuf, at_symlink_nofollow | at_no_automount);
788+ }
789+
790+ int statx (int dirfd, const char __user *pathname, int flags, unsigned int mask, struct statx __user *statxbuf)
791+ {
792+ const auto proc = sched::this_thread ()->parent ;
793+
794+ constexpr auto valid_flags =
795+ at_symlink_nofollow |
796+ at_no_automount |
797+ at_empty_path |
798+ at_statx_sync_type;
799+
800+ if ((flags & ~valid_flags) != 0 )
801+ return (errno = EINVAL, -1 );
802+
803+ const auto sync_mode = flags & at_statx_sync_type;
804+ if (sync_mode == (at_statx_force_sync | at_statx_dont_sync))
805+ return (errno = EINVAL, -1 );
806+
807+ constexpr std::uint32_t statx_basic_stats = 0x000007FFu ;
808+ constexpr std::uint32_t statx_mnt_id = 0x00001000u ;
809+ constexpr std::uint32_t statx_reserved = 0x80000000u ;
810+
811+ if (mask & statx_reserved)
812+ return (errno = EINVAL, -1 );
813+
814+ const bool follow_links = (flags & at_symlink_nofollow) == 0 ;
815+ const bool empty_path = (flags & at_empty_path) != 0 ;
816+ const bool automount = (flags & at_no_automount) == 0 ;
817+
818+ const auto target = get_target (proc, dirfd, pathname, follow_links, empty_path, automount);
819+ if (!target.has_value ())
820+ return -1 ;
821+
822+ struct stat val;
823+ {
824+ const std::unique_lock _ { target->dentry ->inode ->lock };
825+ val = target->dentry ->inode ->stat ;
826+ }
827+
828+ constexpr auto to_statx_timestamp = [](const timespec &ts)
829+ {
830+ return statx_timestamp {
831+ .tv_sec = static_cast <std::int64_t >(ts.tv_sec ),
832+ .tv_nsec = static_cast <std::uint32_t >(ts.tv_nsec ),
833+ .__reserved = 0
834+ };
835+ };
836+
837+ struct statx ret { };
838+ ret.stx_mask = statx_basic_stats;
839+ ret.stx_blksize = static_cast <std::uint32_t >(val.st_blksize );
840+ ret.stx_nlink = static_cast <std::uint32_t >(val.st_nlink );
841+ ret.stx_uid = static_cast <std::uint32_t >(val.st_uid );
842+ ret.stx_gid = static_cast <std::uint32_t >(val.st_gid );
843+ ret.stx_mode = static_cast <std::uint16_t >(val.st_mode );
844+ ret.stx_ino = static_cast <std::uint64_t >(val.st_ino );
845+ ret.stx_size = static_cast <std::uint64_t >(val.st_size );
846+ ret.stx_blocks = static_cast <std::uint64_t >(val.st_blocks );
847+ ret.stx_atime = to_statx_timestamp (val.st_atim );
848+ ret.stx_ctime = to_statx_timestamp (val.st_ctim );
849+ ret.stx_mtime = to_statx_timestamp (val.st_mtim );
850+ ret.stx_rdev_major = dev::major (val.st_rdev );
851+ ret.stx_rdev_minor = dev::minor (val.st_rdev );
852+ ret.stx_dev_major = dev::major (val.st_dev );
853+ ret.stx_dev_minor = dev::minor (val.st_dev );
854+
855+ if ((mask & statx_mnt_id) != 0 && target->mnt != nullptr )
856+ {
857+ ret.stx_mask |= statx_mnt_id;
858+ ret.stx_mnt_id = target->mnt ->fs .lock ()->dev_id ;
859+ }
860+
861+ if (!lib::copy_to_user (statxbuf, &ret, sizeof (struct statx )))
862+ return (errno = EFAULT, -1 );
863+
864+ return 0 ;
766865 }
767866
768867 int faccessat2 (int dirfd, const char __user *pathname, int mode, int flags)
@@ -773,7 +872,7 @@ namespace syscall::vfs
773872 const bool empty_path = (flags & at_empty_path) != 0 ;
774873 const bool eaccess = (flags & at_eaccess) != 0 ;
775874
776- const auto target = get_target (proc, dirfd, pathname, follow_links, empty_path);
875+ const auto target = get_target (proc, dirfd, pathname, follow_links, empty_path, true );
777876 if (!target.has_value ())
778877 return -1 ;
779878
@@ -896,7 +995,7 @@ namespace syscall::vfs
896995 {
897996 const auto proc = sched::this_thread ()->parent ;
898997
899- const auto target = get_target (proc, at_fdcwd, pathname, true , false );
998+ const auto target = get_target (proc, at_fdcwd, pathname, true , false , true );
900999 if (!target.has_value ())
9011000 return -1 ;
9021001
@@ -911,7 +1010,7 @@ namespace syscall::vfs
9111010 {
9121011 const auto proc = sched::this_thread ()->parent ;
9131012
914- const auto target = get_target (proc, fd, nullptr , true , true );
1013+ const auto target = get_target (proc, fd, nullptr , true , true , true );
9151014 if (!target.has_value ())
9161015 return -1 ;
9171016
0 commit comments