The Pedigree Project 0.1
mknodat-syscalls.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/process/FilesystemCredentials.h"
3#include "pedigree/kernel/process/TerminationDeferral.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/processor/Processor.h"
6#include "pedigree/kernel/processor/ProcessorInformation.h"
7#include "pedigree/kernel/syscallError.h"
8
9#include <fcntl.h>
10#include <limits.h>
11
12#include "FileDescriptor.h"
13#include "PosixProcess.h"
14#include "PosixSubsystem.h"
15#include "file-syscalls.h"
16#include "landlock.h"
17#include "metadata-syscalls.h"
18#include "modules/system/vfs/MountView.h"
19#include "modules/system/vfs/Pipe.h"
20#include "modules/system/vfs/VFS.h"
21#include <sys/stat.h>
22
23namespace {
24struct MknodResult {
25 MknodResult(int result)
26 : value(result),
27 error(result < 0 ? Processor::information().getCurrentThread()->getErrno() : 0) {}
28 int value, error;
29};
30
31MknodResult createNode(int dirfd, const char* userPath, mode_t suppliedMode) {
32 TerminationDeferral lifetime;
33 const mode_t mode = static_cast<uint16_t>(suppliedMode);
34 const mode_t type = mode & S_IFMT;
35 if (type != 0 && type != S_IFREG && type != S_IFIFO) {
36 syscallError(type == S_IFBLK || type == S_IFCHR || type == S_IFSOCK
37 ? Error::OperationNotSupported
38 : type == S_IFDIR ? Error::NotEnoughPermissions
39 : Error::InvalidArgument);
40 return -1;
41 }
42 if (mode & (S_ISUID | S_ISGID)) {
43 SYSCALL_ERROR(OperationNotSupported);
44 return -1;
45 }
46 String copied;
47 const auto copiedStatus = PosixSubsystem::copyUserString(userPath, copied, PATH_MAX);
48 if (copiedStatus != PosixSubsystem::UserStringSuccess) {
49 syscallError(copiedStatus == PosixSubsystem::UserStringBadAddress ? Error::BadAddress
50 : Error::NameTooLong);
51 return -1;
52 }
53 if (!copied.length()) {
54 SYSCALL_ERROR(DoesNotExist);
55 return -1;
56 }
57 Process* process = Processor::information().getCurrentThread()->getParent();
58 auto* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
59 auto* view = VFS::instance().mountView();
60 auto context = process->acquireFilesystemContext();
61 if (!subsystem || !view || !context) {
62 SYSCALL_ERROR(DoesNotExist);
63 return -1;
64 }
65 DescriptorLease descriptor;
66 FilesystemPathRef start, parent;
67 if (copied[0] != '/' && dirfd != AT_FDCWD) {
68 if (!subsystem->acquireFileDescriptor(dirfd, descriptor) || !descriptor->getFile()) {
69 SYSCALL_ERROR(BadFileDescriptor);
70 return -1;
71 }
72 start = descriptor->openingPath();
73 if (!descriptor->getFile()->isDirectory() || !start) {
74 SYSCALL_ERROR(NotADirectory);
75 return -1;
76 }
77 }
78 String path, basename;
79 normalisePath(path, copied.cstr());
80 if (!view->resolveParent(context, start, path, parent, basename))
81 return -1;
82 if (!basename.length() || basename == "." || basename == "..") {
83 SYSCALL_ERROR(FileExists);
84 return -1;
85 }
86 if (basename.length() > NAME_MAX) {
87 SYSCALL_ERROR(NameTooLong);
88 return -1;
89 }
90 if (copied[copied.length() - 1] == '/') {
91 Directory::ChildLease existing;
92 const auto status =
93 Directory::fromFile(parent->node())->lookupChild(HashedStringView(basename), existing);
94 syscallError(status == Directory::LookupStatus::Found ? Error::FileExists
95 : status == Directory::LookupStatus::NotFound ? Error::DoesNotExist
96 : status == Directory::LookupStatus::Retry ? Error::NoMoreProcesses
97 : Error::IoError);
98 return -1;
99 }
100 mode_t permissions = mode & 01777;
101 if (process->getType() == Process::Posix)
102 permissions &= ~static_cast<PosixProcess*>(process)->getMask();
103 if (type != S_IFIFO)
104 return view->createFile(parent, basename, permissions) ? 0 : -1;
105
106 // The retained attachment and directory admission protect this exact parent;
107 // a second pathname lookup could publish into a different mount after pivot.
108 if (!parent || parent->provider() != view || !parent->node()->isDirectory()) {
109 SYSCALL_ERROR(NotADirectory);
110 return -1;
111 }
112 Directory* directory = Directory::fromFile(parent->node());
113 if (directory->getFilesystem()->isReadOnly()) {
114 SYSCALL_ERROR(ReadOnlyFilesystem);
115 return -1;
116 }
117 if (!VFS::checkAccess(directory, false, true, true))
118 return -1;
119 FilesystemCredentials credentials;
120 if (!Process::currentFilesystemCredentials(credentials)) {
121 SYSCALL_ERROR(PermissionDenied);
122 return -1;
123 }
124 Pipe* pipe = new Pipe(basename, 0, 0, 0, 0, directory->getFilesystem(), 0, directory);
125 if (!pipe) {
126 SYSCALL_ERROR(OutOfMemory);
127 return -1;
128 }
129 // VFS owner rights occupy the low bits, unlike Unix modes.
130 const uint32_t vfsPermissions =
131 ((permissions & 0400) ? FILE_UR : 0) | ((permissions & 0200) ? FILE_UW : 0) |
132 ((permissions & 0100) ? FILE_UX : 0) | ((permissions & 0040) ? FILE_GR : 0) |
133 ((permissions & 0020) ? FILE_GW : 0) | ((permissions & 0010) ? FILE_GX : 0) |
134 ((permissions & 0004) ? FILE_OR : 0) | ((permissions & 0002) ? FILE_OW : 0) |
135 ((permissions & 0001) ? FILE_OX : 0) | ((permissions & S_ISVTX) ? FILE_STICKY : 0);
136 pipe->setPermissions(vfsPermissions);
137 pipe->setUid(credentials.uid);
138 pipe->setGid(credentials.gid);
139 syscallError(0);
140 const Directory::AddStatus added = view->createEphemeral(parent, pipe, LandlockAccess::MakeFifo);
141 if (added != Directory::AddStatus::Added) {
142 const size_t error = Processor::information().getCurrentThread()->getErrno();
143 delete pipe;
144 if (error) {
145 syscallError(error);
146 } else {
147 syscallError(added == Directory::AddStatus::IoError ? Error::IoError
148 : added == Directory::AddStatus::Detached ? Error::DoesNotExist
149 : Error::FileExists);
150 }
151 return -1;
152 }
153 return 0;
154}
155} // namespace
156
157int posix_mknodat(int dirfd, const char* path, mode_t mode, dev_t device) {
158 // Linux ignores the device argument for regular files and FIFOs.
159 (void)device;
160 const MknodResult result = createNode(dirfd, path, mode);
161 syscallError(result.error);
162 return result.value;
163}
static Directory * fromFile(File *pF)
Definition Directory.h:151
MUST_USE_RESULT LookupStatus lookupChild(const HashedStringView &s, ChildLease &child) const
Definition Directory.cc:355
virtual bool isDirectory()
Definition File.cc:804
bool isReadOnly()
Definition Filesystem.h:150
Definition Pipe.h:36
static UserStringResult copyUserString(const char *userString, String &copy, size_t maxLength)
Process * getParent()
Definition Process.h:620
static ProcessorInformation & information()
static bool checkAccess(File *pFile, bool bRead, bool bWrite, bool bExecute)
Definition VFS.cc:1502
static VFS & instance()
Definition VFS.cc:311