just found Documentation/filesystems/seq_file.rst
in the kernel and this seems to do everything i want. was having difficulty finding info about how to do a virtual proc file but it's probably because everything is just using seq_file
just found Documentation/filesystems/seq_file.rst
in the kernel and this seems to do everything i want. was having difficulty finding info about how to do a virtual proc file but it's probably because everything is just using seq_file
i had actually seen it before because it was also the one place which mentioned the deprecated API create_proc_entry()
but i didn't realize i was looking at the procfile documentation in general lol
this is just useful to know about how the fs layer works in general
A seq_file implementation that is formatting firewall rules from a
table, for example, could provide a simple iterator that interprets
position N as the Nth rule in the chain. A seq_file implementation
that presents the content of a, potentially volatile, linked list
might record a pointer into that list, providing that can be done
without risk of the current location being removed.Positioning can thus be done in whatever way makes the most sense for
the generator of the data, which need not be aware of how a position
translates to an offset in the virtual file. The one obvious exception
is that a position of zero should indicate the beginning of the file.
i know that directory streams do not use an integer offset but more like an opaque "cookie" referencing a hash table entry these days
there's so much cool stuff in /proc/self/
lol
this documentation is so good lol. being honest about historical issues is so refreshing
The next() function should set
*pos
to a value that start() can use
to find the new location in the sequence. When the iterator is being
stored in the private data area, rather than being reinitialized on each
start(), it might seem sufficient to simply set*pos
to any non-zero
value (zero always tells start() to restart the sequence). This is not
sufficient due to historical problems.Historically, many next() functions have not updated
*pos
at
end-of-file. If the value is then used by start() to initialise the
iterator, this can result in corner cases where the last entry in the
sequence is reported twice in the file. In order to discourage this bug
from being resurrected, the core seq_file code now produces a warning if
a next() function does not change the value of*pos
. Consequently a
next() function must change the value of*pos
, and of course must
set it to a non-zero value.
literally it's good
It's worth noting that the iterator value returned by start() and
manipulated by the other functions is considered to be completely opaque by
the seq_file code. It can thus be anything that is useful in stepping
through the data to be output. Counters can be useful, but it could also be
a direct pointer into an array or linked list. Anything goes, as long as
the programmer is aware that things can happen between calls to the
iterator function. However, the seq_file code (by design) will not sleep
between the calls to start() and stop(), so holding a lock during that time
is a reasonable thing to do. The seq_file code will also avoid taking any
other locks while the iterator is active.
kinda becoming more sympathetic to the idea that you need to read documentation to use kernel stuff correctly. usually you can't expect documentation to be good
nice i'm gonna use these functions for this module for sure
There are also a pair of functions for printing filenames::
int seq_path(struct seq_file *m, const struct path *path,
const char *esc);
int seq_path_root(struct seq_file *m, const struct path *path,
const struct path *root, const char *esc)
A true return from seq_has_overflowed means that the seq_file buffer will
be discarded and the seq_show function will attempt to allocate a larger
buffer and retry printing.
oh love this so much too Documentation/filesystems/api-summary.rst
specifically links to source code as well as other documentation elevating source code as a form of documentation
like usually when people tell you shit like "my code is self-documenting" they don't specifically point to which code is the documentation you should be looking at bc it's obviously a dodge this instead takes ownership of that
kind of interesting that procfs seems to be entirely about producing text output for humans to consider as opposed to binary communication formats. i suppose if you wanted to do binary communication with the kernel you would use a syscall for that
love this shit
* %__GFP_RETRY_MAYFAIL
* Try really hard to succeed the allocation but fail
* eventually.
Documentation/filesystems/locking.rst
has some fantastic description of when a lock is held->llseek() locking has moved from llseek to the individual llseek
implementations. If your fs is not using generic_file_llseek, you
need to acquire and release the appropriate locks in your ->llseek().
For many filesystems, it is probably safe to acquire the inode
mutex or just to use i_size_read() instead.
Note: this does not protect the file->f_pos against concurrent modifications
since this is something the userspace has to take care about.
always wondered how copy_file_range() handled concurrent modifications
->copy_file_range and ->remap_file_range implementations need to serialize
against modifications of file data while the operation is running. For
blocking changes through write(2) and similar operations inode->i_rwsem can be
used. To block changes to file contents via a memory mapping during the
operation, the filesystem must take mapping->invalidate_lock to coordinate
with ->page_mkwrite.
oh jesus fucking christ Documentation/filesystems/directory-locking.rst
answers a whole lot of questions
i think i'm just going to take a single big mutex on the whole procfile for now and figure out if/how i want to allow multithreaded access later
lockdep seems really cool. mutex correctness validation
* We split the mutex lock/unlock logic into separate fastpath and
* slowpath functions, to reduce the register pressure on the fastpath.
* We also put the fastpath first in the kernel image, to make sure the
* branch is predicted by the CPU as default-untaken.
i might not need this lock. but it will give me peace of mind
omg wait can i use C defer? probably not i bet the kernel needs time for that
OMG seq_file already has a lock. maybe i just use that? but my data has different lifetimes than the seq_file data structure so nvm
one interesting thing about not having non-local returns like exceptions mean you have all control flow explicit. i think RAII is the right way to answer this and is strictly more powerful than what C gives you with neither non-local returns or RAII but it's kind of nice to read code that won't return early unless you tell it to with a highlighted return statement
btw i made a mistake earlier by misinterpreting a void *
as the wrong type of pointer and surprisingly it did not crash my system it just caused an infinite loop which again somehow did not crash my system. this is obviously where microkernels shine
god now i wanna look at hurd lmao lmao lmao
also i'm using raku to test my shit bc i need something with a persistent file handle and i don't wanna use python and raku has always seemed so cool to me so i'm finally learning it 😁 love it
hilarious that we still use null-terminated strings in places lmao
just learned about the guard()
and free()
macros in include/linux/cleanup.h
. RAII is awesome
huh. apparently C does have function-level optimization directives https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-optimize-function-attribute compiler-specific obv
oh hell yes there's a glob_match()
implementation in include/linux/glob.h
i might be using this for hijinx
looks like i will have to implement it myself to get gitignore semantics. basically i was wondering if my procfile module could support being written to add new globs to add at a certain directory level. i think it could
would be a ridiculously overengineered sort of thing to do in general compared to what the rust ignore crate does which is use an efficient regex engine in userspace but the idea here would be to establish a general RPC-like interface for directory traversal none of this module is gonna be shipped anywhere i think it's just fun
i'm finally getting into translating user-visible paths (i.e. from a particular process) to the kernel and vice versa and it turns out Linux Can Do That https://www.kernel.org/doc/html/latest/filesystems/path-lookup.html
Most races are much more subtle, and a big part of the task of
pathname lookup is to prevent them from having damaging effects.
YOU DON'T SAY
honestly it might be better implementing this as a set of syscalls........the procfs abstraction is getting way too strained. stuff like user_path_at()
expects a const char __user *name
i.e. it needs to have the whole pointer in a single go and writes may be arbitrarily split. this also makes locking much easier. now i'll have to learn how to make a syscall but i think that's supposed to be not too hard
linux tells me to try using ioctl(2)
for this https://docs.kernel.org/process/adding-syscalls.html
oh man and it looks like .proc_ioctl
is VERY infrequently used. i bet it's gonna break on me lmao
ok so it does look like ioctl can send and receive arbitrary pointers so i can definitely do that here