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