The Pedigree Project 0.1
advisory-lock-syscalls.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "advisory-lock-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"
9
10#include <fcntl.h>
11#include <stddef.h>
12
13#include "FileDescriptor.h"
14#include "PosixSubsystem.h"
15#include "advisory-lock-state.h"
16#include "modules/system/vfs/File.h"
17#include <sys/file.h>
18
19using namespace PosixAdvisory;
20
21namespace {
22struct LinuxFlock {
23 int16_t type;
24 int16_t whence;
25 uint32_t padding;
26 int64_t start;
27 int64_t length;
28 int32_t pid;
29 uint32_t tailPadding;
30};
31static_assert(sizeof(LinuxFlock) == 32, "Linux amd64 flock size");
32static_assert(offsetof(LinuxFlock, type) == 0 && offsetof(LinuxFlock, whence) == 2 &&
33 offsetof(LinuxFlock, start) == 8 && offsetof(LinuxFlock, length) == 16 &&
34 offsetof(LinuxFlock, pid) == 24,
35 "Linux amd64 flock offsets");
36
37bool regularFile(File* file) {
38 if (!file || !file->supportsRegularFileOperations()) {
39 SYSCALL_ERROR(OperationNotSupported);
40 return false;
41 }
42 return true;
43}
44
45struct Admission {
46 PosixSubsystem& subsystem;
47 size_t fd;
49};
50
51bool admit(void* context) {
52 const auto& admission = *static_cast<Admission*>(context);
53 return admission.subsystem.descriptorMatchesOpenDescription(admission.fd, admission.description);
54}
55} // namespace
56
57int posix_advisory_flock(const DescriptorLease& descriptor, int operation) {
58 TerminationDeferral lifetime;
59 const int lockType = operation & ~LOCK_NB;
60 if (lockType != LOCK_SH && lockType != LOCK_EX && lockType != LOCK_UN) {
61 SYSCALL_ERROR(InvalidArgument);
62 return -1;
63 }
64 if (!descriptor) {
65 SYSCALL_ERROR(BadFileDescriptor);
66 return -1;
67 }
68 auto description = descriptor->acquireOpenFileDescription();
69 File* file = description->getFile();
70 if (!regularFile(file))
71 return -1;
72 if (descriptor->getStatusFlags() & O_PATH) {
73 SYSCALL_ERROR(BadFileDescriptor);
74 return -1;
75 }
76 Grant request;
77 // Ext2 aliases already share this backing token; paths and File pointers do not.
78 request.inode = file->futexIdentity();
79 request.name = Namespace::Flock;
80 request.type =
81 lockType == LOCK_UN ? Type::Unlock : (lockType == LOCK_SH ? Type::Read : Type::Write);
82 return posix_advisory_apply(description->advisoryOwner(), request, !(operation & LOCK_NB));
83}
84
85int posix_advisory_fcntl(PosixSubsystem& subsystem, int fd, const DescriptorLease& descriptor,
86 int command, void* userLock) {
87 TerminationDeferral lifetime;
88 const bool ofd = command == F_OFD_GETLK || command == F_OFD_SETLK || command == F_OFD_SETLKW;
89 const bool query = command == F_GETLK || command == F_OFD_GETLK;
90 const bool wait = command == F_SETLKW || command == F_OFD_SETLKW;
91 if (!ofd && command != F_GETLK && command != F_SETLK && command != F_SETLKW) {
92 SYSCALL_ERROR(InvalidArgument);
93 return -1;
94 }
95 if (!descriptor) {
96 SYSCALL_ERROR(BadFileDescriptor);
97 return -1;
98 }
99 auto description = descriptor->acquireOpenFileDescription();
100 File* file = description->getFile();
101 if (!regularFile(file))
102 return -1;
103 const int flags = descriptor->getStatusFlags();
104 if (flags & O_PATH) {
105 SYSCALL_ERROR(BadFileDescriptor);
106 return -1;
107 }
108 LinuxFlock wire;
109 if (!PosixSubsystem::copyFromUser(&wire, userLock, sizeof(wire))) {
110 SYSCALL_ERROR(BadAddress);
111 return -1;
112 }
113 if ((wire.type != F_RDLCK && wire.type != F_WRLCK && wire.type != F_UNLCK) ||
114 (command == F_GETLK && wire.type == F_UNLCK) || (ofd && wire.pid)) {
115 SYSCALL_ERROR(InvalidArgument);
116 return -1;
117 }
118 Grant request;
119 request.inode = file->futexIdentity();
120 request.type =
121 wire.type == F_UNLCK ? Type::Unlock : (wire.type == F_RDLCK ? Type::Read : Type::Write);
122 request.pid = static_cast<int32_t>(
123 Processor::information().getCurrentThread()->getParent()->getUserspaceId());
124 uint64_t position = 0, fileSize = 0;
125 if (wire.whence == 1) {
126 auto positionGuard = descriptor->lockPosition();
127 position = positionGuard.offset();
128 } else if (wire.whence == 2) {
129 auto sizeGuard = file->lockWrites();
130 fileSize = file->getSize();
131 }
132 const RangeResult range =
133 normalise(wire.whence, wire.start, wire.length, position, fileSize, request.range);
134 if (range != RangeResult::Success) {
135 syscallError(range == RangeResult::Overflow ? Error::ValueTooLarge : Error::InvalidArgument);
136 return -1;
137 }
138 AdvisoryOwner& owner = ofd ? description->advisoryOwner() : subsystem.advisoryOwner();
139 if (query) {
140 Grant conflict;
141 bool found = false;
142 if (posix_advisory_query(owner, request, conflict, found) < 0)
143 return -1;
144 wire.type = F_UNLCK;
145 if (found) {
146 wire.type = conflict.type == Type::Read ? F_RDLCK : F_WRLCK;
147 wire.whence = 0;
148 wire.start = conflict.range.first;
149 wire.length =
150 conflict.range.last == LastOffset ? 0 : conflict.range.last - conflict.range.first + 1;
151 wire.pid = conflict.pid;
152 }
153 if (!PosixSubsystem::copyToUser(userLock, &wire, sizeof(wire))) {
154 SYSCALL_ERROR(BadAddress);
155 return -1;
156 }
157 return 0;
158 }
159 if ((request.type == Type::Read && (flags & O_ACCMODE) == O_WRONLY) ||
160 (request.type == Type::Write && (flags & O_ACCMODE) == O_RDONLY)) {
161 SYSCALL_ERROR(BadFileDescriptor);
162 return -1;
163 }
164 Admission admission{subsystem, static_cast<size_t>(fd), description};
165 return posix_advisory_apply(owner, request, wait,
166 !ofd && request.type != Type::Unlock ? admit : nullptr, &admission);
167}
OpenFileDescriptionLease acquireOpenFileDescription() const
PositionGuard lockPosition() const
int getStatusFlags() const
Get current status flags.
Definition File.h:75
WriteGuard lockWrites()
Definition File.cc:420
bool supportsRegularFileOperations()
Definition File.cc:824
virtual uintptr_t futexIdentity()
Definition File.cc:75
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static ProcessorInformation & information()