Argument invalid when using cat to read a character device driver - c

When I try to read a char device using:
cat /dev/fifodev
I receive the next message from the terminal
cat: /dev/fifodev: Invalid argument.
I have created the file and gaven it the permissions like this
sudo mknod /dev/fifodev c 251 0
sudo chmod 666 /dev/fifodev
The code of my driver is:
/*
* Called when a process, which already opened the dev file, attempts to
* read from it.
*/
static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */
char *buffer, /* buffer to fill with data */
size_t length, /* length of the buffer */
loff_t * offset)
{
char aux[BUF_LEN];
printk(KERN_ALERT "Entering into device_read");
if (size_cbuffer_t(buf)<length){
return -EINVAL;
}
remove_items_cbuffer_t (buf,aux, length);
copy_to_user(buffer, aux, length);
printk(KERN_ALERT "Getting out from device_read");
return length;
}
What problem do I have here? Why can't i use cat with the /dev/fifodev file?

Per your comment, the issue you're running into appears to be that your application is requesting to read data into a buffer that is larger than you have data to fill.
You will need to calculate the appropriate amount of data to copy (e.g, the smaller value of length and size_cbuffer_t(buf)), and use that value in the place of length.

in the function prototype buffer should be mentioned as __user to specify it as userspace pointer.
Return of the read method is length of the string read. This functions keeps getting recalled until it returns zero.
I think following code will work.
static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */
char __user *buffer, /* buffer to fill with data */
size_t length, /* length of the buffer */
loff_t * offset)
{
char aux[BUF_LEN];
int byte_to_read,maxbyte;
printk(KERN_ALERT "Entering into device_read");
/*
if (size_cbuffer_t(buf)<length){
return -EINVAL;
}
*/
maxbyte=strlen(buf) - *offset; //considering buf is the pointer where you have data to copy to buffer(userspace)
byte_to_read=maxbyte>length?length:maxbyte;
if(byte_to_read==0)
{
printk(KERN_ALERT "Allready Read\n");
return 0;
}
aux=buf;//as in your code AUX doesn't have anything. i'm supposing you want to copy data to this from buf and then use copy_to_user
remove_items_cbuffer_t (buf,aux, length); //i have no idea why you have used this but i'm sure this wont create any problem
copy_to_user(buffer, aux, length); //this will copy your data to userspace
printk(KERN_ALERT "Getting out from device_read");
return length;
}

Related

Is it possible to allow to use linux kernel module only for one process ?

I have linux kernel module, which get some info from user space application via device file and return some other data.
int *g_GlobalVariable;
static ssize_t dev_write(struct file* file, const char __user* buffer, size_t count, loff_t* pos)
{
int i;
int some_size = 10000;
char dev_buffer[64];
copy_from_user(dev_buffer, buffer, count);
g_GlobalVariable = kmalloc(kmalloc(some_size * sizeof(int), GFP_KERNEL);
for(i = 0; i < some_size; i++)
{
g_GlobalVariable[i] = 45; //any info
}
}
when user space application reads from device file, driver executes function dev_read.
static ssize_t dev_read(struct file* filep, char* buffer, size_t len, loff_t* offset)
{
//do something with g_GlobalVariable
}
I was thinking, that it is possible situation, that two or more processes use this driver and bad things can happen, for example:
1) first process pass data via device file and function dev_write generated and filled array g_GlobalVariable
2) first process read from device file, dev_read invoked and it works with g_GlobalVariable
3) second process writes into the same device file and g_GlobalVariable contains after that other information.
4) first process in dev_read get the wrong data, or read memory, which already doesn't exist.
How can I fix this situation ?

How to send a size_t variable with a TCP socket in C?

