0x90
0x90

Reputation: 40982

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;
}

Upvotes: 1

Views: 4727

Answers (3)

silentnights
silentnights

Reputation: 921

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:

Upvotes: 1

0x90
0x90

Reputation: 40982

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;
}

Upvotes: 3

parucra
parucra

Reputation: 51

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.

Upvotes: 1

Related Questions