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