summaryrefslogtreecommitdiffstats
path: root/assoc_buf.h
blob: e9e7fbc4b3090030fab98e6245b4e9f0e66e15d5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <linux/slab.h>
#include <linux/kernel.h>

struct asbuf {
    unsigned long ino;
    char *buf;
    size_t alloc;
};


ssize_t asbuf_free_alloc(struct asbuf *buf, size_t size) {
    char *mem;

    mem = kmalloc(size, GFP_KERNEL);
    if (!mem)
        return -ENOMEM;

    if (buf->alloc)
        kfree(buf->buf);

    buf->buf = mem;
    buf->alloc = size;
    return 0;
}

void asbuf_free(struct asbuf *buf) {
    kfree(buf->buf);
}

ssize_t asbuf_to_user(struct asbuf *buf,
                      char __user *user_buf,
                      size_t len,
                      loff_t *offset) {

    size_t chars_left, request;
    ssize_t uncopied, copied;

    if (!buf->alloc)
        return -EINVAL;

    if (*offset > buf->alloc)
        return -EINVAL;

    chars_left = buf->alloc - *offset;
    request = min(len, chars_left);

    printk(KERN_INFO "buf: %p\t alloc: %zu\t request: %zu\n", buf, buf->alloc, request);

    uncopied = copy_to_user(user_buf, buf->buf + *offset, request);
    copied = request - uncopied;

    *offset += copied;
    return copied;
}

ssize_t asbuf_from_user(struct asbuf *buf,
                        const char __user *user_buf,
                        size_t len,
                        loff_t *offset) {
    ssize_t rc = 0;
    size_t uncopied, copied;
    rc = asbuf_free_alloc(buf, len);
    if (rc != 0)
        return rc;

    uncopied = copy_from_user(buf->buf, user_buf, len);
    copied = len - uncopied;

    *offset = 0;

    return copied;
}