1/* 2 * Copyright (c) 2009, 2010, 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 <stdlib.h> 12#include <string.h> 13#include <barrelfish/barrelfish.h> 14#include <vfs/vfs.h> 15#include <vfs/vfs_path.h> 16 17#include "vfs_ops.h" 18#include "vfs_backends.h" 19 20struct vfs_mount { 21 const char *mountpoint; 22 struct vfs_ops *ops; 23 void *st; 24 struct vfs_mount *next; 25}; 26 27static struct vfs_mount *mounts; 28 29static bool mount_matches(const char *mount, const char *path, size_t *matchlen) 30{ 31 size_t len = 0; 32 33 // both inputs must be absolute paths 34 assert(mount != NULL && mount[0] == VFS_PATH_SEP); 35 assert(path != NULL && path[0] == VFS_PATH_SEP); 36 37 while (mount[len] != '\0' && mount[len] == path[len]) { 38 len++; 39 } 40 41 if (mount[len] == '\0' && 42 (len == 1 /*root*/ || path[len] == '\0' || path[len] == VFS_PATH_SEP)) { 43 assert(matchlen != NULL); 44 *matchlen = len; 45 return true; 46 } else { 47 return false; 48 } 49} 50 51/// find the closest matching mountpoint for a given path 52/// return mount, and pointer (within input string) to relative path 53static struct vfs_mount *find_mount(const char *path, const char **ret_relpath) 54{ 55 struct vfs_mount *match = NULL; 56 size_t len, matchlen = 0; 57 58 // path must be absolute 59 if (path == NULL || path[0] != VFS_PATH_SEP) { 60 return NULL; 61 } 62 63 for (struct vfs_mount *m = mounts; m != NULL; m = m->next) { 64 if (mount_matches(m->mountpoint, path, &len) 65 && (match == NULL || len > matchlen)) { 66 match = m; 67 matchlen = len; 68 } 69 } 70 71 if (match != NULL && ret_relpath != NULL) { 72 *ret_relpath = &path[matchlen]; 73 } 74 75 return match; 76} 77 78/** 79 * \brief Mount a filesystem into the local VFS 80 * 81 * \param mountpoint Fully-qualified absolute path to the mount-point 82 * Must be a directory in the existing VFS which is not already a mount-point 83 * 84 * \param uri URI of source file system to mount. 85 * Currently-supported are: 86 * ramfs://[servicename] where servicename is registered in the name service 87 * nfs://hostip/path 88 */ 89errval_t vfs_mount(const char *mountpoint, const char *uri) 90{ 91 errval_t err; 92 93 // copy mountpoint and normalise it 94 assert(mountpoint != NULL); 95 char *mp = strdup(mountpoint); 96 assert(mp != NULL); 97 vfs_path_normalise(mp); 98 99 // sanity-check mountpoint: must start at root and not have .. in it 100 if (mp[0] != VFS_PATH_SEP || strncmp(mp, "/../", 4) == 0) { 101 free(mp); 102 return VFS_ERR_BAD_MOUNTPOINT; 103 } 104 105 // sanity-check mountpoint 106 // if this is the first mount, it must be for the root, otherwise it must 107 // not duplicate an existing mount, and the mount-point must exist 108 if (mounts == NULL) { 109 if (strcmp(mp, VFS_PATH_SEP_STR) != 0) { 110 free(mp); 111 return VFS_ERR_BAD_MOUNTPOINT; 112 } 113 } else { 114 struct vfs_mount *parent = find_mount(mp, NULL); 115 assert(parent != NULL); // root should always have matched 116 if (strcmp(parent->mountpoint, mp) == 0) { 117 free(mp); 118 return VFS_ERR_MOUNTPOINT_IN_USE; 119 } 120 121 // check for existence of mountpoint by attempting to open it 122 vfs_handle_t tmp; 123 err = vfs_opendir(mp, &tmp); 124 if (err_is_fail(err)) { 125 free(mp); 126 return err_push(err, VFS_ERR_MOUNTPOINT_NOTFOUND); 127 } 128 vfs_closedir(tmp); 129 } 130 131 // parse protocol part of URI 132 char *pos = strstr(uri, "://"); 133 if (pos == NULL) { 134 free(mp); 135 return VFS_ERR_BAD_URI; 136 } 137 138 struct vfs_mount *m = malloc(sizeof(struct vfs_mount)); 139 assert(m != NULL); 140 141 m->mountpoint = mp; 142 143 size_t len = pos - uri; 144 if (strncmp(uri, "nfs", len) == 0) { 145#ifndef DISABLE_NFS 146 err = vfs_nfs_mount(uri, &m->st, &m->ops); 147#else 148 err = VFS_ERR_UNKNOWN_FILESYSTEM; 149#endif 150 } else if (strncmp(uri, "ramfs", len) == 0) { 151 err = vfs_ramfs_mount(uri, &m->st, &m->ops); 152 } else if (strncmp(uri, "blockdevfs", len) == 0) { 153#ifndef DISABLE_BLOCKDEV 154 err = vfs_blockdevfs_mount(uri, &m->st, &m->ops); 155#else 156 err = VFS_ERR_UNKNOWN_FILESYSTEM; 157#endif 158 } else if (strncmp(uri, "fat16", len) == 0) { 159#ifndef DISABLE_BLOCKDEV 160 err = vfs_fat_mount(uri, &m->st, &m->ops); 161#else 162 err = VFS_ERR_UNKNOWN_FILESYSTEM; 163#endif 164 } else if (strncmp(uri, "fat32", len) == 0) { 165#ifndef DISABLE_BLOCKDEV 166 err = vfs_fat_mount(uri, &m->st, &m->ops); 167#else 168 err = VFS_ERR_UNKNOWN_FILESYSTEM; 169#endif 170 } else { 171 debug_printf("VFS: unknown file system %.*s\n", (int)len, uri); 172 err = VFS_ERR_UNKNOWN_FILESYSTEM; 173 } 174 175 if (err_is_fail(err)) { 176 free(m); 177 free(mp); 178 return err; 179 } 180 181 // add to list of mounts 182 m->next = mounts; 183 mounts = m; 184 185 return SYS_ERR_OK; 186} 187 188/** 189 * \brief Unmount a filesystem from the local VFS 190 * 191 * \param mountpoint Fully-qualified absolute path to the existing mount-point 192 */ 193errval_t vfs_unmount(const char *mointpoint) 194{ 195 // TODO: ensure there are no live handles (ie. need refcount on open/close) 196 USER_PANIC("vfs_unmount NYI"); 197} 198 199/** 200 * \brief Open the given file, failing if it doesn't exist 201 * 202 * \param path Fully-qualified absolute path 203 * \param handle Return handle, if call succeeds 204 */ 205errval_t vfs_open(const char *path, vfs_handle_t *handle) 206{ 207 const char *relpath = NULL; 208 209 // locate mount point 210 struct vfs_mount *m = find_mount(path, &relpath); 211 if (m == NULL) { 212 return FS_ERR_NOTFOUND; 213 } 214 215 // call fs ops func 216 assert(m->ops->open != NULL); 217 errval_t ret = m->ops->open(m->st, relpath, handle); 218 219 // update handle with mount pointer 220 if (err_is_ok(ret)) { 221 struct vfs_handle *h = *handle; 222 h->mount = m; 223 } 224 225 return ret; 226} 227 228/** 229 * \brief Open the given file, creating it if it doesn't already exist 230 * 231 * \param path Fully-qualified absolute path 232 * \param handle Return handle, if call succeeds 233 */ 234errval_t vfs_create(const char *path, vfs_handle_t *handle) 235{ 236 const char *relpath = NULL; 237 238 // locate mount point 239 struct vfs_mount *m = find_mount(path, &relpath); 240 if (m == NULL) { 241 return FS_ERR_NOTFOUND; 242 } 243 244 // call fs ops func 245 assert(m->ops != NULL); 246 assert(m->ops->create != NULL); 247 errval_t ret = m->ops->create(m->st, relpath, handle); 248 249 // update handle with mount pointer 250 if (err_is_ok(ret)) { 251 struct vfs_handle *h = *handle; 252 h->mount = m; 253 } 254 255 return ret; 256} 257 258/** 259 * \brief Remove the given file, fail if not present 260 * 261 * \param path Fully-qualified absolute path 262 */ 263 errval_t vfs_remove(const char *path) 264{ 265 const char *relpath = NULL; 266 267 // locate mount point 268 struct vfs_mount *m = find_mount(path, &relpath); 269 if (m == NULL) { 270 return FS_ERR_NOTFOUND; 271 } 272 273 // call fs ops func 274 assert(m->ops->remove != NULL); 275 return m->ops->remove(m->st, relpath); 276} 277 278/** 279 * \brief Read from an open file handle 280 * 281 * \param handle Handle to an open file, returned from #vfs_open or #vfs_create 282 * \param buffer Pointer to buffer of at least #bytes where read data is placed 283 * \param bytes Maximum number of bytes to read 284 * \param bytes_read Return pointer containing number of bytes actually read 285 */ 286errval_t vfs_read(vfs_handle_t handle, void *buffer, size_t bytes, 287 size_t *bytes_read) 288{ 289 struct vfs_handle *h = handle; 290 struct vfs_mount *m = h->mount; 291 292 assert(m->ops->read != NULL); 293 return m->ops->read(m->st, handle, buffer, bytes, bytes_read); 294} 295 296/** 297 * \brief Write to an open file handle 298 * 299 * \param handle Handle to an open file, returned from #vfs_open or #vfs_create 300 * \param buffer Pointer to buffer of #bytes containing data to write 301 * \param bytes Maximum number of bytes to write 302 * \param bytes_written Return pointer containing number of bytes actually written 303 */ 304errval_t vfs_write(vfs_handle_t handle, const void *buffer, size_t bytes, 305 size_t *bytes_written) 306{ 307 struct vfs_handle *h = handle; 308 struct vfs_mount *m = h->mount; 309 assert(m->ops->write != NULL); 310 return m->ops->write(m->st, handle, buffer, bytes, bytes_written); 311} 312 313/** 314 * \brief Truncate an open file 315 * 316 * \param handle Handle to an open file, returned from #vfs_open or #vfs_create 317 * \param bytes New size of file 318 * 319 * If bytes is greater than the existing file size, the file is enlarged with 320 * zero bytes. 321 */ 322errval_t vfs_truncate(vfs_handle_t handle, size_t bytes) 323{ 324 struct vfs_handle *h = handle; 325 struct vfs_mount *m = h->mount; 326 327 assert(m->ops->truncate != NULL); 328 return m->ops->truncate(m->st, handle, bytes); 329} 330 331/** 332 * \brief Seek to a new position in an open file 333 * 334 * \param handle Handle to an open file, returned from #vfs_open or #vfs_create 335 * \param whence Determines interpretation of offset 336 * \param offset Offset in bytes from position specified by #whence 337 * 338 * See documentation of the enum #vfs_seekpos for the possible values of #whence. 339 */ 340errval_t vfs_seek(vfs_handle_t handle, enum vfs_seekpos whence, off_t offset) 341{ 342 struct vfs_handle *h = handle; 343 struct vfs_mount *m = h->mount; 344 345 assert(m->ops->seek != NULL); 346 return m->ops->seek(m->st, handle, whence, offset); 347} 348 349/** 350 * \brief Return the current file pointer of an open file 351 * 352 * \param handle Handle to an open file, returned from #vfs_open or #vfs_create 353 * \param pos Return pointer of current position in file 354 */ 355errval_t vfs_tell(vfs_handle_t handle, size_t *pos) 356{ 357 struct vfs_handle *h = handle; 358 struct vfs_mount *m = h->mount; 359 360 assert(m->ops->tell != NULL); 361 return m->ops->tell(m->st, handle, pos); 362} 363 364/** 365 * \brief Return the metadata properties of an open file 366 * 367 * \param handle Handle to an open file, returned from #vfs_open or #vfs_create 368 * \param info Pointer to #vfs_fileinfo structure that will be filled in 369 */ 370errval_t vfs_stat(vfs_handle_t handle, struct vfs_fileinfo *info) 371{ 372 struct vfs_handle *h = handle; 373 struct vfs_mount *m = h->mount; 374 375 assert(m->ops->stat != NULL); 376 return m->ops->stat(m->st, handle, info); 377} 378 379/** 380 * \brief Flush file to disk 381 * \param handle Handle to an open file 382 */ 383errval_t vfs_flush(vfs_handle_t handle) 384{ 385 struct vfs_handle *h = handle; 386 struct vfs_mount *m = h->mount; 387 if (m->ops->flush) { 388 return m->ops->flush(m->st, handle); 389 } 390 else { 391 return VFS_ERR_NOT_SUPPORTED; 392 } 393} 394 395/** 396 * \brief Close an open file, freeing any associated local state 397 * 398 * \param handle Handle to an open file, returned from #vfs_open or #vfs_create 399 */ 400errval_t vfs_close(vfs_handle_t handle) 401{ 402 struct vfs_handle *h = handle; 403 struct vfs_mount *m = h->mount; 404 405 assert(m->ops->close != NULL); 406 return m->ops->close(m->st, handle); 407} 408 409/** 410 * \brief Open the given directory 411 * 412 * \param path Fully-qualified absolute path to directory 413 * \param dhandle Return handle, if call succeeds 414 * 415 * This call fails if the path does not exist or is not a directory. 416 */ 417errval_t vfs_opendir(const char *path, vfs_handle_t *dhandle) 418{ 419 const char *relpath = NULL; 420 421 // locate mount point 422 struct vfs_mount *m = find_mount(path, &relpath); 423 if (m == NULL) { 424 return FS_ERR_NOTFOUND; 425 } 426 427 // call fs ops func 428 assert(m->ops->opendir != NULL); 429 errval_t ret = m->ops->opendir(m->st, relpath, dhandle); 430 431 // update handle with mount pointer 432 if (err_is_ok(ret)) { 433 struct vfs_handle *h = *dhandle; 434 h->mount = m; 435 } 436 437 return ret; 438} 439 440/** 441 * \brief Return information about the next entry in the given directory 442 * 443 * \param dhandle Directory handle returned from #vfs_opendir 444 * \param name Return pointer to string, filled-in if non-NULL with a pointer to 445 * a malloced buffer containing the name of the next entry in the directory. 446 * This buffer must be freed by the caller. 447 * \param info Optional pointer to #vfs_fileinfo structure that will be filled 448 * in (if non-NULL) with metadata for the given entry. 449 * 450 * This call fails if the previous entry returned was the last, or if the 451 * directory is empty. To return the contents again, the directory must be 452 * closed and re-opened (ie. it is not possible to seek in the directory). 453 */ 454errval_t vfs_dir_read_next(vfs_handle_t dhandle, char **name, 455 struct vfs_fileinfo *info) 456{ 457 struct vfs_handle *handle = dhandle; 458 struct vfs_mount *m = handle->mount; 459 460 assert(m->ops->dir_read_next != NULL); 461 return m->ops->dir_read_next(m->st, dhandle, name, info); 462} 463 464/** 465 * \brief Close a directory handle obtained from #vfs_opendir 466 * 467 * \param dhandle Directory handle returned from #vfs_opendir 468 */ 469errval_t vfs_closedir(vfs_handle_t dhandle) 470{ 471 struct vfs_handle *handle = dhandle; 472 struct vfs_mount *m = handle->mount; 473 474 assert(m->ops->closedir != NULL); 475 return m->ops->closedir(m->st, dhandle); 476} 477 478/** 479 * \brief Create a new empty directory 480 * 481 * \param path Fully-qualified absolute path to the new directory 482 * 483 * This call fails if the parent directory does not exist, or if the given 484 * directory already exists (as either a file or directory). 485 */ 486errval_t vfs_mkdir(const char *path) 487{ 488 const char *relpath = NULL; 489 490 // locate mount point 491 struct vfs_mount *m = find_mount(path, &relpath); 492 if (m == NULL) { 493 return FS_ERR_NOTFOUND; 494 } 495 496 // call fs ops func 497 assert(m->ops->mkdir != NULL); 498 return m->ops->mkdir(m->st, relpath); 499} 500 501/** 502 * \brief Remove an existing empty directory 503 * 504 * \param path Fully-qualified absolute path to the directory 505 * 506 * This call fails if the given directory already exists and is not a directory, 507 * or is a directory but is not empty. 508 */ 509errval_t vfs_rmdir(const char *path) 510{ 511 const char *relpath = NULL; 512 513 // locate mount point 514 struct vfs_mount *m = find_mount(path, &relpath); 515 if (m == NULL) { 516 return FS_ERR_NOTFOUND; 517 } 518 519 // check if this is the mountpoint itself 520 if (*relpath == '\0') { 521 return VFS_ERR_MOUNTPOINT_IN_USE; 522 } 523 524 // call fs ops func 525 assert(m->ops->rmdir != NULL); 526 return m->ops->rmdir(m->st, relpath); 527} 528 529static uint8_t vfs_initialized = 0; 530 531/** 532 * \brief Initialise the VFS library 533 * 534 * This call initialises the VFS library. It must be called prior to any 535 * other VFS functions being used. It doesn't need to be a constructor 536 * We call it explicitly.. 537 */ 538void vfs_init(void) 539{ 540 if (vfs_initialized) { 541 return; 542 } 543 544 assert(mounts == NULL); 545 errval_t err; 546 547 // init libc glue 548 vfs_fopen_init(); 549 550 // mount ramfs on root, as a sensible default setup for the time being 551 err = vfs_mount("/", "ramfs://"); 552 if (err_is_fail(err)) { 553 DEBUG_ERR(err, "error mounting ramfs"); 554 // continue anyway... 555 } 556 557 vfs_initialized = 0x1; 558} 559 560 561 562void vfs_dummy(void); 563 564__attribute__((used)) 565void vfs_dummy(void) { 566 // debug_printf("vfs_dummy\n"); 567} 568