Context
I'm writing code that emulates one aspect of pidfds on platforms that don't support them (old Linux, other Unix).
I'm doing this a) in order to test some pidfd-related code on very old platforms and b) as a personal challenge. Mostly as a personal challenge/for fun.
I explicitly am not trying to re-implement all pidfd functionality; I don't actually care about processes//proc
/PIDs at all. Instead, I'm trying to emulate just one part of pidfds' features: the fact that a pidfd's file descriptor has three behaviours:
- If opened in blocking mode, block on read.
- If opened or
fcntl
'd to nonlocking mode, return EWOULDBLOCK on read. - Explicitly return
EINVAL
on read.
That third part is the tricky part, and an unusual aspect of pidfds at this time (very little else intentionally returns EINVAL
for read()
).
Question
I want to induce that behaviour on some other kind of file descriptor (doesn't matter what kind). Specifically, I want a file descriptor that:
- By default, obeys the usual
O_NONBLOCK
(or not) behaviour. - After I do something to it, all calls to
read(2)
that would not ordinarily return an error will instead returnEINVAL
, regardless of the parameters toread(2)
.
This turns out to be surprisingly tricky.
What I've tried
read(2)
's manual page for EINVAL
says it's returned if:
fd is attached to an object which is unsuitable for reading; or the file was opened with the O_DIRECT flag, and either the address specified in buf, the value specified in count, or the file offset is not suitably aligned.
...or if an invalid buffer size is passed to read(2)
on a timerfd.
Neither the timerfd case nor the O_DIRECT
case satisfy my requirements, as they only return EINVAL
if certain arguments are passed to read(2)
, and I want it to be returned in all non-erroring cases.
I've also tried signalfds (couldn't find a case that returned EINVAL on read), inotify FDs (same), and various permutations of forcibly close(2)
d or shutdown(2)
pipes, FIFOs, and anonymous sockets.
I'm not that well-versed in POSIX trivia, though, so it's entirely possible I missed something that allows a file descriptor type I've already experimented on to return EINVAL
.
Bonus points if there's a solution that works on BSD/MacOS, but really anything is better than nothing, even if Linux-specific or kernel-version-specific.
I've tried some of the other tricks on this question, but they largely generate error codes other than EINVAL
.
pidfd_read()
function that's normally just a wrapper overread()
instead of directly usingread()
? That way you have control over what it returns when running tests.LD_PRELOAD
hacks to shim in a new version ofread(2)
(nor can I rely on dynamic linking even if that were an option).read(2) EINVAL
also says"fd was created via a call to timerfd_create(2) and the wrong size buffer was given to read(); see timerfd_create(2) for further information."
-- have you followed up there? Also, I think @Shawn was suggesting you simply write a wrapper forread
, not dynamically load something already part of another library. That way you control under what circumstances your wrapper returnsEINVAL
given the parameters and whatever testing you wish to do to arrive at the circumstance you want to returnEINVAL
. Another good option.O_DIRECT
? Check Use of O_DIRECT on Linux for various cases and concerns.read(2)
internally, so replacingread()
with my own function is not really ideal. Thetimerfd_create(2)
andO_DIRECT
discussions are only relevant insofar as the manpage forread(2)
mentions those systems as ways to makeread()
returnEINVAL
. Unfortunately, bothtimerfd
andO_DIRECT
's inducedEINVAL
behaviors depend on the arguments toread()
, which doesn't satisfy my requirements of allread()
calls returningEINVAL
.