1

I'm running some experiments with seq_files and have some confusion regarding it.

I analyzed implementation of common functions from seq_file.c and judging by seq_printf implementation the internal char *buf of the struct seq_file is used entirely to store a formatted string to copy to a user in seq_read. But there is seq_write function defined in in seq_file.c which can write to the buffer.

QUESTION: Is it possible to reuse the struct seq_file's internal buffer and use it for writing data coming from user or it is for data formatting only?

I currently used another buffer for writing data and struct seq_file for data formatting only:

static char buf[4096];    
static char *limit = buf;

void *pfsw_seq_start(struct seq_file *m, loff_t *pos){
    if(*pos >= limit - buf) {
        return NULL;
    }
    char *data = buf + *pos;
    *pos = limit - buf;
    return data;
}

void pfsw_seq_stop(struct seq_file *m, void *v){ }

void *pfsw_seq_next(struct seq_file *m, void *v, loff_t *pos){ return NULL; }

int pfsw_seq_show(struct seq_file *m, void *v){
    seq_printf(m, "Data: %s\n", (char *) v);
    return 0;
}

ssize_t pfsw_seq_write(struct file *filp, const char __user * user_data, size_t sz, loff_t *off){
    if(*off < 0 || *off > sizeof buf){
        return -EINVAL;
    }
    size_t space_left_from_off = sizeof buf - (size_t) *off;
    size_t bytes_to_write = space_left_from_off <= sz ? space_left_from_off : sz;
    if(copy_from_user(buf + *off, user_data, bytes_to_write)){
        return -EAGAIN;
    }
    *off += bytes_to_write;
    if(*off > limit - buf){
        limit = buf + *off;
    }
    return bytes_to_write;
}

So I defined struct file_operations as

static const struct seq_operations seq_ops = {
    .start = pfsw_seq_start,
    .stop  = pfsw_seq_stop,
    .next  = pfsw_seq_next,
    .show  = pfsw_seq_show
};

int pfsw_seq_open(struct inode *ino, struct file *filp){ 
    return seq_open(filp, &seq_ops);
}

static const struct file_operations fops = {
    .open = pfsw_seq_open,
    .read = seq_read,
    .write = pfsw_seq_write,
    .release = seq_release,
};
St.Antario
  • 26,175
  • 41
  • 130
  • 318
  • 2
    I suppose you can if you need a temporary buffer, but why can't you just copy directly to where the data needs to go? – stark Jan 01 '20 at 17:30
  • @stark For example In case if some large object is assembled from small chunks written with separate `write`s. Then it goes to some other place. – St.Antario Jan 01 '20 at 17:36
  • @0andriy The thing that I actually confused by is if it is even common to use `procfs` for writing like this. Should modules developed for newer kernels switch to `sysfs`? – St.Antario Jan 02 '20 at 04:05
  • I'm sorry, I didn't read the question well. Why do you need seq_file be used for that in the first place? – 0andriy Jan 02 '20 at 08:42
  • @0andriy Hmm... So what's the alternative? – St.Antario Jan 02 '20 at 16:13

1 Answers1

2

The helper you are looking for is simple_write_to_buffer.

Given a pointer to the buffer and its available size, the helper processes all parameters passed to the .write function:

ssize_t pfsw_write(struct file *filp, const char __user * user_data, size_t sz, loff_t *off)
{
    // This performs the most work.
    ssize_t res = simple_write_to_buffer(buf, sizeof(buf), off, user_data, sz);

    // Optionally, you may update the buffer's actual size.
    // NOTE: In the simple form this doesn't work with **concurrent** writing.

    if ((res > 0) && *off > buf_size) {
      buf_size = *off;
    }

    return res;
}

The "pair" for simple_write_to_buffer helper is simple_read_from_buffer. That helper can be used in the implementation of the .read method:

ssize_t pfsw_read(struct file filp*, char __user * user_data, size_t sz, loff_t *off)
{
    // Here the helper does the whole work.
    //
    // 'buf_size' could be 'sizeof(buf)' in case of the buffer of the fixed size.
    // Alternatively, you need to take care about concurrency;
    // see the comments in the 'pfsw_write' function.
    return simple_read_from_buffer(buf, buf_size, off, user_data, sz);
}

Subsystem seq_file is only for implement the reading from the file (.read function).

While seq_read is an actual implementation for the .read function, seq_write is just a helper for .show function in struct seq_operations.

See that my answer for the related question about seq_file and writing into the file.

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153