I'm working on something that sends data to a TCP server, but first it is supposed to send the size of the data in 8 bytes.
That is, the server will read the first 8 bytes sent to it and cast them back into a size_t variable. My problem is, when there is a file size that doesn't use any of the top bits (i.e. 83 = 0000000S <- char's, not hex), it only sends the non-zero bytes.
This is how I do it:
void send_file_to_server(char *filename){
struct stat buf;
if (stat(filename, &buf)==-1){ exit(1); }
size_t file_size = buf.st_size;
char *filesize_string = calloc(1, 8);
filesize_string = (char*)&file_size;
//this function actually writes to the server
write_to_server((char*) filesize_string);
// will be code later on that sends the actual file using write_to_server()
}
The char* passed into my write_to_server() function has some weird behavior: it only recognizes it as a string of size 6, and it gets distorted from before it was passed in. Any advice on how to make this work is appreciated.
Note: I do not have to worry about endianness (htonl, etc.) or a differing size of size_t since this is for a project that will only ever be run on a specific VM.
Edits:
here is the other function:
void write_to_server(char *message){
ssize_t bytes_sent = 0;
ssize_t message_size = strlen(message);
while ( bytes_sent < message_size ){
ssize_t ret = write(server_socket, message+bytes_sent, message_size-bytes_sent);
if (ret==0){
print_connection_closed();
exit(1);
}
if (ret==-1 && (errno!=EINTR || errno!=EAGAIN)){
printf("write failed: sent %zd bytes out of %zd\n", bytes_sent, message_size);
exit(1);
}
if (ret!=-1){ bytes_sent+=ret; }
}
}
You can't use strlen() to determine the length of binary data. It'll miscount the data as soon as it sees a zero (NUL) byte in the binary encoding of the length field.
Write a more "primitive" function that takes the address of the data and its length as parameters, e.g.
void write_to_server_raw(const void *message, size_t message_size) {
...
}
If you still need the ability to send NUL terminated strings you can then rewrite your existing write_to_server() function so that it calls the new function to do the real work.
void write_to_server_string(const char *message) {
size_t message_size = strlen(message);
write_to_server_raw(message, message_size);
}

How to copy_to_user a string and use offp in a Linux Kernel read function

Declared:
static char status[128] = "off\0";
and implemented a read function:
static ssize_t read_proc(struct file *filep, char __user *buf,
size_t len, loff_t *offp)
{
ssize_t cnt = strlen(status), ret;
ret = copy_to_user(buf, status, cnt);
*offp += cnt;
return cnt;
}
How do I take into account the offp?
currently it prints endless the status to screen
Thanks the guys comments here I came up with the following implementation, which I believe is the right way to use offp:
static ssize_t read_proc(struct file *filep, char __user *buf,
size_t len, loff_t *offp)
{
ssize_t cnt = strlen(status), ret;
/* ret contains the amount of chare wasn't successfully written to `buf` */
ret = copy_to_user(buf, status, cnt);
*offp += cnt - ret;
/* Making sure there are no left bytes of data to send user */
if (*offp > cnt)
return 0;
else
return cnt;
}
your read_proc() will not stop reading until the function returns "0" or error. I believe you need to modify your read_proc to have this logic.
To understand the return value from read let me quote from Linux Device Drivers 3rd edition:
The return value for read is interpreted by the calling application program:
If the value equals the count argument passed to the read system call, the requested number of bytes has been transferred. This is the optimal case.
If the value is positive, but smaller than count, only part of the data has been transferred. This may happen for a number of reasons, depending on the device. Most often, the application program retries the read. For instance, if you read using the fread function, the library function reissues the system call until completion of the requested data transfer.
If the value is 0, end-of-file was reached (and no data was read).
A negative value means there was an error. The value specifies what the error was, according to <linux/errno.h>. Typical values returned on error include -EINTR (interrupted system call) or -EFAULT (bad address).
So in short You need to always update offp and check on it's value before you start reading, and return 0 if it already passed your data length, and in case of partial reads return the number of bytes read, you will update the offp in this case so the next call to read will result to returning 0 if you passed the length of data.
static ssize_t read_proc(struct file *filep, char __user *buf,
size_t len, loff_t *offp)
{
size_t count = len, status_length = strlen(status);
ssize_t retval = 0;
unsigned long ret = 0;
if (*offp >= status_length)
goto out;
if (*offp + len > status_length)
count = status_length - *offp;
/* ret contains the amount of chars wasn't successfully written to `buf` */
ret = copy_to_user(buf, status, count);
*offp += count - ret;
retval = count - ret;
out:
return retval;
}
References:
Linux Device Drivers 3rd edition - Chapter 3

How to read evbuffer and put it into a string (char*) in libevent

