1/**
2 * \file
3 * \brief ramfs service
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 <string.h>
16#include <barrelfish/barrelfish.h>
17#include <barrelfish/nameservice_client.h>
18#include <barrelfish/bulk_transfer.h>
19#include <barrelfish/vregion.h>
20#include <if/trivfs_defs.h>
21#include <if/monitor_defs.h>
22
23#include "ramfs.h"
24
25#define SERVICE_NAME    "ramfs"
26
27#define FHTAB_SIZE_BITS 8
28#define FHTAB_SIZE_MASK ((1U << FHTAB_SIZE_BITS) - 1)
29#define FHTAB_LEN       (1U << FHTAB_SIZE_BITS)
30#define FH_BITS         (sizeof(trivfs_fh_t) * NBBY)
31#define FHGEN_BITS      (FH_BITS - FHTAB_SIZE_BITS)
32
33#define NULL_FH         ((trivfs_fh_t)-1u)
34
35struct msgq_elem {
36    enum trivfs_msg_enum msgnum;
37    union trivfs_rx_arg_union a;
38    struct dirent *dirent;
39    struct msgq_elem *next;
40};
41
42struct client_state {
43    struct dirent *root;
44    struct dirent *fhtab[FHTAB_LEN];
45    unsigned fhindex, fhgen;
46    struct msgq_elem *qstart, *qend; ///< queue of pending replies
47    struct bulk_transfer_slave bulk;
48    struct vregion *bulk_vregion;
49};
50
51/* ------------------------------------------------------------------------- */
52
53static void client_state_init(struct client_state *st, struct dirent *root)
54{
55    st->root = root;
56    memset(st->fhtab, 0, sizeof(st->fhtab));
57    st->fhindex = 0;
58    st->fhgen = 0;
59    st->qstart = st->qend = NULL;
60    st->bulk_vregion = NULL;
61}
62
63static trivfs_fh_t fh_set(struct client_state *st, struct dirent *d)
64{
65    // use the next index slot
66    st->fhtab[st->fhindex] = d;
67    ramfs_incref(d);
68
69    // construct fh: generation and index
70    trivfs_fh_t fh = ((trivfs_fh_t)st->fhgen << FHTAB_SIZE_BITS) | st->fhindex;
71
72    // update index (and generation if needed)
73    if (++st->fhindex == FHTAB_LEN) {
74        st->fhindex = 0;
75        st->fhgen++;
76    }
77
78    return fh;
79}
80
81static struct dirent *fh_get(struct client_state *st, trivfs_fh_t fh)
82{
83    // unpack fh
84    unsigned gen = fh >> FHTAB_SIZE_BITS;
85    unsigned idx = fh & FHTAB_SIZE_MASK;
86
87    // check if it's still valid
88    struct dirent *e = NULL;
89    if ((gen == st->fhgen && idx < st->fhindex)
90        || (gen == st->fhgen - 1 && idx >= st->fhindex)) {
91        e = st->fhtab[idx];
92    }
93
94    if (e == NULL) {
95        return NULL; // invalid or stale
96    }
97
98    if (ramfs_islive(e)) {
99        return e; // valid
100    }
101
102    // has been deleted
103    st->fhtab[idx] = NULL;
104    ramfs_decref(e);
105    return NULL;
106}
107
108/* ------------------------------------------------------------------------- */
109
110static errval_t ramfs_bulk_init(struct trivfs_binding *b, struct capref shared_frame,
111                                errval_t *reterr)
112{
113    struct client_state *st = b->st;
114    errval_t err;
115
116    *reterr = SYS_ERR_OK;
117
118    if (st->bulk_vregion != NULL) {
119        *reterr = FS_ERR_BULK_ALREADY_INIT;
120        cap_destroy(shared_frame);
121        return SYS_ERR_OK;
122    }
123
124    // Determine size of frame
125    struct frame_identity frameid;
126    err = frame_identify(shared_frame, &frameid);
127    if (err_is_fail(err)) {
128        *reterr = err_push(err, LIB_ERR_FRAME_IDENTIFY);
129        cap_destroy(shared_frame);
130        return SYS_ERR_OK;
131    }
132
133    size_t bulk_size = frameid.bytes;
134
135    // Map the frame in local memory
136    void *bulk_pool;
137    err = vspace_map_one_frame_attr(&bulk_pool, bulk_size, shared_frame,
138                                    VREGION_FLAGS_READ_WRITE_MPB, NULL,
139                                    &st->bulk_vregion);
140    if (err_is_fail(err)) {
141        cap_destroy(shared_frame);
142        *reterr = err_push(err, LIB_ERR_VSPACE_MAP);
143        return SYS_ERR_OK;
144    }
145    assert(bulk_pool != NULL);
146    assert(st->bulk_vregion != NULL);
147
148    // Init the bulk transfer library
149    err = bulk_slave_init(bulk_pool, bulk_size, &st->bulk);
150    assert(err_is_ok(err));
151
152    return SYS_ERR_OK;
153}
154
155static errval_t getroot(struct trivfs_binding *b, trivfs_fh_t *rootfh)
156{
157    struct client_state *st = b->st;
158    *rootfh = fh_set(st, st->root);
159
160    return SYS_ERR_OK;
161}
162
163static errval_t readdir(struct trivfs_binding *b, trivfs_fh_t dir, uint32_t idx,
164                        errval_t *reterr, char *name, bool *isdir,
165                        trivfs_fsize_t *size)
166{
167    errval_t err;
168    *reterr = SYS_ERR_OK;
169    struct client_state *st = b->st;
170    name[0] = 0;
171    *isdir = false;
172    *size = 0;
173
174    struct dirent *d = fh_get(st, dir);
175    if (d == NULL) {
176        *reterr = FS_ERR_INVALID_FH;
177        return SYS_ERR_OK;
178    }
179
180    struct dirent *e = NULL;
181    err = ramfs_readdir(d, idx, &e);
182    if (err_is_fail(err)) {
183        *reterr = err;
184        return SYS_ERR_OK;
185    } else if (e == NULL) {
186        *reterr = FS_ERR_INDEX_BOUNDS;
187        return SYS_ERR_OK;
188    }
189
190    ramfs_incref(e);
191    strncpy(name, ramfs_get_name(e), trivfs__read_response_data_MAX_ARGUMENT_SIZE);
192    *isdir = ramfs_isdir(e);
193    *size = ramfs_get_size(e);
194    return SYS_ERR_OK;
195}
196
197static errval_t lookup(struct trivfs_binding *b, trivfs_fh_t dir, const char *name,
198                       errval_t *reterr, trivfs_fh_t *retfh, bool *isdir)
199{
200    errval_t err;
201    *reterr = SYS_ERR_OK;
202    struct client_state *st = b->st;
203    *retfh = NULL_FH;
204    *isdir = false;
205
206    struct dirent *d = fh_get(st, dir);
207    if (d == NULL) {
208        *reterr = FS_ERR_INVALID_FH;
209        return SYS_ERR_OK;
210    }
211
212    if (name == NULL) {
213        *reterr = FS_ERR_NOTFOUND;
214        return SYS_ERR_OK;
215    }
216
217    struct dirent *e = NULL;
218    err = ramfs_lookup(d, name, &e);
219    if (err_is_fail(err)) {
220        *reterr = err;
221        return SYS_ERR_OK;
222    } else if (e == NULL) {
223        *reterr = FS_ERR_INDEX_BOUNDS;
224        return SYS_ERR_OK;
225    }
226
227    *retfh = fh_set(st, e);
228    *isdir = ramfs_isdir(e);
229    return SYS_ERR_OK;
230}
231
232static errval_t getattr(struct trivfs_binding *b, trivfs_fh_t fh,
233                        errval_t *reterr, bool *isdir, trivfs_fsize_t *size)
234{
235    *reterr = SYS_ERR_OK;
236    struct client_state *st = b->st;
237    *isdir = false;
238    *size = 0;
239
240    struct dirent *e = fh_get(st, fh);
241    if (e == NULL) {
242        *reterr = FS_ERR_INVALID_FH;
243        return SYS_ERR_OK;
244
245    }
246    *isdir = ramfs_isdir(e);
247    *size = ramfs_get_size(e);
248    return SYS_ERR_OK;
249}
250
251static errval_t read(struct trivfs_binding *b, trivfs_fh_t fh,
252                 trivfs_offset_t offset, trivfs_fsize_t maxlen,
253                 errval_t *reterr, uint8_t data[2048], size_t *len)
254{
255    errval_t err;
256    *reterr = SYS_ERR_OK;
257    struct client_state *st = b->st;
258    uint8_t *buf = NULL;
259    *len = 0;
260
261    struct dirent *f = fh_get(st, fh);
262    if (f == NULL) {
263        *reterr = FS_ERR_INVALID_FH;
264        return SYS_ERR_OK;
265    }
266
267    err = ramfs_read(f, offset, &buf, len);
268    if (err_is_fail(err)) {
269        *reterr = err;
270        return SYS_ERR_OK;
271    }
272
273    if (*len > maxlen) {
274        *len = maxlen;
275    }
276    memcpy(data, buf, *len);
277    ramfs_incref(f);
278    return SYS_ERR_OK;
279}
280
281static errval_t write(struct trivfs_binding *b, trivfs_fh_t fh,
282                      trivfs_offset_t offset, const uint8_t *data, size_t len,
283                      errval_t *reterr)
284{
285    errval_t err;
286    *reterr = SYS_ERR_OK;
287    struct client_state *st = b->st;
288
289    struct dirent *f = fh_get(st, fh);
290    if (f == NULL) {
291        *reterr = FS_ERR_INVALID_FH;
292        return SYS_ERR_OK;
293    }
294
295    uint8_t *buf;
296
297    err = ramfs_grow(f, offset, len, &buf);
298    if (err_is_fail(err)) {
299        *reterr = err;
300        return SYS_ERR_OK;
301    }
302
303    memcpy(buf, data, len);
304    return SYS_ERR_OK;
305}
306
307static errval_t read_bulk(struct trivfs_binding *b, trivfs_fh_t fh,
308                          trivfs_offset_t offset, trivfs_fsize_t maxlen,
309                          trivfs_bulkid_t bulkid, errval_t *reterr,
310                          trivfs_fsize_t *retlen)
311{
312    errval_t err;
313    *reterr = SYS_ERR_OK;
314    struct client_state *st = b->st;
315    uint8_t *ramfsbuf = NULL;
316    size_t len = 0;
317
318    if (st->bulk_vregion == NULL) {
319        *reterr = FS_ERR_BULK_NOT_INIT;
320        return SYS_ERR_OK;
321    }
322
323    struct dirent *f = fh_get(st, fh);
324    if (f == NULL) {
325        *reterr = FS_ERR_INVALID_FH;
326        return SYS_ERR_OK;
327    }
328
329    err = ramfs_read(f, offset, &ramfsbuf, &len);
330    if (err_is_fail(err)) {
331        *reterr = err;
332        return SYS_ERR_OK;
333    }
334
335    // determine local address of bulk buffer
336    size_t bulk_size;
337    void *bulkbuf = bulk_slave_buf_get_mem(&st->bulk, bulkid, &bulk_size);
338
339    // limit max len to size of bulk buffer
340    if (maxlen > bulk_size) {
341        maxlen = bulk_size;
342    }
343
344    // limit read len to maxlen
345    if (len > maxlen) {
346        len = maxlen;
347    }
348
349    *retlen = len;
350    // copy data to bulk buffer
351    memcpy(bulkbuf, ramfsbuf, len);
352    // prepare bulk buffer for reply
353    bulk_slave_prepare_send(&st->bulk, bulkid);
354    return SYS_ERR_OK;
355}
356
357static errval_t write_bulk(struct trivfs_binding *b, trivfs_fh_t fh,
358                       trivfs_offset_t offset, trivfs_fsize_t len,
359                       trivfs_bulkid_t bulkid, errval_t *reterr)
360{
361    errval_t err;
362    *reterr = SYS_ERR_OK;
363    struct client_state *st = b->st;
364
365    if (st->bulk_vregion == NULL) {
366        *reterr = FS_ERR_BULK_NOT_INIT;
367        return SYS_ERR_OK;
368    }
369
370    struct dirent *f = fh_get(st, fh);
371    if (f == NULL) {
372        *reterr = FS_ERR_INVALID_FH;
373        return SYS_ERR_OK;
374    }
375
376    // determine local address of bulk buffer
377    size_t maxlen;
378    void *bulkbuf = bulk_slave_buf_get_mem(&st->bulk, bulkid, &maxlen);
379
380    // limit len to size of bulk buffer
381    if (len > maxlen) {
382        len = maxlen;
383    }
384
385    uint8_t *ramfsbuf;
386    err = ramfs_grow(f, offset, len, &ramfsbuf);
387    if (err_is_fail(err)) {
388        *reterr = err;
389        return SYS_ERR_OK;
390    }
391
392    bulk_slave_prepare_recv(&st->bulk, bulkid);
393
394    memcpy(ramfsbuf, bulkbuf, len);
395    return SYS_ERR_OK;
396}
397
398static errval_t trivfs_truncate(struct trivfs_binding *b, trivfs_fh_t fh,
399                         trivfs_fsize_t newsize, errval_t *reterr)
400{
401    *reterr = SYS_ERR_OK;
402    struct client_state *st = b->st;
403
404    struct dirent *f = fh_get(st, fh);
405    if (f == NULL) {
406        *reterr = FS_ERR_INVALID_FH;
407    } else {
408        *reterr = ramfs_resize(f, newsize);
409    }
410    return SYS_ERR_OK;
411}
412
413static errval_t create(struct trivfs_binding *b, trivfs_fh_t dir, const char *name,
414                       errval_t *reterr, trivfs_fh_t *fh)
415{
416    errval_t err;
417    *reterr = SYS_ERR_OK;
418    struct client_state *st = b->st;
419    *fh = NULL_FH;
420
421    struct dirent *d = fh_get(st, dir);
422    if (d == NULL) {
423        *reterr = FS_ERR_INVALID_FH;
424        return SYS_ERR_OK;
425    }
426
427    if (name == NULL) {
428        *reterr = FS_ERR_EXISTS; // XXX
429        return SYS_ERR_OK;
430    }
431
432    struct dirent *newf;
433    err = ramfs_create(d, name, &newf);
434    if (err_is_fail(err)) {
435        *reterr = err;
436        return SYS_ERR_OK;
437    }
438
439    *fh = fh_set(st, newf);
440    return SYS_ERR_OK;
441}
442
443static errval_t mkdir(struct trivfs_binding *b, trivfs_fh_t dir, const char *name,
444                      errval_t *reterr, trivfs_fh_t *fh)
445{
446    errval_t err;
447    *reterr = SYS_ERR_OK;
448    struct client_state *st = b->st;
449    *fh = NULL_FH;
450
451    struct dirent *d = fh_get(st, dir);
452    if (d == NULL) {
453        *reterr = FS_ERR_INVALID_FH;
454        return SYS_ERR_OK;
455    }
456
457    if (name == NULL) {
458        *reterr = FS_ERR_EXISTS; // XXX
459        return SYS_ERR_OK;
460    }
461
462    struct dirent *newd;
463    err = ramfs_mkdir(d, name, &newd);
464    if (err_is_fail(err)) {
465        *reterr = err;
466        return SYS_ERR_OK;
467    }
468
469    *fh = fh_set(st, newd);
470    return SYS_ERR_OK;
471}
472
473static errval_t delete(struct trivfs_binding *b, trivfs_fh_t fh, errval_t *reterr)
474{
475    *reterr = SYS_ERR_OK;
476    struct client_state *st = b->st;
477
478    struct dirent *d = fh_get(st, fh);
479    if (d == NULL) {
480        *reterr = FS_ERR_INVALID_FH;
481    } else {
482        *reterr = ramfs_delete(d);
483    }
484    return SYS_ERR_OK;
485}
486
487/* ------------------------------------------------------------------------- */
488
489static struct trivfs_rpc_rx_vtbl rpc_rx_vtbl = {
490    .bulk_init_call = ramfs_bulk_init,
491    .getroot_call = getroot,
492    .readdir_call = readdir,
493    .lookup_call = lookup,
494    .getattr_call = getattr,
495    .read_call = read,
496    .write_call = write,
497    .read_bulk_call = read_bulk,
498    .write_bulk_call = write_bulk,
499    .truncate_call = trivfs_truncate,
500    .create_call = create,
501    .mkdir_call = mkdir,
502    .delete_call = delete,
503};
504
505static void export_cb(void *st, errval_t err, iref_t iref)
506{
507    if (err_is_fail(err)) {
508        DEBUG_ERR(err, "export failed");
509        abort();
510    }
511
512    // register this iref with the name service
513    struct monitor_binding *mb = get_monitor_binding();
514    err = mb->tx_vtbl.set_ramfs_iref_request(mb, NOP_CONT, iref);
515    if(err_is_fail(err)) {
516        USER_PANIC_ERR(err, "failed to send set_ramfs_iref_request to monitor");
517    }
518}
519
520static errval_t connect_cb(void *st, struct trivfs_binding *b)
521{
522    // copy my message receive handler vtable to the binding
523    b->rpc_rx_vtbl = rpc_rx_vtbl;
524
525    // init state
526    struct client_state *bst = malloc(sizeof(struct client_state));
527    assert(bst != NULL);
528    client_state_init(bst, st);
529    b->st = bst;
530
531    return SYS_ERR_OK;
532}
533
534errval_t start_service(struct dirent *root)
535{
536    // Offer the fs service
537    return trivfs_export(root, export_cb, connect_cb, get_default_waitset(),
538                         IDC_EXPORT_FLAGS_DEFAULT);
539}
540