Kernel space write a file - c

I'm trying to write a file /proc/test/enable in kernel 5.10 C code.
I can write it in user space by calling fwrite.
When I using kernel_write in kernel C code, I will get the error kernel write not supported for file test/enable and error code 22.
struct file *filp;
char context[] = "test";
filp = filp_open("/proc/test/enable", O_WRONLY, 0);
int err = kernel_write(filp, (void *)context, sizeof(context), &filp->f_pos);
dev_info(afe->dev, "%s(), DONE, code = %d", __func__, err);
I can't understand why kernel_write can't get proc file write function, but userspace fwrite can

For support "normal" write (from userspace), a file may define .write or .write_iter operations (in its struct file_operations).
But writing from the kernel can use only .write_iter operation.
For support writing your file from the kernel, you need to define .write_iter operation for the file instead of .write one.
The failed check for your case is
/*
* Also fail if ->write_iter and ->write are both wired up as that
* implies very convoluted semantics.
*/
if (unlikely(!file->f_op->write_iter || file->f_op->write))
return warn_unsupported(file, "write");

Related

Writing to Linux device driver causes infinite loop

I have been writing a kernel space device driver that can be read from and written to from user space. The open, read, release operations all work perfectly. The problem I am having is with the user-space code that should access the device driver and and write something to it.
The user-space program writes to two files: 1) to a .txt file (and prints a to the console to let the user know it was completed), and 2) to the device driver (and also prints a text to let the user know it was also completed).
Below is the user-space code in full:
int main() {
FILE *fp;
fp = fopen("./test.txt","w");
fputs("Test\n", fp);
fclose(fp);
printf("Printed to txt\n"); //Prints normally.
fp = fopen("/dev/testchar", "w");
fputs("Test\n", fp);
fclose(fp);
printf("Printed to dev\n"); //Never gets to this point
return 0;
}
When I compile and run the code the program spits out
Printed to txt
and just hangs until ctrl+c is called. It never gets beyond the second fputs().
While monitoring kern.log I see endless calls to write to the device driver.
Here I have extracted relevant code from the device driver:
static char msg[256] = {0};
static struct file_operations fops =
{
.write = dev_write
};
static ssize_t dev_write(struct file *file, const char *buf, size_t len, loff_t *ppos)
{
sprintf(msg, "Input:%s, Chars:%lu\n", buf, len);
printk(KERN_NOTICE "%s\n", msg);
return 0;
}
uname -r: 4.10.0-38-generic
gcc -v: gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)
My question is: why is the program getting stuck in an infinite loop when writing to the device, and how do I fix it?
Thanks in advance. Any help will be greatly appreciated.
I think the kernel write operation is supposed to return the number of bytes written. You return 0. So the write system call returns to userspace with 0. However, since your userspace code is using stdio, then your userspace code tries the write again, assuming the system call simply didn't write out all the data. If you return the length of the input, then stdio will know all the data was written. Alternatively you can use the write system call directly rather than fputs. Your kernel code will still be incorrect, but your program will terminate.
You can test this using strace and see all the system calls.

Linux Kernel Module File close not quite correct

I have a small problem with this code. I can't figure out why it is not working.
static int test(const char *path)
{
struct file *filp;
filp = filp_open(path, O_RDONLY, 0);
if (IS_ERR(filp))
return filp;
// some code (only read from filp (like inode and stuff))
filp_close(filp, NULL);
}
When I use this snippet once or twice or even a thousand times it works but after approximately 63000 times I run into error -23 and after that cann't open a single file. I looked up the syscalls for open and close and these use filp_open/filp_close and I just can't figure out what is wrong with this code. It must be something with the file descriptors not being deallocated, but why?
Try this:
mm_segment_t st_old_fs;
st_old_fs = get_fs();
set_fs(get_ds());
struct file *filp;
filp = file_open(path, O_RDONLY, 0);
if (IS_ERR(filp))
{
set_fs(st_old_fs);
return filp;
}
// some code (only read from filp (like inode and stuff))
filp_close(filp, NULL);
set_fs(st_old_fs);
The kernel has some special memory manager way for the file. So you need to save the old way and restore it after use the file_open.
file objects, like plenty others, are freed only after RCU grace period finishes. I suspect your kernel is not preemptible and you don't context switch anywhere, thus the grace period never completes and the objects accumulate. You can learn more about rcu here: https://lwn.net/Articles/262464/
The real question is what are you doing. Kernel-level work is not fit for programming beginners. If this is a college assignment, it is extremely likely you are going about it wrong or the assignment itself is just bad.

C File descriptor duplication without sharing offset or flags

