8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/errors.h"
11#include "pedigree/kernel/process/Process.h"
12#include "pedigree/kernel/process/Thread.h"
13#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
14#include "pedigree/kernel/processor/Processor.h"
15#include "pedigree/kernel/utilities/utility.h"
20#include "modules/subsys/posix/FileDescriptor.h"
21#include "modules/subsys/posix/PosixProcess.h"
22#include "modules/subsys/posix/PosixSubsystem.h"
23#include "modules/subsys/posix/UnixFilesystem.h"
24#include "modules/subsys/posix/file-syscalls.h"
25#include "modules/system/vfs/File.h"
27#include "modules/system/vfs/MountView.h"
28#include "modules/system/vfs/Symlink.h"
29#include "modules/system/vfs/VFS.h"
32constexpr size_t AccessDescriptor = 97;
33constexpr size_t DirectoryDescriptor = 98;
34constexpr int PreservedErrno = 149;
36class AccessSymlink final :
public Symlink {
39 :
Symlink(name, 0, 0, 0, 2, filesystem, target.length(), parent), m_Target(target) {}
42 uint64_t
readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
bool)
override {
43 if (location >= m_Target.length()) {
46 if (size > m_Target.length() - location) {
47 size = m_Target.length() - location;
49 MemoryCopy(
reinterpret_cast<void*
>(buffer), m_Target.cstr() + location, size);
60 pathResolution(false),
76bool allocateUserMapping(
Process* process,
size_t length, uintptr_t& address) {
78 if (!process->allocateUserRange(Process::UserRegion::Normal, length, address)) {
82 uintptr_t mappedAddress = address;
84 mappedAddress, length, MemoryMappedObject::Read | MemoryMappedObject::Write);
85 if (!mapping || mappedAddress != address) {
87 process->freeUserRange(Process::UserRegion::Normal, address, length);
94bool expectFailure(
Thread* thread,
int result,
size_t error) {
95 return result == -1 && thread->
getErrno() == error;
98bool expectSuccess(
Thread* thread,
int result) {
99 return result == 0 && thread->
getErrno() == PreservedErrno;
102int accessWorker(
void* parameter) {
103 AccessContext* context =
reinterpret_cast<AccessContext*
>(parameter);
107 uintptr_t address = 0;
108 if (!allocateUserMapping(process, pageSize, address)) {
109 context->returned += 1;
113 constexpr char AbsolutePath[] =
"/access-file";
114 constexpr char RelativePath[] =
"access-file";
115 constexpr char MissingPath[] =
"/access-missing";
116 constexpr char SymlinkPath[] =
"/access-link";
117 char* absolutePath =
reinterpret_cast<char*
>(address);
118 char* relativePath = absolutePath +
sizeof(AbsolutePath);
119 char* missingPath = relativePath +
sizeof(RelativePath);
120 char* symlinkPath = missingPath +
sizeof(MissingPath);
121 char* emptyPath = symlinkPath +
sizeof(SymlinkPath);
122 MemoryCopy(absolutePath, AbsolutePath,
sizeof(AbsolutePath));
123 MemoryCopy(relativePath, RelativePath,
sizeof(RelativePath));
124 MemoryCopy(missingPath, MissingPath,
sizeof(MissingPath));
125 MemoryCopy(symlinkPath, SymlinkPath,
sizeof(SymlinkPath));
129 const bool invalidMode =
130 expectFailure(thread, posix_faccessat(AT_FDCWD, absolutePath, 8, 0), Error::InvalidArgument);
132 const bool invalidFlags = expectFailure(
133 thread, posix_faccessat(AT_FDCWD, absolutePath, F_OK, 0x40000000), Error::InvalidArgument);
134 context->validation = invalidMode && invalidFlags;
137 const bool absoluteIgnoresFd = expectSuccess(thread, posix_faccessat(-1, absolutePath, F_OK, 0));
139 const bool relativeRejectsFd =
140 expectFailure(thread, posix_faccessat(-1, relativePath, F_OK, 0), Error::BadFileDescriptor);
142 const bool relativeRejectsFile = expectFailure(
143 thread, posix_faccessat(AccessDescriptor, relativePath, F_OK, 0), Error::NotADirectory);
145 const bool relativeUsesDirectory =
146 expectSuccess(thread, posix_faccessat(DirectoryDescriptor, relativePath, F_OK, 0));
148 const bool missingIsEnoent =
149 expectFailure(thread, posix_faccessat(-1, missingPath, F_OK, 0), Error::DoesNotExist);
150 context->pathResolution = absoluteIgnoresFd && relativeRejectsFd && relativeRejectsFile &&
151 relativeUsesDirectory && missingIsEnoent;
154 const bool realDenied = expectFailure(thread, posix_faccessat(AT_FDCWD, absolutePath, R_OK, 0),
155 Error::PermissionDenied);
157 const bool effectiveAllowed =
158 expectSuccess(thread, posix_faccessat(AT_FDCWD, absolutePath, R_OK, AT_EACCESS));
159 context->credentials = realDenied && effectiveAllowed && process->
getUserId() == 100 &&
160 process->getEffectiveUserId() == 200 && process->getGroupId() == 10 &&
161 process->getEffectiveGroupId() == 20;
164 const bool emptyNeedsFlag = expectFailure(
165 thread, posix_faccessat(AccessDescriptor, emptyPath, F_OK, 0), Error::DoesNotExist);
167 const bool emptyDescriptorExists =
168 expectSuccess(thread, posix_faccessat(AccessDescriptor, emptyPath, F_OK, AT_EMPTY_PATH));
170 const bool emptyCwdExists =
171 expectSuccess(thread, posix_faccessat(AT_FDCWD, emptyPath, F_OK, AT_EMPTY_PATH));
173 const bool emptyRealDenied =
174 expectFailure(thread, posix_faccessat(AccessDescriptor, emptyPath, R_OK, AT_EMPTY_PATH),
175 Error::PermissionDenied);
177 const bool emptyEffectiveAllowed = expectSuccess(
178 thread, posix_faccessat(AccessDescriptor, emptyPath, R_OK, AT_EMPTY_PATH | AT_EACCESS));
180 const bool emptyRejectsFd = expectFailure(
181 thread, posix_faccessat(-1, emptyPath, F_OK, AT_EMPTY_PATH), Error::BadFileDescriptor);
182 context->emptyPath = emptyNeedsFlag && emptyDescriptorExists && emptyCwdExists &&
183 emptyRealDenied && emptyEffectiveAllowed && emptyRejectsFd;
186 const bool followsByDefault = expectFailure(
187 thread, posix_faccessat(AT_FDCWD, symlinkPath, W_OK, 0), Error::PermissionDenied);
189 const bool nofollowChecksLink =
190 expectSuccess(thread, posix_faccessat(AT_FDCWD, symlinkPath, W_OK, AT_SYMLINK_NOFOLLOW));
191 context->symlinks = followsByDefault && nofollowChecksLink;
195 expectFailure(thread, posix_faccessat(AT_FDCWD,
nullptr, F_OK, 0), Error::BadAddress);
198 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
199 context->returned += 1;
209bool accessSemantics(
Process* kernelProcess) {
212 VFS::HostedRootViewScope fixture;
214 if (!fixture.open(filesystem)) {
221 File* target =
new File(
String(
"access-file"), 0, 0, 0, 1, filesystem, 0, root);
224 target->setPermissions(FILE_UR);
225 const bool targetAdded = directory->addEntry(target->
getName(), target);
227 AccessSymlink* link =
228 new AccessSymlink(
String(
"access-link"),
String(
"/access-file"), filesystem, root);
231 link->setPermissions(FILE_UW);
232 const bool linkAdded = directory->addEntry(link->getName(), link);
235 new PosixProcess(kernelProcess,
true, Process::FilesystemContextMode::Deferred);
237 process->setSubsystem(subsystem);
238 subsystem->
setAbi(PosixSubsystem::LinuxAbi);
239 const bool contextInstalled = fixture.installContext(*process);
240 process->setUserId(100);
241 process->setEffectiveUserId(200);
242 process->setGroupId(10);
243 process->setEffectiveGroupId(20);
247 const bool rootResolved = fixture.view()->bootRootPath(rootPath);
250 new FileDescriptor(rootPath, 0, DirectoryDescriptor, 0, O_RDONLY));
252 AccessContext context;
253 Thread*
worker =
new Thread(process, accessWorker, &context,
nullptr,
false,
true,
true);
254 worker->setName(
"hosted faccessat2 semantics");
256 targetAdded && linkAdded && contextInstalled && rootResolved &&
worker->start();
257 const bool joined = started &&
worker->joinForCompletion();
262 bool passed = started && joined && context.returned == 1 && context.validation &&
263 context.pathResolution && context.credentials && context.emptyPath &&
264 context.symlinks && context.usercopy;
265 passed = closeDescriptor(subsystem, AccessDescriptor) && passed;
266 passed = closeDescriptor(subsystem, DirectoryDescriptor) && passed;
270 const bool rootRestored = fixture.close();
272 FATAL(
"Hosted filesystem fixture retained owners after teardown");
279 "HOSTED-SYSCALL-TEST: FAIL faccessat2-semantics: "
280 "validation, resolution, credential, empty-path, symlink, or usercopy behavior regressed");
284 NOTICE(
"HOSTED-SYSCALL-TEST: PASS faccessat2-semantics");
289bool runHostedAccessSyscallRegressions(
Process* process) {
290 return accessSemantics(process);
Memory-mapped file interface.
static Directory * fromFile(File *pF)
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
MemoryMappedObject * mapAnon(uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms)
size_t remove(uintptr_t base, size_t length)
static MemoryMapManager & instance()
static constexpr size_t getPageSize() PURE
bool acquireFileDescriptor(size_t fd, DescriptorLease &descriptor)
bool closeFileDescriptor(size_t fd, const DescriptorLease &descriptor)
void addFileDescriptor(size_t fd, FileDescriptor *pFd)
virtual int64_t getUserId() const
static ProcessorInformation & information()
void setErrno(size_t err)
Process * getParent() const
virtual File * getRoot() const
Filesystem * getRootFilesystem() const