1/*
2 * Copyright 2016, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(D61_BSD)
11 */
12
13#include <assert.h>
14#include <stdio.h>
15#include <stdint.h>
16#include <stdlib.h>
17#include <string.h>
18#include <sel4/sel4.h>
19
20#include <refos/refos.h>
21#include <refos/error.h>
22#include <refos-io/filetable.h>
23#include <refos-io/internal_state.h>
24#include <refos-rpc/serv_client.h>
25#include <refos-rpc/serv_client_helper.h>
26#include <refos-util/dprintf.h>
27
28#define FD_TABLE_DEFAULT_SIZE 1024
29#define FD_TABLE_ENTRY_TYPE_NONE 0
30#define FD_TABLE_ENTRY_TYPE_DATASPACE 1
31
32#define FD_TABLE_ENTRY_DATASPACE_MAGIC 0x4E6CC517
33#define FD_TABLE_DATASPACE_IPC_MAXLEN 32
34
35typedef struct fd_table_entry_dataspace_s {
36    char type; /* FD_TABLE_ENTRY_TYPE. Inherited, must be first. */
37    int magic;
38    int fd;
39
40    serv_connection_t connection;
41    seL4_CPtr dspace;
42    int32_t dspacePos;
43    uint32_t dspaceSize;
44} fd_table_entry_dataspace_t;
45
46/* ----------------------------- Filetable OAT functions ---------------------------------------- */
47
48static cvector_item_t
49filetable_oat_create(coat_t *oat, int id, uint32_t arg[COAT_ARGS])
50{
51    char type = (char) arg[0];
52    cvector_item_t item = NULL;
53
54    fd_table_entry_dataspace_t *e = NULL;
55
56    switch (type) {
57        case FD_TABLE_ENTRY_TYPE_DATASPACE:
58            /* Allocate and set a new dataspace FD entry struct. */
59            e = (fd_table_entry_dataspace_t*) malloc(sizeof(fd_table_entry_dataspace_t));
60            if (e){
61                memset(e, 0, sizeof(fd_table_entry_dataspace_t));
62                e->type = type;
63                e->magic = FD_TABLE_ENTRY_DATASPACE_MAGIC;
64                e->fd = id;
65            }
66            item = (cvector_item_t) e;
67            break;
68        default:
69            printf("filetable_oat_create error: Unknown type.\n");
70            break;
71    }
72
73    if (!item) {
74        printf("filetable_oat_create error: Could not allocate structure.\n");
75        return NULL;
76    }
77
78    return item;
79}
80
81static void
82filetable_oat_delete(coat_t *oat, cvector_item_t *obj)
83{
84    char type = *((char*) obj);
85    fd_table_entry_dataspace_t *e = NULL;
86
87    switch(type) {
88        case FD_TABLE_ENTRY_TYPE_DATASPACE:
89            e = (fd_table_entry_dataspace_t*) obj;
90            assert(e->type == FD_TABLE_ENTRY_TYPE_DATASPACE);
91            assert(e->magic == FD_TABLE_ENTRY_DATASPACE_MAGIC);
92
93            /* Delete dataspace. */
94            if (e->connection.serverSession && e->dspace) {
95                refos_err_t error = data_close(e->connection.serverSession, e->dspace);
96                if (error != ESUCCESS) {
97                    printf("filetable_oat_delete error: couldn't close dspace.\n");
98                    return;
99                }
100                csfree_delete(e->dspace);
101                e->dspace = 0;
102            }
103
104            /* Disconnect from server. */
105            if (e->connection.serverSession) {
106                serv_disconnect(&e->connection);
107                e->connection.serverSession = 0;
108            }
109
110            e->magic = 0x0;
111            free(e);
112            break;
113        default:
114            printf("filetable_oat_delete error: Unknown type.\n");
115            break;
116    }
117
118    return;
119}
120
121/* -------------------------- Filetable interface functions ------------------------------------- */
122
123void
124filetable_init(fd_table_t *fdt, uint32_t tableSize)
125{
126    assert(fdt);
127    fdt->magic = FD_TABLE_MAGIC;
128    fdt->tableSize = tableSize;
129
130    /* Initialise FD allocation table. */
131    memset(&fdt->table, 0, sizeof(coat_t));
132    fdt->table.oat_create = filetable_oat_create;
133    fdt->table.oat_delete = filetable_oat_delete;
134    coat_init(&fdt->table, FD_TABLE_BASE, tableSize);
135}
136
137void
138filetable_release(fd_table_t *fdt)
139{
140    assert(fdt && fdt->magic == FD_TABLE_MAGIC);
141    coat_release(&fdt->table);
142    fdt->magic = 0x0;
143}
144
145int
146filetable_dspace_open(fd_table_t *fdt, char* filePath, int flags, int mode, int size)
147{
148    assert(fdt && fdt->magic == FD_TABLE_MAGIC);
149    if (!filePath) {
150        return -EFILENOTFOUND;
151    }
152
153    int error = -EINVALID;
154    fd_table_entry_dataspace_t* e = NULL;
155    uint32_t arg[COAT_ARGS];
156    arg[0] = FD_TABLE_ENTRY_TYPE_DATASPACE;
157
158    /* Allocate an ID, and the FD entry structure associated with it. */
159    coat_alloc(&fdt->table, arg, (cvector_item_t *) &e);
160    if (!e) {
161        printf("filetable_open out of memory.\n");
162        return -ENOMEM;
163    }
164
165    /* Connect to the dataspace server. */
166    assert(e->magic == FD_TABLE_ENTRY_DATASPACE_MAGIC);
167    e->connection = serv_connect_no_pbuffer(filePath);
168    if (e->connection.error != ESUCCESS || !e->connection.serverSession) {
169        error = -ESERVERNOTFOUND;
170        goto exit1;
171    }
172
173    /* Open the dataspace on the server. */
174    e->dspace = data_open(e->connection.serverSession,
175            e->connection.serverMountPoint.dspaceName, flags, mode, size, &error);
176    if (error || !e->dspace) {
177        error = -EFILENOTFOUND;
178        goto exit2;
179    }
180
181    e->dspaceSize = data_get_size(e->connection.serverSession, e->dspace);
182    e->dspacePos = 0;
183    return e->fd;
184
185    /* Exit stack. */
186exit2:
187    serv_disconnect(&e->connection);
188exit1:
189    assert(e && e->fd);
190    coat_free(&fdt->table, e->fd);
191    assert(error < 0);
192    return error;
193}
194
195int
196filetable_close(fd_table_t *fdt, int fd)
197{
198    assert(fdt && fdt->magic == FD_TABLE_MAGIC);
199    if (fd < FD_TABLE_BASE || fd >= fdt->tableSize) {
200        return -EFILENOTFOUND;
201    }
202    coat_free(&fdt->table, fd);
203    return ESUCCESS;
204}
205
206refos_err_t
207filetable_lseek(fd_table_t *fdt, int fd, int *offset, int whence)
208{
209    /* because muslc can now call seek when closing a file,
210       fdt->magic may now be 0 here as well as FD_TABLE_MAGIC */
211    assert(fdt && (fdt->magic == FD_TABLE_MAGIC || fdt->magic == 0));
212    if (!offset) {
213        printf("filetable_lseek - NULL parameter.\n");
214        return EINVALIDPARAM;
215    }
216    if (fd < FD_TABLE_BASE || fd >= fdt->tableSize) {
217        return EFILENOTFOUND;
218    }
219
220    /* Retrieve the file descr entry. */
221    cvector_item_t entry = coat_get(&fdt->table, fd);
222    if (!entry) {
223        return EFILENOTFOUND;
224    }
225    char type = *((char*) entry);
226
227    /* lseek only support for dataspace entries. */
228    if (type != FD_TABLE_ENTRY_TYPE_DATASPACE) {
229        assert(!"lseek for this type unimplemented.");
230        return EUNIMPLEMENTED;
231    }
232
233    fd_table_entry_dataspace_t *fdEntry = (fd_table_entry_dataspace_t*) entry;
234    assert(fdEntry->magic == FD_TABLE_ENTRY_DATASPACE_MAGIC);
235
236    switch(whence) {
237        case SEEK_SET:
238            fdEntry->dspacePos = (*offset);
239            break;
240        case SEEK_CUR:
241            fdEntry->dspacePos += (*offset);
242            break;
243        case SEEK_END:
244            fdEntry->dspacePos = fdEntry->dspaceSize + (*offset);
245            break;
246        default:
247            return EINVALIDPARAM;
248    }
249
250    if (fdEntry->dspacePos < 0) {
251        fdEntry->dspacePos = 0;
252    }
253    if (fdEntry->dspacePos > fdEntry->dspaceSize) {
254        fdEntry->dspacePos = fdEntry->dspaceSize;
255    }
256
257    (*offset) = fdEntry->dspacePos;
258    return ESUCCESS;
259}
260
261static int
262filetable_internal_read_write(fd_table_t *fdt, int fd, char *buffer, int bufferLen, bool read)
263{
264    assert(fdt && fdt->magic == FD_TABLE_MAGIC);
265    if (!buffer || !bufferLen) {
266        ROS_SET_ERRNO(ESUCCESS);
267        return 0;
268    }
269    if (fd < FD_TABLE_BASE || fd >= fdt->tableSize) {
270        ROS_SET_ERRNO(EFILENOTFOUND);
271        return -EFILENOTFOUND;
272    }
273
274    /* Retrieve the file descr entry. */
275    cvector_item_t entry = coat_get(&fdt->table, fd);
276    if (!entry) {
277        ROS_SET_ERRNO(EFILENOTFOUND);
278        return -EFILENOTFOUND;
279    }
280    char type = *((char*) entry);
281
282    /* Read / write only supported for dataspace entries. */
283    if (type != FD_TABLE_ENTRY_TYPE_DATASPACE) {
284        assert(!"read / write for this type unimplemented.");
285        ROS_SET_ERRNO(EUNIMPLEMENTED);
286        return -EUNIMPLEMENTED;
287    }
288
289    fd_table_entry_dataspace_t *fdEntry = (fd_table_entry_dataspace_t*) entry;
290    assert(fdEntry->magic == FD_TABLE_ENTRY_DATASPACE_MAGIC);
291
292    /* Cap length so we don't overrun IPC buffer.
293       Currently read / write is implemented over IPC, and this is inefficient and somewhat hacky.
294       In the future, file read / write using mapped shared memory should be implemented.
295    */
296    if (bufferLen > FD_TABLE_DATASPACE_IPC_MAXLEN) {
297        bufferLen = FD_TABLE_DATASPACE_IPC_MAXLEN;
298    }
299
300    /* Perform the actual dataspace read / write operation. */
301    assert(fdEntry->dspace);
302    int nr = -EINVALID;
303    if (read) {
304        nr = data_read(fdEntry->connection.serverSession, fdEntry->dspace, fdEntry->dspacePos,
305                       buffer, bufferLen);
306    } else {
307        nr = data_write(fdEntry->connection.serverSession, fdEntry->dspace, fdEntry->dspacePos,
308                       buffer, bufferLen);
309    }
310    if (nr < 0) {
311        ROS_SET_ERRNO(-nr);
312        return nr;
313    }
314
315    /* Shift the dataspace position offset. */
316    fdEntry->dspacePos += nr;
317    if (fdEntry->dspacePos < 0) {
318        fdEntry->dspacePos = 0;
319    }
320    if (read) {
321        if (fdEntry->dspacePos > fdEntry->dspaceSize) {
322            fdEntry->dspacePos = fdEntry->dspaceSize;
323        }
324    } else {
325        fdEntry->dspaceSize = data_get_size(fdEntry->connection.serverSession, fdEntry->dspace);
326    }
327
328    ROS_SET_ERRNO(ESUCCESS);
329    return nr;
330}
331
332int
333filetable_read(fd_table_t *fdt, int fd, char *bufferDest, int bufferLen)
334{
335    return filetable_internal_read_write(fdt, fd, bufferDest, bufferLen, true);
336}
337
338int
339filetable_write(fd_table_t *fdt, int fd, char *bufferSrc, int bufferLen)
340{
341    return filetable_internal_read_write(fdt, fd, bufferSrc, bufferLen, false);
342}
343
344seL4_CPtr
345filetable_dspace_get(fd_table_t *fdt, int fd)
346{
347    assert(fdt && fdt->magic == FD_TABLE_MAGIC);
348    if (fd < FD_TABLE_BASE || fd >= fdt->tableSize) {
349        ROS_SET_ERRNO(EFILENOTFOUND);
350        return -EFILENOTFOUND;
351    }
352
353    /* Retrieve the file descr entry. */
354    cvector_item_t entry = coat_get(&fdt->table, fd);
355    if (!entry) {
356        ROS_SET_ERRNO(EFILENOTFOUND);
357        return -EFILENOTFOUND;
358    }
359    char type = *((char*) entry);
360
361    /* Read / write only supported for dataspace entries. */
362    if (type != FD_TABLE_ENTRY_TYPE_DATASPACE) {
363        assert(!"dspace_get for this type unsupported.");
364        ROS_SET_ERRNO(EUNIMPLEMENTED);
365        return -EUNIMPLEMENTED;
366    }
367
368    fd_table_entry_dataspace_t *fdEntry = (fd_table_entry_dataspace_t*) entry;
369    assert(fdEntry->magic == FD_TABLE_ENTRY_DATASPACE_MAGIC);
370    return fdEntry->dspace;
371}
372
373/* ----------------------- Refos IO default filetable functions --------------------------------- */
374
375void
376filetable_init_default(void)
377{
378    filetable_init(&refosIOState.fdTable, FD_TABLE_DEFAULT_SIZE);
379}
380
381void
382filetable_deinit_default(void)
383{
384    filetable_release(&refosIOState.fdTable);
385}
386