I need to concurrently read from a file in different offsets using C.
dup unforunately creates a file descriptor that shares offset and flags with the original.
Is there a function like dup that does not share the offset and flags?
EDIT I only have access to the file pointer FILE* fp; I do not have the file path
EDIT This program is compiled for windows in addition to mac and many flavors of linux
SOLUTION
We can use pread on posix systems, and I wrote a pread function for windows which solves this problem
https://github.com/Storj/libstorj/blob/master/src/utils.c#L227
On Linux, you can recover the filename from /proc/self/fd/N, where N is the integral value of the file descriptor:
sprintf( linkname, "/proc/self/fd/%d", fd );
Then use readlink() on the resulting link name.
If the file has been renamed or deleted, you may be out of luck.
But why do you need another file descriptor? You can use pread() and/or pwrite() on the original file descriptor to read/write from/to the file without affecting the current offset. (caveat: on Linux, pwrite() to a file opened in append mode is buggy - POSIX states that pwrite() to a file opened in append mode will write to the offset specified in the pwrite() call, but the Linux pwrite() implementation is broken and will ignore the offset and append the data to the end of the file - see the BUGS section of the Linux man page)
No, neither C nor POSIX (since you mention dup()) has a function for opening a new, independent file handle based on an existing file handle. As you observed, you can dup() a file descriptor, but the result refers to the same underlying open file description.
To get an independent handle, you need to open() or fopen() the same path (which is possible only if the FILE refers to an object accessible through the file system). If you don't know what path that is, or if there isn't any in the first place, then you'll need a different approach.
Some alternatives to consider:
buffer some or all of the file contents in memory, and read as needed from the buffer to serve your needs for independent file offsets;
build an internal equivalent of the tee command; this will probably require a second thread, and you'll probably not be able to read one file too far ahead of the other, or to seek in either one;
copy the file contents to a temp file with a known name, and open that as many times as you want;
if the FILE corresponds to a regular file, map it into memory and access its contents there. The POSIX function fmemopen() could be useful in this case to adapt the memory mapping to your existing stream-based usage.
On windows (assuming VisualStudio), you can get access to the OS file handle from the stdio FILE handle.
From there, reopen it and convert back to a new FILE handle.
This is windows only, but I think Andrews answer will work for Linux and probably the Mac as well - unfortunately there is no portable way to have it work on all systems.
#include <Windows.h>
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
FILE *jreopen(FILE* f)
{
int n = _fileno(f);
HANDLE h = (HANDLE)_get_osfhandle(n);
HANDLE h2 = ReOpenFile(h, GENERIC_READ, FILE_SHARE_READ, 0);
int n2 = _open_osfhandle((intptr_t)h2, _O_RDONLY);
FILE* g = _fdopen(n2, "r");
return g;
}
I was able to use pread and pwrite on POSIX systems, and I wrapped ReadFile/WriteFile on Windows Systems into pread and pwrite functions
#ifdef _WIN32
ssize_t pread(int fd, void *buf, size_t count, uint64_t offset)
{
long unsigned int read_bytes = 0;
OVERLAPPED overlapped;
memset(&overlapped, 0, sizeof(OVERLAPPED));
overlapped.OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32);
overlapped.Offset = (uint32_t)(offset & 0xFFFFFFFFLL);
HANDLE file = (HANDLE)_get_osfhandle(fd);
SetLastError(0);
bool RF = ReadFile(file, buf, count, &read_bytes, &overlapped);
// For some reason it errors when it hits end of file so we don't want to check that
if ((RF == 0) && GetLastError() != ERROR_HANDLE_EOF) {
errno = GetLastError();
// printf ("Error reading file : %d\n", GetLastError());
return -1;
}
return read_bytes;
}
ssize_t pwrite(int fd, const void *buf, size_t count, uint64_t offset)
{
long unsigned int written_bytes = 0;
OVERLAPPED overlapped;
memset(&overlapped, 0, sizeof(OVERLAPPED));
overlapped.OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32);
overlapped.Offset = (uint32_t)(offset & 0xFFFFFFFFLL);
HANDLE file = (HANDLE)_get_osfhandle(fd);
SetLastError(0);
bool RF = WriteFile(file, buf, count, &written_bytes, &overlapped);
if ((RF == 0)) {
errno = GetLastError();
// printf ("Error reading file :%d\n", GetLastError());
return -1;
}
return written_bytes;
}
#endif

Create GNU C File Descriptor Without File Handle

