2#include "metadata-syscalls.h"
3#include "pedigree/kernel/process/Process.h"
4#include "pedigree/kernel/process/TerminationDeferral.h"
5#include "pedigree/kernel/process/Thread.h"
6#include "pedigree/kernel/processor/Processor.h"
7#include "pedigree/kernel/processor/ProcessorInformation.h"
8#include "pedigree/kernel/syscallError.h"
13#include "FileDescriptor.h"
14#include "PosixSubsystem.h"
15#include "file-metadata.h"
16#include "file-syscalls.h"
18#include "metadata-abi.h"
19#include "modules/system/vfs/MountView.h"
20#include "modules/system/vfs/VFS.h"
21#include "user-namespace.h"
25struct MetadataResult {
26 MetadataResult(
int result)
28 error(result < 0 ?
Processor::information().getCurrentThread()->getErrno() : 0) {}
37 enum class Input { Path, OpenDescriptor, NullablePath };
38 bool resolve(
int dirfd,
const char* user,
int flags, Input input = Input::Path) {
39 const bool descriptorOnly = input == Input::OpenDescriptor;
40 if (!descriptorOnly && (user || input != Input::NullablePath)) {
42 if (status != PosixSubsystem::UserStringSuccess) {
43 syscallError(status == PosixSubsystem::UserStringBadAddress ? Error::BadAddress
44 : Error::NameTooLong);
47 if (!copied.length() && !(flags & AT_EMPTY_PATH)) {
48 SYSCALL_ERROR(DoesNotExist);
53 auto* subsystem =
static_cast<PosixSubsystem*
>(process->getSubsystem());
54 context = process->acquireFilesystemContext();
56 const bool absolute = copied.length() && copied[0] ==
'/';
58 if (!absolute && (descriptorOnly || dirfd != AT_FDCWD)) {
59 if (!subsystem || !subsystem->acquireFileDescriptor(dirfd, descriptor) ||
60 !descriptor->getFile() || (descriptorOnly && (descriptor->getStatusFlags() & O_PATH))) {
61 SYSCALL_ERROR(BadFileDescriptor);
64 file = descriptor->getFile();
65 retainedPath = descriptor->openingPath();
68 if (!file->isDirectory() || !retainedPath) {
69 SYSCALL_ERROR(NotADirectory);
74 if (!context || !view) {
75 SYSCALL_ERROR(DoesNotExist);
78 if (!copied.length()) {
80 if (!context->snapshot(snapshot) || !snapshot.cwd) {
81 SYSCALL_ERROR(DoesNotExist);
84 retainedPath = snapshot.cwd;
87 normalisePath(normalised, copied.cstr());
89 options.followFinal = !(flags & AT_SYMLINK_NOFOLLOW);
90 if (!view->resolve(context, start, normalised,
options, retainedPath))
93 file = retainedPath->node();
94 return file !=
nullptr;
106bool writableFilesystem(
File* file) {
107 if (file->getFilesystem() && file->getFilesystem()->
isReadOnly()) {
108 SYSCALL_ERROR(ReadOnlyFilesystem);
114MetadataResult truncatePath(
const char* path, off_t length) {
116 SYSCALL_ERROR(InvalidArgument);
121 if (!target.resolve(AT_FDCWD, path, 0))
123 if (target.file->isDirectory()) {
124 SYSCALL_ERROR(IsADirectory);
127 if (!target.file->supportsRegularFileOperations()) {
128 SYSCALL_ERROR(InvalidArgument);
131 if ((!writableFilesystem(target.file) ||
132 (target.retainedPath && !mountWrite.acquire(target.retainedPath))) ||
135 if (!posix_landlock_check(target.retainedPath, LandlockAccess::Truncate)) {
138 return target.file->resize(
static_cast<size_t>(length)) ? 0 : -1;
141MetadataResult linkOwnership(
const char* path, uid_t owner, gid_t group) {
144 if (!target.resolve(AT_FDCWD, path, AT_SYMLINK_NOFOLLOW) ||
145 (!writableFilesystem(target.file) ||
146 (target.retainedPath && !mountWrite.acquire(target.retainedPath))))
148 return posix_chown_file(target.file, owner, group) ? 0 : -1;
151MetadataResult chmodAt(
int dirfd,
const char* path, mode_t mode,
int flags) {
152 if (flags & ~(AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW)) {
153 SYSCALL_ERROR(InvalidArgument);
158 if (!target.resolve(dirfd, path, flags) ||
159 (!writableFilesystem(target.file) ||
160 (target.retainedPath && !mountWrite.acquire(target.retainedPath))))
162 if (target.file->isSymlink()) {
163 SYSCALL_ERROR(OperationNotSupported);
166 return posix_chmod_file(target.file, mode) ? 0 : -1;
169MetadataResult updateTimes(
int dirfd,
const char* path,
const void* userTimes,
int flags) {
170 using namespace PosixMetadata;
171 Timespec times[2] = {{0, TimeNow}, {0, TimeNow}};
173 SYSCALL_ERROR(BadAddress);
177 if (times[0].nanoseconds == TimeOmit && times[1].nanoseconds == TimeOmit)
179 for (
const auto& time : times) {
180 if (!validNanoseconds(time.nanoseconds)) {
181 SYSCALL_ERROR(InvalidArgument);
184 if (time.nanoseconds != TimeNow && time.nanoseconds != TimeOmit &&
185 (time.seconds < 0 ||
static_cast<uint64_t
>(time.seconds) > UINT32_MAX)) {
186 SYSCALL_ERROR(ValueTooLarge);
190 const bool descriptorOnly = !path && dirfd != AT_FDCWD;
191 if ((flags & ~(AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW)) || (descriptorOnly && flags)) {
192 SYSCALL_ERROR(InvalidArgument);
199 descriptorOnly ? MetadataPath::Input::OpenDescriptor : MetadataPath::Input::Path) ||
200 (!writableFilesystem(target.file) ||
201 (target.retainedPath && !mountWrite.acquire(target.retainedPath))))
204 if (!Process::currentFilesystemCredentials(credentials)) {
205 SYSCALL_ERROR(NotEnoughPermissions);
209 posix_global_capable(PosixCapabilities::Fowner) || credentials.uid == target.file->getUid();
210 const bool touch = times[0].nanoseconds == TimeNow && times[1].nanoseconds == TimeNow;
211 if (!owner && !touch) {
212 SYSCALL_ERROR(NotEnoughPermissions);
217 const Time::Timestamp now = Time::getTime();
218 target.file->setTimes(times[0].nanoseconds == TimeNow ? now : times[0].seconds,
219 times[1].nanoseconds == TimeNow ? now : times[1].seconds,
220 times[0].nanoseconds != TimeOmit, times[1].nanoseconds != TimeOmit);
224MetadataResult extendedStat(
int dirfd,
const char* path,
int flags,
unsigned mask,
void* output) {
225 constexpr unsigned SyncFlags = 0x6000, NoAutomount = 0x800, ReservedMask = 0x80000000U;
226 if ((
static_cast<unsigned>(flags) &
227 ~(AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW | NoAutomount | SyncFlags)) ||
228 (flags & SyncFlags) == SyncFlags || (mask & ReservedMask)) {
229 SYSCALL_ERROR(InvalidArgument);
235 (flags & AT_EMPTY_PATH) ? MetadataPath::Input::NullablePath : MetadataPath::Input::Path))
237 struct stat ordinary = {};
238 if (!posix_stat_file(target.copied.cstr(), target.file, &ordinary))
241 snapshot.mask = 0x7ff;
242 snapshot.blockSize = ordinary.st_blksize;
243 snapshot.links = ordinary.st_nlink;
244 snapshot.uid = ordinary.st_uid;
245 snapshot.gid = ordinary.st_gid;
246 snapshot.mode = ordinary.st_mode;
247 snapshot.inode = ordinary.st_ino;
248 snapshot.size = ordinary.st_size;
249 snapshot.blocks = ordinary.st_blocks;
250 snapshot.accessed.seconds = ordinary.st_atime;
251 snapshot.changed.seconds = ordinary.st_ctime;
252 snapshot.modified.seconds = ordinary.st_mtime;
253 if (target.retainedPath) {
254 snapshot.mountId =
VFS::instance().mountView()->attachmentId(target.retainedPath);
255 if (snapshot.mountId)
256 snapshot.mask |= 0x1000;
258 snapshot.deviceMajor = PosixMetadata::deviceMajor(ordinary.st_rdev);
259 snapshot.deviceMinor = PosixMetadata::deviceMinor(ordinary.st_rdev);
260 snapshot.filesystemMajor = PosixMetadata::deviceMajor(ordinary.st_dev);
261 snapshot.filesystemMinor = PosixMetadata::deviceMinor(ordinary.st_dev);
263 SYSCALL_ERROR(BadAddress);
270int posix_truncate(
const char* path, off_t length) {
271 const MetadataResult result = truncatePath(path, length);
272 syscallError(result.error);
275int posix_lchown(
const char* path, uid_t owner, gid_t group) {
276 const MetadataResult result = linkOwnership(path, owner, group);
277 syscallError(result.error);
280int posix_utimensat(
int dirfd,
const char* path,
const void* times,
int flags) {
281 const MetadataResult result = updateTimes(dirfd, path, times, flags);
282 syscallError(result.error);
285int posix_statx(
int dirfd,
const char* path,
int flags,
unsigned mask,
void* output) {
286 const MetadataResult result = extendedStat(dirfd, path, flags, mask, output);
287 syscallError(result.error);
290int posix_fchmodat2(
int dirfd,
const char* path, mode_t mode,
int flags) {
291 const MetadataResult result = chmodAt(dirfd, path, mode, flags);
292 syscallError(result.error);
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static UserStringResult copyUserString(const char *userString, String ©, size_t maxLength)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static ProcessorInformation & information()
static bool checkAccess(File *pFile, bool bRead, bool bWrite, bool bExecute)