1/*
2 * Copyright (c) 2009, 2011, ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#define _USE_XOPEN // for strdup()
11#include <stdio.h>
12#include <string.h>
13#include <barrelfish/barrelfish.h>
14#include <barrelfish/nameservice_client.h>
15#include <barrelfish/bulk_transfer.h>
16#include <vfs/vfs_path.h>
17
18#include "vfs_backends.h"
19#include "vfs_blockdevfs.h"
20
21/*
22 * This is a simple filesystem that has virtual files for any blockdevice
23 * style devices
24 *
25 * Assumptions:
26 * - devices are fixed in size
27 * - no concurrent accesses. blockdevfs will only hand out one handle to every
28 *   device
29 *
30 * Currently implemented:
31 * - SATA disks over AHCI (/<mountpoint>/ahciX)
32 */
33
34static struct blockdev_entry *entries_first = NULL;
35static struct blockdev_entry *entries_last = NULL;
36
37void blockdev_append_entry(struct blockdev_entry *entry)
38{
39    entry->next = NULL; // this is the last element in any case
40    if (entries_last == NULL) {
41        entries_first = entry;
42        entries_last = entry;
43        entry->prev = NULL;
44    } else {
45        entries_last->next = entry;
46        entry->prev = entries_last;
47        entries_last = entry;
48    }
49}
50
51struct blockdevfs_handle {
52    struct vfs_handle common;
53    struct blockdev_entry *entry;
54    size_t pos;
55};
56
57struct blockdevfs_dirhandle {
58    struct vfs_handle common;
59    struct blockdev_entry *entry;
60};
61
62struct backend_ops {
63    errval_t (*open)(void *handle);
64    errval_t (*close)(void *handle);
65    errval_t (*read)(void *handle, size_t pos, void *buffer, size_t bytes,
66                     size_t *bytes_read);
67    errval_t (*write)(void *handle, size_t pos, const void *buffer, size_t bytes,
68                      size_t *bytes_written);
69    errval_t (*flush)(void *handle);
70};
71
72
73
74struct backend_ops backends[3] = {
75    {
76        .open = blockdevfs_ahci_open,
77        .close = blockdevfs_ahci_close,
78        .read = blockdevfs_ahci_read,
79        .write = blockdevfs_ahci_write,
80        .flush = blockdevfs_ahci_flush
81    },
82    {
83        .open = blockdevfs_ata_open,
84        .close = blockdevfs_ata_close,
85        .read = blockdevfs_ata_read,
86        .write = blockdevfs_ata_write,
87        .flush = blockdevfs_ata_flush
88    },
89#ifndef DISABLE_MEGARAID
90    {
91        .open = blockdevfs_megaraid_open,
92        .close = blockdevfs_megaraid_close,
93        .read = blockdevfs_megaraid_read,
94        .write = blockdevfs_megaraid_write,
95        .flush = blockdevfs_megaraid_flush,
96    },
97#endif
98};
99
100
101static errval_t open(void *st, const char *path, vfs_handle_t *rethandle)
102{
103    VFS_BLK_DEBUG("blockdevfs_open: entering\n");
104    errval_t err;
105
106    char *path_ = (char *)path;
107    if (path[0] == VFS_PATH_SEP) {
108        path_++; // skip leading seperator
109    }
110
111    // search for entry
112    struct blockdev_entry *cur = entries_first;
113    for (; cur != NULL; cur = cur->next) {
114        VFS_BLK_DEBUG("cur->path = %s\n", cur->path);
115        if (!strcmp(path_, cur->path)) {
116            break;
117        }
118    }
119
120    if (cur == NULL) {
121        return FS_ERR_NOTFOUND;
122    }
123
124    if (cur->open) {
125        printf("already open\n");
126        return FS_ERR_INVALID_FH; //FIXME: proper error code
127    }
128
129    // we can open it
130    cur->open = true;
131
132    err = backends[cur->type].open(cur->backend_handle);
133    if (err_is_fail(err)) {
134        cur->open = false;
135        printf("failed to open\n");
136        return FS_ERR_INVALID_FH; // FIXME: better error code
137    }
138
139    struct blockdevfs_handle *handle = malloc(sizeof(struct blockdevfs_handle));
140    assert(handle != NULL);
141    handle->pos = 0;
142    handle->entry = cur;
143    *rethandle = handle;
144
145    VFS_BLK_DEBUG("blockdevfs_open: exiting\n");
146    return SYS_ERR_OK;
147}
148
149// create is not allowed in blockdevfs
150static errval_t create(void *st, const char *path, vfs_handle_t *rethandle)
151{
152    return open(st, path, rethandle);
153}
154
155// remove is not allowed in blockdevfs
156static errval_t blockdevfs_remove(void *st, const char *path)
157{
158    return FS_ERR_NOTFILE; //FIXME: more suitable error code
159}
160
161static errval_t read(void *st, vfs_handle_t handle, void *buffer, size_t bytes,
162        size_t *bytes_read)
163{
164    struct blockdevfs_handle *h = handle;
165    struct blockdev_entry *entry = h->entry;
166    VFS_BLK_DEBUG("blockdevfs: read %zu bytes at pos %zu into %p from type %d\n",
167            bytes, h->pos, buffer, entry->type);
168    VFS_BLK_DEBUG("blockdevfs: %p %p\n",
169            blockdevfs_ahci_read, backends[entry->type].read);
170    errval_t ret = backends[entry->type].read(entry->backend_handle, h->pos,
171            buffer, bytes, bytes_read);
172
173    if (err_is_ok(ret)) {
174        h->pos += *bytes_read;
175    }
176
177    return ret;
178}
179
180static errval_t write(void *st, vfs_handle_t handle, const void *buffer, size_t
181        bytes, size_t *bytes_written)
182{
183    struct blockdevfs_handle *h = handle;
184    struct blockdev_entry *entry = h->entry;
185    errval_t ret = backends[entry->type].write(entry->backend_handle, h->pos,
186            buffer, bytes, bytes_written);
187
188    if (err_is_ok(ret)) {
189        h->pos += *bytes_written;
190    }
191
192    return ret;
193}
194
195// truncate is not allowed in blockdevfs
196static errval_t blockdevfs_truncate(void *st, vfs_handle_t handle, size_t bytes)
197{
198    return FS_ERR_NOTFILE; //FIXME: more suitable error code
199}
200
201static errval_t tell(void *st, vfs_handle_t handle, size_t *pos)
202{
203    struct blockdevfs_handle *h = handle;
204    *pos = h->pos;
205    return SYS_ERR_OK;
206}
207
208static errval_t stat(void *st, vfs_handle_t inhandle, struct vfs_fileinfo *info)
209{
210    struct blockdevfs_handle *h = inhandle;
211
212    info->type = VFS_FILE;
213    info->size = h->entry->size;
214
215    return SYS_ERR_OK;
216}
217
218static errval_t seek(void *st, vfs_handle_t handle, enum vfs_seekpos whence,
219        off_t offset)
220{
221    struct blockdevfs_handle *h = handle;
222    struct vfs_fileinfo info;
223    errval_t err;
224
225    switch (whence) {
226    case VFS_SEEK_SET:
227        assert(offset >= 0);
228        h->pos = offset;
229        break;
230
231    case VFS_SEEK_CUR:
232        assert(offset >= 0 || -offset <= h->pos);
233        h->pos += offset;
234        break;
235
236    case VFS_SEEK_END:
237        err = stat(st, handle, &info);
238        if (err_is_fail(err)) {
239            return err;
240        }
241        assert(offset >= 0 || -offset <= info.size);
242        h->pos = info.size + offset;
243        break;
244
245    default:
246        USER_PANIC("invalid whence argument to blockdevfs seek");
247    }
248
249    return SYS_ERR_OK;
250}
251
252static errval_t close(void *st, vfs_handle_t inhandle)
253{
254    struct blockdevfs_handle *handle = inhandle;
255    struct blockdev_entry *entry = handle->entry;
256
257    errval_t ret = backends[entry->type].close(entry->backend_handle);
258
259    handle->entry->open = false;
260    free(handle);
261
262    return ret;
263}
264
265static errval_t opendir(void *st, const char *path, vfs_handle_t *rethandle)
266{
267    struct blockdevfs_dirhandle *handle =
268        calloc(1, sizeof(struct blockdevfs_dirhandle));
269    assert(handle != NULL);
270    handle->entry = entries_first;
271
272    *rethandle = handle;
273
274    return SYS_ERR_OK;
275}
276
277static errval_t dir_read_next(void *st, vfs_handle_t inhandle, char **retname,
278        struct vfs_fileinfo *info)
279{
280    struct blockdevfs_dirhandle *handle = inhandle;
281    if (handle->entry == NULL) {
282        return FS_ERR_INDEX_BOUNDS;
283    }
284
285    if (retname != NULL) {
286        *retname = strdup(handle->entry->path);
287    }
288
289    info->size = handle->entry->size;
290    info->type = VFS_FILE;
291
292    handle->entry = handle->entry->next;
293
294    return SYS_ERR_OK;
295}
296
297static errval_t closedir(void *st, vfs_handle_t dhandle)
298{
299    struct blockdevfs_dirhandle *handle = dhandle;
300    free(handle);
301    return SYS_ERR_OK;
302}
303
304// creating/removing dirs is not allowed in blockdevfs
305static errval_t mkdir(void *st, const char *path)
306{
307    return FS_ERR_NOTFILE; //FIXME: more suitable error code
308}
309
310static errval_t rmdir(void *st, const char *path)
311{
312    return FS_ERR_NOTFILE; //FIXME: more suitable error code
313}
314
315static errval_t flush(void *st, vfs_handle_t inhandle)
316{
317    struct blockdevfs_handle *handle = inhandle;
318    struct blockdev_entry *entry = handle->entry;
319
320    return backends[entry->type].flush(entry->backend_handle);
321}
322
323static struct vfs_ops blockdevfsops = {
324    .open = open,
325    .create = create,
326    .remove = blockdevfs_remove,
327    .read = read,
328    .write = write,
329    .truncate = blockdevfs_truncate,
330    .seek = seek,
331    .tell = tell,
332    .stat = stat,
333    .close = close,
334    .opendir = opendir,
335    .dir_read_next = dir_read_next,
336    .closedir = closedir,
337    .mkdir = mkdir,
338    .rmdir = rmdir,
339    .flush = flush,
340};
341
342errval_t vfs_blockdevfs_mount(const char *uri, void **retst, struct vfs_ops **retops)
343{
344    errval_t err;
345
346    // skip over protocol part of URI to get service name
347    char *service = strstr(uri, "://");
348    if (service == NULL) {
349        return VFS_ERR_BAD_URI;
350    }
351    service += 3;
352
353    if (*service == 0) {
354        // init all kinds of blockdevice sources
355        err = blockdevfs_ahci_init();       // AHCI
356        assert(err_is_ok(err));
357        err = blockdevfs_ata_init();
358        assert(err_is_ok(err));
359#ifndef DISABLE_MEGARAID // Flounder
360    } else if(!strcmp(service, "megaraid")) {
361        err = blockdevfs_megaraid_init();
362        assert(err_is_ok(err));
363#endif
364    } else {
365        return VFS_ERR_BAD_URI;
366    }
367
368    *retops = &blockdevfsops;
369    *retst = NULL;
370
371    return SYS_ERR_OK;
372}
373