If I want to use a physical file along with other types of streams such as a socket, I can simply convert a file handle into a file descriptor:
#include <stdlib.h>
#include <stdio.h>
int main(void) {
FILE *f = fopen("uniquefilename.ext", "w");
int fd = fileno(f);
printf("%d\n", fd);
fclose(f);
return 0;
}
Does the GNU Standard Library provide a way to obtain a physical file's descriptor directly? Something to the effect of:
int fd = some_call("file_name.ext", "mode");
It seems I need to note I am completely aware of how a descriptor is not implicitly bound to any specific file. I was misleading when I wrote "obtain a physical file's descriptor"; what I should have wrote is something like "create a descriptor enabling access to a specific physical file".
It does not.
However, you can use the open function directly! This is part of Linux itself, not the C standard library (technically the C standard library provides a small wrapper to allow you to call it as a C function).
Example usage:
int fd = open("file_name.ext", O_RDWR); // not fopen
// do stuff with fd
close(fd); // not fclose
Note: The man page recommends including <sys/types.h>, <sys/stat.h>, and <fcntl.h>, and for close you need <unistd.h>. That's quite a few headers, and I don't know if they're all necessary.

stat(), fstat(), lstat(), and fopen(); how to write TOCTOU protected system independent code

I've been dealing with a problem for a few weeks now updating 20 year code that needs to be system independent (work on both Linux and Windows). It involves Time-of-Check, Time-of-Use (TOCTOU) issues. I made a thread here, but it didn't go very far, and after ruminating on it for a while and searching deeper into the problem, I think I understand my question a bit better. Maybe I can ask it a bit better too...
From what I've read, the code needs to check if the file exists, if it is accessible, open the file, do some operations and finally close the file. It seems the best way to do this is a call to lstat(), a call to fopen(), a call to fstat() (to rule out the TOCTOU), and then the operations and closing the file.
However, I've been lead to believe that lstat() and fstat() are POSIX defined, not C Standard defined, ruling out their use for a system agnostic program, much in the same way open() shouldn't be used for cross-compatibility. How would you implement this?
If you look at my first post, you can see the developer from 20 years ago used the C preprocessor to cut the code into cross-compatible parts, but even if I did that, I wouldn't know what to replace lstat() or fstat() with (their windows counterparts).
Edit: Added abreviated code to this post; if something is unclear please go to the original post
#ifdef WIN32
struct _stat buf;
#else
struct stat buf;
#endif //WIN32
FILE *fp;
char data[2560];
// Make sure file exists and is readable
#ifdef WIN32
if (_access(file.c_str(), R_OK) == -1) {
#else
if (access(file.c_str(), R_OK) == -1) {
#endif //WIN32
char message[2560];
sprintf(message, "File '%s' Not Found or Not Readable", file.c_str());
throw message;
}
// Get the file status information
#ifdef WIN32
if (_stat(file.c_str(), &buf) != 0) {
#else
if (stat(file.c_str(), &buf) != 0) {
#endif //WIN32
char message[2560];
sprintf(message, "File '%s' No Status Available", file.c_str());
throw message;
}
// Open the file for reading
fp = fopen(file.c_str(), "r");
if (fp == NULL) {
char message[2560];
sprintf(message, "File '%s' Cound Not be Opened", file.c_str());
throw message;
}
// Read the file
MvString s, ss;
while (fgets(data, sizeof(data), fp) != (char *)0) {
s = data;
s.trimBoth();
if (s.compare( 0, 5, "GROUP" ) == 0) {
//size_t t = s.find_last_of( ":" );
size_t t = s.find( ":" );
if (t != string::npos) {
ss = s.substr( t+1 ).c_str();
ss.trimBoth();
ss = ss.substr( 1, ss.length() - 3 ).c_str();
group_list.push_back( ss );
}
}
}
// Close the file
fclose(fp);
}
The reliable way to check whether the file exists and can be opened is to try opening it. If it was opened, all was OK. If it was not opened, you can think about spending time to analyze what went wrong.
The access() function formally asks a different question from what you think; it asks 'can the real user ID or the real group ID access the file', but the program will use the effective user ID or the effective group ID to access the file. If your program is not running SUID or SGID, and was not launched from a program running SUID or SGID — and that's the normal case — then there's no difference. But the question is different.
The use of stat() or
lstat() doesn't seem helpful. In particular, lstat() only tells you whether you start at a symlink, but the code doesn't care about that.
Both the access() and the stat() calls provide you with TOCTOU windows of vulnerability; the file could be removed after they reported it was present, or created after they reported it was absent.
You should simply call fopen() and see whether it works; the code will be simpler and more resistant to TOCTOU problems. You might need to consider whether to use open() with all its extra controls (O_EXCL, etc), and then convert the file descriptor to a file pointer (fdopen()).
All of this applies to the Unix side.
The details will be different, but on the Windows side, you will still be best off trying to open the file and reacting appropriately to failure.
In both systems, make sure the options provided to the open function are appropriate.

Resources