I am using libevent and its http API to write a simple HTTP server capable of writing C servlets. This servlet is working ok with GET but now I am sending some data with POST and I would like to read the incoming event buffer evb. I would like to print/inspect the data present in evb but I cannot. Do you know how I can put the data in evb (evbuffer) in char* variable? I saw only methods to manipulate buffers but not reading it. I tried:
evb->
This is the code:
#include <stdio.h>
#include "servlet.h"
void servlet(struct evhttp_request *req, struct evbuffer *evb) {
time_t now;
time(&now);
evbuffer_add_printf(evb, "<html>\n <head>\n"
" <title>%s</title>\n"
" </head>\n"
" <body>\n"
" <h1>%s</h1>\n"
" <p>Current time is: %s</p>",
"C servlet engine", "C servlet", ctime(&now));
evhttp_add_header(evhttp_request_get_output_headers(req),
"Content-Type", "text/html");
evhttp_send_reply(req, 200, "OK", evb);
}
I am trying this
void servlet(struct evhttp_request *req, struct evbuffer *evb) {
size_t len = evbuffer_get_length(evhttp_request_get_input_buffer(req));
struct evbuffer *in_evb = evhttp_request_get_input_buffer(req);
char *data;
evbuffer_copyout(in_evb, data, len);
but I get Bus error: 10 (I am on a mac)
Sounds like you want to use
ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data, size_t datalen)
It copies the buffer contents (at most datalen bytes) to the memory area data.
It's documented in the libevent book
here's a method that works:
static void echo_read_cb(struct bufferevent *bev, void *ctx)
{
/* This callback is invoked when there is data to read on bev. */
struct evbuffer *input = bufferevent_get_input(bev);
struct evbuffer *output = bufferevent_get_output(bev);
size_t len = evbuffer_get_length(input);
char *data;
data = malloc(len);
evbuffer_copyout(input, data, len);
printf("we got some data: %s\n", data);
/* Copy all the data from the input buffer to the output buffer. */
evbuffer_add_buffer(output, input);
free(data);
}
According the source code in buffer.h of libevent, we should use
int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen);
instead of
ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen);
Here is the code borrowed from buffer.h
/**
Read data from an evbuffer and drain the bytes read.
If more bytes are requested than are available in the evbuffer, we
only extract as many bytes as were available.
#param buf the evbuffer to be read from
#param data the destination buffer to store the result
#param datlen the maximum size of the destination buffer
#return the number of bytes read, or -1 if we can't drain the buffer.
*/
int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen);
/**
Read data from an evbuffer, and leave the buffer unchanged.
If more bytes are requested than are available in the evbuffer, we
only extract as many bytes as were available.
#param buf the evbuffer to be read from
#param data_out the destination buffer to store the result
#param datlen the maximum size of the destination buffer
#return the number of bytes read, or -1 if we can't drain the buffer.
*/
ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen);

linux virtual file as device driver

I write a linux char device driver to simulate a file. The data is stored in an array and I want to implement a "read-file"-handler...
static ssize_t data_read(struct file *f, char __user *buf, size_t count, loff_t *f_pos){
char *msg_pointer;
int bytes_read = 0;
if(vault.storage==NULL)
return -EFAULT;
msg_pointer = vault.storage + *f_pos;
while (count && (*f_pos < vault.size) ) {
put_user(*(msg_pointer++), buf++);
count--;
bytes_read++;
++*f_pos;
}
return bytes_read;
}
vault.storage is a pointer to a kmalloc-creation. If I test the code by copying with dd it works as expected, but when I want to open the file with C
if((fp_data = open("/dev/vault0", O_RDWR)) < 0){
perror("could not open file.\n");
}
err = write(fp_data, "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", 36);
if (err < 0){
perror("failed to write to sv \n");
}
read(fp_data, buffer, 36);
read(fp_data, buffer, 36);
the first read-command returns 4.. the second 0 - how is this possible?
write performed on a file is not guaranteed to write all the bytes requested atomically ... that is only reserved for a pipe or FIFO when the requested write-amount is less than PIPE_BUF in size. For instance, write can be interrupted by a signal after writing some bytes, and there will be other instances where write will not output the full number of requested bytes before returning. Therefore you should be testing the number of bytes written before reading back any information into a buffer to make sure you are attempting to read-back the same number of bytes written.
Put a printk in the data_read call and print the count and print what is returned to the user(check the value of bytes_read). The bytes_read is returned to the read() call in the use space. Make sure you are returning correct value. And you can also print the fpos and check what is happening.
Here I assume that your drivers read and write functions are called properly, I mean major and minor numbers of your device file belongs to your driver

Resources