1/**
2 * \file
3 * \brief Trivial RAMFS implementation
4 */
5
6/*
7 * Copyright (c) 2010, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <stdio.h>
16#include <string.h>
17#include <barrelfish/barrelfish.h>
18#include <if/trivfs_defs.h>
19#include "ramfs.h"
20
21struct dirent {
22    struct dirent *next;   ///< next entry in same directory
23    struct dirent **prevp; ///< locn where the preceding child / parent links us
24    struct dirent *parent; ///< parent directory
25    const char *name;   ///< malloc'ed name buffer
26    bool isdir;         ///< is a directory or a file?
27    bool islive;        ///< false if this has been deleted but not yet freed
28    unsigned refcount;  ///< outstanding references (handles and/or ongoing IDCs)
29    union {
30        struct {
31            uint8_t *data;  ///< on heap
32            size_t size;    ///< size of data
33        } file;
34        struct {
35            struct dirent *entries; ///< children of this dir
36            size_t nentries;        ///< number of children
37        } dir;
38    } u;
39};
40
41struct dirent *ramfs_init(void)
42{
43    struct dirent *root = malloc(sizeof(struct dirent));
44    assert(root != NULL);
45
46    root->next = NULL;
47    root->prevp = NULL;
48    root->parent = NULL;
49    root->name = "";
50    root->isdir = true;
51    root->islive = true;
52    root->u.dir.entries = NULL;
53    root->u.dir.nentries = 0;
54    root->refcount = 1;
55
56    return root;
57}
58
59inline void ramfs_incref(struct dirent *e)
60{
61    assert(e->refcount > 0);
62    assert(e->islive);
63    e->refcount++;
64}
65
66void ramfs_decref(struct dirent *e)
67{
68    assert(e->refcount > 0);
69    if (--e->refcount == 0) {
70        assert(!e->islive);
71        assert(e->next == NULL && e->prevp == NULL);
72        free((void *)e->name);
73        if (e->isdir) {
74            assert(e->u.dir.nentries == 0);
75            assert(e->u.dir.entries == NULL);
76        } else {
77            free(e->u.file.data);
78        }
79        free(e);
80    }
81}
82
83const char *ramfs_get_name(struct dirent *e)
84{
85    assert(e->islive && e->refcount > 0);
86    return e->name;
87}
88
89bool ramfs_isdir(struct dirent *e)
90{
91    assert(e->islive && e->refcount > 0);
92    return e->isdir;
93}
94
95bool ramfs_islive(struct dirent *e)
96{
97    assert(e->refcount > 0);
98    return e->islive;
99}
100
101size_t ramfs_get_size(struct dirent *e)
102{
103    assert(e->islive && e->refcount > 0);
104    return e->isdir ? e->u.dir.nentries : e->u.file.size;
105}
106
107errval_t ramfs_readdir(struct dirent *dir, uint32_t idx, struct dirent **ret)
108{
109    assert(dir->islive && dir->refcount > 0);
110
111    if (!dir->isdir) {
112        return FS_ERR_NOTDIR;
113    }
114
115    struct dirent *e = dir->u.dir.entries;
116    while (e != NULL && idx > 0) {
117        e = e->next;
118        idx--;
119    }
120
121    if (idx > 0 || e == NULL) {
122        return FS_ERR_INDEX_BOUNDS;
123    }
124
125    assert(ret != NULL);
126    *ret = e;
127    return SYS_ERR_OK;
128}
129
130errval_t ramfs_lookup(struct dirent *dir, const char *name, struct dirent **ret)
131{
132    assert(dir != NULL);
133    assert(dir->islive && dir->refcount > 0);
134
135    if (!dir->isdir) {
136        return FS_ERR_NOTDIR;
137    }
138
139    struct dirent *e;
140    for (e = dir->u.dir.entries; e != NULL; e = e->next) {
141        if (strcmp(e->name, name) == 0) {
142            *ret = e;
143            return SYS_ERR_OK;
144        }
145    }
146
147    return FS_ERR_NOTFOUND;
148}
149
150errval_t ramfs_read(struct dirent *f, off_t offset, uint8_t **retbuf,
151                    size_t *maxlen)
152{
153    assert(f->islive && f->refcount > 0);
154
155    if (f->isdir) {
156        return FS_ERR_NOTFILE;
157    }
158
159    if (offset < 0 || offset >= f->u.file.size) {
160        *retbuf = NULL;
161        *maxlen = 0;
162    } else {
163        assert(f->u.file.data != NULL);
164        *retbuf = &f->u.file.data[offset];
165        *maxlen = f->u.file.size - offset;
166    }
167
168    return SYS_ERR_OK;
169}
170
171/// Ensure that the file has at least space for a write of the given size at
172/// the given offset, return buffer pointer
173errval_t ramfs_grow(struct dirent *f, off_t offset, size_t len,
174                    uint8_t **retbuf)
175{
176    assert(f->islive && f->refcount > 0);
177
178    if (f->isdir) {
179        return FS_ERR_NOTFILE;
180    }
181
182    assert(offset >= 0);
183
184    // do we need to grow the file? if not, we're done
185    if (offset + len <= f->u.file.size) {
186        *retbuf = &f->u.file.data[offset];
187        return SYS_ERR_OK;
188    }
189
190    uint8_t *newdata = realloc(f->u.file.data, (size_t)offset + len);
191    if (newdata == NULL) {
192        return LIB_ERR_MALLOC_FAIL;
193    } else {
194        f->u.file.data = newdata;
195        f->u.file.size = (size_t)offset + len;
196        *retbuf = &newdata[offset];
197        // XXX: new data is not initialised, we rely on the caller to do this!
198        return SYS_ERR_OK;
199    }
200}
201
202errval_t ramfs_resize(struct dirent *f, size_t newlen)
203{
204    assert(f->islive && f->refcount > 0);
205
206    if (f->isdir) {
207        return FS_ERR_NOTFILE;
208    }
209
210    uint8_t *newdata = realloc(f->u.file.data, newlen);
211    if (newdata == NULL) {
212        return LIB_ERR_MALLOC_FAIL;
213    }
214
215    if (newlen > f->u.file.size) {
216        // zero-fill new data
217        memset(&newdata[f->u.file.size], 0, newlen - f->u.file.size);
218    }
219
220    f->u.file.data = newdata;
221    f->u.file.size = newlen;
222
223    return SYS_ERR_OK;
224}
225
226static errval_t addchild(struct dirent *dir, struct dirent *child)
227{
228    assert(child->refcount == 1);
229    if (dir->u.dir.entries == NULL) {
230        assert(dir->u.dir.nentries == 0);
231        dir->u.dir.entries = child;
232        child->prevp = &dir->u.dir.entries;
233    } else {
234        // search to end of list
235        struct dirent *e;
236        for (e = dir->u.dir.entries; ; e = e->next) {
237            // check for duplicates
238            if (strcmp(e->name, child->name) == 0) {
239                return FS_ERR_EXISTS;
240            }
241            if (e->next == NULL) {
242                break;
243            }
244        }
245        e->next = child;
246        child->prevp = &e->next;
247    }
248    child->parent = dir;
249    dir->u.dir.nentries++;
250    return SYS_ERR_OK;
251}
252
253errval_t ramfs_create(struct dirent *dir, const char *name, struct dirent **ret)
254{
255    assert(dir != NULL);
256    assert(dir->islive && dir->refcount > 0);
257
258    if (!dir->isdir) {
259        return FS_ERR_NOTDIR;
260    }
261
262    struct dirent *f = malloc(sizeof(struct dirent));
263    if (f == NULL) {
264        return LIB_ERR_MALLOC_FAIL;
265    }
266
267    f->next = NULL;
268    f->name = name; /* XXX: takes ownership of name buffer */
269    f->isdir = false;
270    f->refcount = 1;
271    f->islive = true;
272    f->u.file.data = NULL;
273    f->u.file.size = 0;
274
275    errval_t err = addchild(dir, f);
276
277    if (err_is_ok(err)) {
278        if (ret != NULL) {
279            *ret = f;
280        }
281    } else {
282        free(f);
283    }
284
285    return err;
286}
287
288errval_t ramfs_mkdir(struct dirent *dir, const char *name, struct dirent **ret)
289{
290    assert(dir != NULL);
291    assert(dir->islive && dir->refcount > 0);
292
293    if (!dir->isdir) {
294        return FS_ERR_NOTDIR;
295    }
296
297    struct dirent *d = malloc(sizeof(struct dirent));
298    if (d == NULL) {
299        return LIB_ERR_MALLOC_FAIL;
300    }
301
302    d->next = NULL;
303    d->name = name; /* XXX: takes ownership of name buffer */
304    d->isdir = true;
305    d->refcount = 1;
306    d->islive = true;
307    d->u.dir.entries = NULL;
308    d->u.dir.nentries = 0;
309
310    errval_t err = addchild(dir, d);
311
312    if (err_is_fail(err)) {
313        free(d);
314    }
315
316    if (ret != NULL && err_is_ok(err)) {
317        *ret = d;
318    }
319
320    return err;
321}
322
323errval_t ramfs_delete(struct dirent *e)
324{
325    assert(e != NULL);
326    assert(e->islive && e->refcount > 0);
327
328    // check if we can delete this
329    if (e->isdir && e->u.dir.entries != NULL) {
330        return FS_ERR_NOTEMPTY;
331    }
332
333    // XXX: prevent deletion of root dir, even if empty
334    if (e->prevp == NULL) {
335        assert(e->isdir);
336        assert(e->next == NULL);
337        return FS_ERR_NOTEMPTY; // XXX
338    }
339
340    // unlink from parent directory
341    assert(e->prevp != NULL);
342    *e->prevp = e->next;
343    if (e->next != NULL) {
344        e->next->prevp = e->prevp;
345    }
346
347    // update parent's child count
348    assert(e->parent != NULL && e->parent->isdir);
349    assert(e->parent->u.dir.nentries > 0);
350    e->parent->u.dir.nentries--;
351
352    // mark dead, deref and we're done
353    e->islive = false;
354    ramfs_decref(e);
355
356    return SYS_ERR_OK;
357}
358