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, Haldeneggsteig 4, 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    {
90        .open = blockdevfs_megaraid_open,
91        .close = blockdevfs_megaraid_close,
92        .read = blockdevfs_megaraid_read,
93        .write = blockdevfs_megaraid_write,
94        .flush = blockdevfs_megaraid_flush,
95    },
96};
97
98
99static errval_t open(void *st, const char *path, vfs_handle_t *rethandle)
100{
101    VFS_BLK_DEBUG("blockdevfs_open: entering\n");
102    errval_t err;
103
104    char *path_ = (char *)path;
105    if (path[0] == VFS_PATH_SEP) {
106        path_++; // skip leading seperator
107    }
108
109    // search for entry
110    struct blockdev_entry *cur = entries_first;
111    for (; cur != NULL; cur = cur->next) {
112        VFS_BLK_DEBUG("cur->path = %s\n", cur->path);
113        if (!strcmp(path_, cur->path)) {
114            break;
115        }
116    }
117
118    if (cur == NULL) {
119        return FS_ERR_NOTFOUND;
120    }
121
122    if (cur->open) {
123        printf("already open\n");
124        return FS_ERR_INVALID_FH; //FIXME: proper error code
125    }
126
127    // we can open it
128    cur->open = true;
129
130    err = backends[cur->type].open(cur->backend_handle);
131    if (err_is_fail(err)) {
132        cur->open = false;
133        printf("failed to open\n");
134        return FS_ERR_INVALID_FH; // FIXME: better error code
135    }
136
137    struct blockdevfs_handle *handle = malloc(sizeof(struct blockdevfs_handle));
138    assert(handle != NULL);
139    handle->pos = 0;
140    handle->entry = cur;
141    *rethandle = handle;
142
143    VFS_BLK_DEBUG("blockdevfs_open: exiting\n");
144    return SYS_ERR_OK;
145}
146
147// create is not allowed in blockdevfs
148static errval_t create(void *st, const char *path, vfs_handle_t *rethandle)
149{
150    return open(st, path, rethandle);
151}
152
153// remove is not allowed in blockdevfs
154static errval_t blockdevfs_remove(void *st, const char *path)
155{
156    return FS_ERR_NOTFILE; //FIXME: more suitable error code
157}
158
159static errval_t read(void *st, vfs_handle_t handle, void *buffer, size_t bytes,
160        size_t *bytes_read)
161{
162    struct blockdevfs_handle *h = handle;
163    struct blockdev_entry *entry = h->entry;
164    VFS_BLK_DEBUG("blockdevfs: read %zu bytes at pos %zu into %p from type %d\n",
165            bytes, h->pos, buffer, entry->type);
166    VFS_BLK_DEBUG("blockdevfs: %p %p\n",
167            blockdevfs_ahci_read, backends[entry->type].read);
168    errval_t ret = backends[entry->type].read(entry->backend_handle, h->pos,
169            buffer, bytes, bytes_read);
170
171    if (err_is_ok(ret)) {
172        h->pos += *bytes_read;
173    }
174
175    return ret;
176}
177
178static errval_t write(void *st, vfs_handle_t handle, const void *buffer, size_t
179        bytes, size_t *bytes_written)
180{
181    struct blockdevfs_handle *h = handle;
182    struct blockdev_entry *entry = h->entry;
183    errval_t ret = backends[entry->type].write(entry->backend_handle, h->pos,
184            buffer, bytes, bytes_written);
185
186    if (err_is_ok(ret)) {
187        h->pos += *bytes_written;
188    }
189
190    return ret;
191}
192
193// truncate is not allowed in blockdevfs
194static errval_t blockdevfs_truncate(void *st, vfs_handle_t handle, size_t bytes)
195{
196    return FS_ERR_NOTFILE; //FIXME: more suitable error code
197}
198
199static errval_t tell(void *st, vfs_handle_t handle, size_t *pos)
200{
201    struct blockdevfs_handle *h = handle;
202    *pos = h->pos;
203    return SYS_ERR_OK;
204}
205
206static errval_t stat(void *st, vfs_handle_t inhandle, struct vfs_fileinfo *info)
207{
208    struct blockdevfs_handle *h = inhandle;
209
210    info->type = VFS_FILE;
211    info->size = h->entry->size;
212
213    return SYS_ERR_OK;
214}
215
216static errval_t seek(void *st, vfs_handle_t handle, enum vfs_seekpos whence,
217        off_t offset)
218{
219    struct blockdevfs_handle *h = handle;
220    struct vfs_fileinfo info;
221    errval_t err;
222
223    switch (whence) {
224    case VFS_SEEK_SET:
225        assert(offset >= 0);
226        h->pos = offset;
227        break;
228
229    case VFS_SEEK_CUR:
230        assert(offset >= 0 || -offset <= h->pos);
231        h->pos += offset;
232        break;
233
234    case VFS_SEEK_END:
235        err = stat(st, handle, &info);
236        if (err_is_fail(err)) {
237            return err;
238        }
239        assert(offset >= 0 || -offset <= info.size);
240        h->pos = info.size + offset;
241        break;
242
243    default:
244        USER_PANIC("invalid whence argument to blockdevfs seek");
245    }
246
247    return SYS_ERR_OK;
248}
249
250static errval_t close(void *st, vfs_handle_t inhandle)
251{
252    struct blockdevfs_handle *handle = inhandle;
253    struct blockdev_entry *entry = handle->entry;
254
255    errval_t ret = backends[entry->type].close(entry->backend_handle);
256
257    handle->entry->open = false;
258    free(handle);
259
260    return ret;
261}
262
263static errval_t opendir(void *st, const char *path, vfs_handle_t *rethandle)
264{
265    struct blockdevfs_dirhandle *handle =
266        calloc(1, sizeof(struct blockdevfs_dirhandle));
267    assert(handle != NULL);
268    handle->entry = entries_first;
269
270    *rethandle = handle;
271
272    return SYS_ERR_OK;
273}
274
275static errval_t dir_read_next(void *st, vfs_handle_t inhandle, char **retname,
276        struct vfs_fileinfo *info)
277{
278    struct blockdevfs_dirhandle *handle = inhandle;
279    if (handle->entry == NULL) {
280        return FS_ERR_INDEX_BOUNDS;
281    }
282
283    if (retname != NULL) {
284        *retname = strdup(handle->entry->path);
285    }
286
287    info->size = handle->entry->size;
288    info->type = VFS_FILE;
289
290    handle->entry = handle->entry->next;
291
292    return SYS_ERR_OK;
293}
294
295static errval_t closedir(void *st, vfs_handle_t dhandle)
296{
297    struct blockdevfs_dirhandle *handle = dhandle;
298    free(handle);
299    return SYS_ERR_OK;
300}
301
302// creating/removing dirs is not allowed in blockdevfs
303static errval_t mkdir(void *st, const char *path)
304{
305    return FS_ERR_NOTFILE; //FIXME: more suitable error code
306}
307
308static errval_t rmdir(void *st, const char *path)
309{
310    return FS_ERR_NOTFILE; //FIXME: more suitable error code
311}
312
313static errval_t flush(void *st, vfs_handle_t inhandle)
314{
315    struct blockdevfs_handle *handle = inhandle;
316    struct blockdev_entry *entry = handle->entry;
317
318    return backends[entry->type].flush(entry->backend_handle);
319}
320
321static struct vfs_ops blockdevfsops = {
322    .open = open,
323    .create = create,
324    .remove = blockdevfs_remove,
325    .read = read,
326    .write = write,
327    .truncate = blockdevfs_truncate,
328    .seek = seek,
329    .tell = tell,
330    .stat = stat,
331    .close = close,
332    .opendir = opendir,
333    .dir_read_next = dir_read_next,
334    .closedir = closedir,
335    .mkdir = mkdir,
336    .rmdir = rmdir,
337    .flush = flush,
338};
339
340errval_t vfs_blockdevfs_mount(const char *uri, void **retst, struct vfs_ops **retops)
341{
342    errval_t err;
343
344    // skip over protocol part of URI to get service name
345    char *service = strstr(uri, "://");
346    if (service == NULL) {
347        return VFS_ERR_BAD_URI;
348    }
349    service += 3;
350
351    if (*service == 0) {
352        // init all kinds of blockdevice sources
353        err = blockdevfs_ahci_init();       // AHCI
354        assert(err_is_ok(err));
355        err = blockdevfs_ata_init();
356        assert(err_is_ok(err));             // Flounder
357    } else if(!strcmp(service, "megaraid")) {
358        err = blockdevfs_megaraid_init();
359        assert(err_is_ok(err));
360    } else {
361        return VFS_ERR_BAD_URI;
362    }
363
364    *retops = &blockdevfsops;
365    *retst = NULL;
366
367    return SYS_ERR_OK;
368}
369