Skip to content

Commit cc3c628

Browse files
committed
vfs: implement sys_statx
add support for specifying automount
1 parent 40ebab8 commit cc3c628

File tree

6 files changed

+183
-33
lines changed

6 files changed

+183
-33
lines changed

kernel/interfaces/lib/stat.cppm

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,46 @@ export
8484
enum time : std::uint8_t { access = (1 << 0), modify = (1 << 1), status = (1 << 2) };
8585
void update_time(std::uint8_t flags);
8686
};
87+
88+
struct statx_timestamp
89+
{
90+
std::int64_t tv_sec;
91+
std::uint32_t tv_nsec;
92+
std::int32_t __reserved;
93+
};
94+
95+
struct statx
96+
{
97+
std::uint32_t stx_mask;
98+
std::uint32_t stx_blksize;
99+
std::uint64_t stx_attributes;
100+
std::uint32_t stx_nlink;
101+
std::uint32_t stx_uid;
102+
std::uint32_t stx_gid;
103+
std::uint16_t stx_mode;
104+
std::uint16_t __spare0[1];
105+
std::uint64_t stx_ino;
106+
std::uint64_t stx_size;
107+
std::uint64_t stx_blocks;
108+
std::uint64_t stx_attributes_mask;
109+
statx_timestamp stx_atime;
110+
statx_timestamp stx_btime;
111+
statx_timestamp stx_ctime;
112+
statx_timestamp stx_mtime;
113+
std::uint32_t stx_rdev_major;
114+
std::uint32_t stx_rdev_minor;
115+
std::uint32_t stx_dev_major;
116+
std::uint32_t stx_dev_minor;
117+
std::uint64_t stx_mnt_id;
118+
std::uint32_t stx_dio_mem_align;
119+
std::uint32_t stx_dio_offset_align;
120+
std::uint64_t stx_subvol;
121+
std::uint32_t stx_atomic_write_unit_min;
122+
std::uint32_t stx_atomic_write_unit_max;
123+
std::uint32_t stx_atomic_write_segments_max;
124+
std::uint32_t stx_dio_read_offset_align;
125+
std::uint32_t stx_atomic_write_unit_max_opt;
126+
std::uint32_t __spare2[1];
127+
std::uint64_t __spare3[8];
128+
};
87129
} // export

kernel/interfaces/system/syscall/vfs.cppm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ export namespace syscall::vfs
3232
int fstat(int fd, struct stat __user *statbuf);
3333
int lstat(const char __user *pathname, struct stat __user *statbuf);
3434

35+
int statx(int dirfd, const char __user *pathname, int flags, unsigned int mask, struct statx __user *statxbuf);
36+
3537
int faccessat2(int dirfd, const char __user *pathname, int mode, int flags);
3638
int faccessat(int dirfd, const char __user *pathname, int mode);
3739
int access(const char __user *pathname, int mode);

kernel/interfaces/system/vfs/vfs.cppm

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ export namespace vfs
7878
at_symlink_follow = 0x400,
7979
at_eaccess = 0x200,
8080
at_no_automount = 0x800,
81-
at_empty_path = 0x1000
81+
at_empty_path = 0x1000,
82+
at_statx_force_sync = 0x2000,
83+
at_statx_sync_type = 0x6000,
84+
at_statx_dont_sync = 0x4000
8285
};
8386

8487
enum accchecks : int
@@ -560,8 +563,8 @@ export namespace vfs
560563
std::string pathname_from(path path);
561564

562565
auto path_for(lib::path _path) -> lib::expect<path>;
563-
auto resolve(std::optional<path> parent, lib::path path) -> lib::expect<resolve_res>;
564-
auto reduce(path parent, path src, std::size_t symlink_depth = symloop_max) -> lib::expect<path>;
566+
auto resolve(std::optional<path> parent, lib::path path, bool automount = true) -> lib::expect<resolve_res>;
567+
auto reduce(path parent, path src, bool automount = true, std::size_t symlink_depth = symloop_max) -> lib::expect<path>;
565568

566569
auto mount(lib::path source, lib::path target, std::string_view fstype, int flags) -> lib::expect<void>;
567570
auto unmount(lib::path target) -> lib::expect<void>;

kernel/source/arch/x86_64/system/syscall.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ namespace x86_64::syscall
9999
[296] = { "pwritev", vfs::pwritev },
100100
[302] = { "prlimit", proc::prlimit },
101101
[318] = { "getrandom", misc::getrandom },
102+
[332] = { "statx", vfs::statx },
102103
[334] = { "rseq", proc::rseq },
103104
[435] = { "clone3", proc::clone3 },
104105
[439] = { "faccessat2", vfs::faccessat2 }

kernel/source/system/syscall/vfs.cpp

Lines changed: 117 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import system.memory.virt;
77
import system.chrono;
88
import system.vfs;
99
import system.vfs.pipe;
10+
import system.vfs.dev;
1011
import magic_enum;
1112
import arch;
1213
import 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

kernel/source/system/vfs/vfs.cpp

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ namespace vfs
308308
return res->target;
309309
}
310310

311-
auto resolve(std::optional<path> parent, lib::path _path) -> lib::expect<resolve_res>
311+
auto resolve(std::optional<path> parent, lib::path _path, bool automount) -> lib::expect<resolve_res>
312312
{
313313
if (!parent || _path.is_absolute())
314314
parent = get_root(false);
@@ -388,17 +388,20 @@ namespace vfs
388388
auto mnt = current.mnt;
389389

390390
again:
391-
for (const auto &child_mnt : dentry->child_mounts)
391+
if (automount || !last)
392392
{
393-
const auto locked = child_mnt.lock();
394-
if (!locked || !locked->mounted_on.has_value() || !locked->root)
395-
continue;
396-
397-
if (mnt == locked->mounted_on->mnt)
393+
for (const auto &child_mnt : dentry->child_mounts)
398394
{
399-
mnt = locked;
400-
dentry = locked->root;
401-
goto again;
395+
const auto locked = child_mnt.lock();
396+
if (!locked || !locked->mounted_on.has_value() || !locked->root)
397+
continue;
398+
399+
if (mnt == locked->mounted_on->mnt)
400+
{
401+
mnt = locked;
402+
dentry = locked->root;
403+
goto again;
404+
}
402405
}
403406
}
404407

@@ -426,7 +429,7 @@ namespace vfs
426429
return std::unexpected { lib::err::not_found };
427430
}
428431

429-
auto reduce(path parent, path src, std::size_t symlink_depth) -> lib::expect<path>
432+
auto reduce(path parent, path src, bool automount, std::size_t symlink_depth) -> lib::expect<path>
430433
{
431434
const auto is_symlink = [&src]
432435
{
@@ -442,7 +445,7 @@ namespace vfs
442445
if (!is_symlink())
443446
return src;
444447

445-
const auto ret = resolve(parent, src.dentry->symlinked_to);
448+
const auto ret = resolve(parent, src.dentry->symlinked_to, automount);
446449
if (!ret || ret->target.dentry == src.dentry)
447450
return std::unexpected { lib::err::invalid_symlink };
448451

0 commit comments

Comments
 (0)