bb0dc052dc30389f835573d6284181b627bee24c
[barrelfish] / lib / vfs / vfs.c
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
20 struct vfs_mount {
21     const char *mountpoint;
22     struct vfs_ops *ops;
23     void *st;
24     struct vfs_mount *next;
25 };
26
27 static struct vfs_mount *mounts;
28
29 static 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
53 static 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  */
89 errval_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         err = vfs_blockdevfs_mount(uri, &m->st, &m->ops);
154     } else if (strncmp(uri, "fat16", len) == 0) {
155         err = vfs_fat_mount(uri, &m->st, &m->ops);
156     } else if (strncmp(uri, "fat32", len) == 0) {
157         err = vfs_fat_mount(uri, &m->st, &m->ops);
158     } else {
159         debug_printf("VFS: unknown file system %.*s\n", (int)len, uri);
160         err = VFS_ERR_UNKNOWN_FILESYSTEM;
161     }
162
163     if (err_is_fail(err)) {
164         free(m);
165         free(mp);
166         return err;
167     }
168
169     // add to list of mounts
170     m->next = mounts;
171     mounts = m;
172
173     return SYS_ERR_OK;
174 }
175
176 /**
177  * \brief Unmount a filesystem from the local VFS
178  *
179  * \param mountpoint Fully-qualified absolute path to the existing mount-point
180  */
181 errval_t vfs_unmount(const char *mointpoint)
182 {
183     // TODO: ensure there are no live handles (ie. need refcount on open/close)
184     USER_PANIC("vfs_unmount NYI");
185 }
186
187 /**
188  * \brief Open the given file, failing if it doesn't exist
189  *
190  * \param path Fully-qualified absolute path
191  * \param handle Return handle, if call succeeds
192  */
193 errval_t vfs_open(const char *path, vfs_handle_t *handle)
194 {
195     const char *relpath = NULL;
196
197     // locate mount point
198     struct vfs_mount *m = find_mount(path, &relpath);
199     if (m == NULL) {
200         return FS_ERR_NOTFOUND;
201     }
202
203     // call fs ops func
204     assert(m->ops->open != NULL);
205     errval_t ret = m->ops->open(m->st, relpath, handle);
206
207     // update handle with mount pointer
208     if (err_is_ok(ret)) {
209         struct vfs_handle *h = *handle;
210         h->mount = m;
211     }
212
213     return ret;
214 }
215
216 /**
217  * \brief Open the given file, creating it if it doesn't already exist
218  *
219  * \param path Fully-qualified absolute path
220  * \param handle Return handle, if call succeeds
221  */
222 errval_t vfs_create(const char *path, vfs_handle_t *handle)
223 {
224     const char *relpath = NULL;
225
226     // locate mount point
227     struct vfs_mount *m = find_mount(path, &relpath);
228     if (m == NULL) {
229         return FS_ERR_NOTFOUND;
230     }
231
232     // call fs ops func
233     assert(m->ops != NULL);
234     assert(m->ops->create != NULL);
235     errval_t ret = m->ops->create(m->st, relpath, handle);
236
237     // update handle with mount pointer
238     if (err_is_ok(ret)) {
239         struct vfs_handle *h = *handle;
240         h->mount = m;
241     }
242
243     return ret;
244 }
245
246 /**
247  * \brief Remove the given file, fail if not present
248  *
249  * \param path Fully-qualified absolute path
250  */
251  errval_t vfs_remove(const char *path)
252 {
253     const char *relpath = NULL;
254
255     // locate mount point
256     struct vfs_mount *m = find_mount(path, &relpath);
257     if (m == NULL) {
258         return FS_ERR_NOTFOUND;
259     }
260
261     // call fs ops func
262     assert(m->ops->remove != NULL);
263     return m->ops->remove(m->st, relpath);
264 }
265
266 /**
267  * \brief Read from an open file handle
268  *
269  * \param handle Handle to an open file, returned from #vfs_open or #vfs_create
270  * \param buffer Pointer to buffer of at least #bytes where read data is placed
271  * \param bytes Maximum number of bytes to read
272  * \param bytes_read Return pointer containing number of bytes actually read
273  */
274 errval_t vfs_read(vfs_handle_t handle, void *buffer, size_t bytes,
275                   size_t *bytes_read)
276 {
277     struct vfs_handle *h = handle;
278     struct vfs_mount *m = h->mount;
279
280     assert(m->ops->read != NULL);
281     return m->ops->read(m->st, handle, buffer, bytes, bytes_read);
282 }
283
284 /**
285  * \brief Write to an open file handle
286  *
287  * \param handle Handle to an open file, returned from #vfs_open or #vfs_create
288  * \param buffer Pointer to buffer of #bytes containing data to write
289  * \param bytes Maximum number of bytes to write
290  * \param bytes_written Return pointer containing number of bytes actually written
291  */
292 errval_t vfs_write(vfs_handle_t handle, const void *buffer, size_t bytes,
293                    size_t *bytes_written)
294 {
295     struct vfs_handle *h = handle;
296     struct vfs_mount *m = h->mount;
297     assert(m->ops->write != NULL);
298     return m->ops->write(m->st, handle, buffer, bytes, bytes_written);
299 }
300
301 /**
302  * \brief Truncate an open file
303  *
304  * \param handle Handle to an open file, returned from #vfs_open or #vfs_create
305  * \param bytes New size of file
306  *
307  * If bytes is greater than the existing file size, the file is enlarged with
308  * zero bytes.
309  */
310 errval_t vfs_truncate(vfs_handle_t handle, size_t bytes)
311 {
312     struct vfs_handle *h = handle;
313     struct vfs_mount *m = h->mount;
314
315     assert(m->ops->truncate != NULL);
316     return m->ops->truncate(m->st, handle, bytes);
317 }
318
319 /**
320  * \brief Seek to a new position in an open file
321  *
322  * \param handle Handle to an open file, returned from #vfs_open or #vfs_create
323  * \param whence Determines interpretation of offset
324  * \param offset Offset in bytes from position specified by #whence
325  *
326  * See documentation of the enum #vfs_seekpos for the possible values of #whence.
327  */
328 errval_t vfs_seek(vfs_handle_t handle, enum vfs_seekpos whence, off_t offset)
329 {
330     struct vfs_handle *h = handle;
331     struct vfs_mount *m = h->mount;
332
333     assert(m->ops->seek != NULL);
334     return m->ops->seek(m->st, handle, whence, offset);
335 }
336
337 /**
338  * \brief Return the current file pointer of an open file
339  *
340  * \param handle Handle to an open file, returned from #vfs_open or #vfs_create
341  * \param pos Return pointer of current position in file
342  */
343 errval_t vfs_tell(vfs_handle_t handle, size_t *pos)
344 {
345     struct vfs_handle *h = handle;
346     struct vfs_mount *m = h->mount;
347
348     assert(m->ops->tell != NULL);
349     return m->ops->tell(m->st, handle, pos);
350 }
351
352 /**
353  * \brief Return the metadata properties of an open file
354  *
355  * \param handle Handle to an open file, returned from #vfs_open or #vfs_create
356  * \param info Pointer to #vfs_fileinfo structure that will be filled in
357  */
358 errval_t vfs_stat(vfs_handle_t handle, struct vfs_fileinfo *info)
359 {
360     struct vfs_handle *h = handle;
361     struct vfs_mount *m = h->mount;
362
363     assert(m->ops->stat != NULL);
364     return m->ops->stat(m->st, handle, info);
365 }
366
367 /**
368  * \brief Flush file to disk
369  * \param handle Handle to an open file
370  */
371 errval_t vfs_flush(vfs_handle_t handle)
372 {
373     struct vfs_handle *h = handle;
374     struct vfs_mount *m = h->mount;
375     if (m->ops->flush) {
376         return m->ops->flush(m->st, handle);
377     }
378     else {
379         return VFS_ERR_NOT_SUPPORTED;
380     }
381 }
382
383 /**
384  * \brief Close an open file, freeing any associated local state
385  *
386  * \param handle Handle to an open file, returned from #vfs_open or #vfs_create
387  */
388 errval_t vfs_close(vfs_handle_t handle)
389 {
390     struct vfs_handle *h = handle;
391     struct vfs_mount *m = h->mount;
392
393     assert(m->ops->close != NULL);
394     return m->ops->close(m->st, handle);
395 }
396
397 /**
398  * \brief Open the given directory
399  *
400  * \param path Fully-qualified absolute path to directory
401  * \param dhandle Return handle, if call succeeds
402  *
403  * This call fails if the path does not exist or is not a directory.
404  */
405 errval_t vfs_opendir(const char *path, vfs_handle_t *dhandle)
406 {
407     const char *relpath = NULL;
408
409     // locate mount point
410     struct vfs_mount *m = find_mount(path, &relpath);
411     if (m == NULL) {
412         return FS_ERR_NOTFOUND;
413     }
414
415     // call fs ops func
416     assert(m->ops->opendir != NULL);
417     errval_t ret = m->ops->opendir(m->st, relpath, dhandle);
418
419     // update handle with mount pointer
420     if (err_is_ok(ret)) {
421         struct vfs_handle *h = *dhandle;
422         h->mount = m;
423     }
424
425     return ret;
426 }
427
428 /**
429  * \brief Return information about the next entry in the given directory
430  *
431  * \param dhandle Directory handle returned from #vfs_opendir
432  * \param name Return pointer to string, filled-in if non-NULL with a pointer to
433  *      a malloced buffer containing the name of the next entry in the directory.
434  *      This buffer must be freed by the caller.
435  * \param info Optional pointer to #vfs_fileinfo structure that will be filled
436  *      in (if non-NULL) with metadata for the given entry.
437  *
438  * This call fails if the previous entry returned was the last, or if the
439  * directory is empty. To return the contents again, the directory must be
440  * closed and re-opened (ie. it is not possible to seek in the directory).
441  */
442 errval_t vfs_dir_read_next(vfs_handle_t dhandle, char **name,
443                            struct vfs_fileinfo *info)
444 {
445     struct vfs_handle *handle = dhandle;
446     struct vfs_mount *m = handle->mount;
447
448     assert(m->ops->dir_read_next != NULL);
449     return m->ops->dir_read_next(m->st, dhandle, name, info);
450 }
451
452 /**
453  * \brief Close a directory handle obtained from #vfs_opendir
454  *
455  * \param dhandle Directory handle returned from #vfs_opendir
456  */
457 errval_t vfs_closedir(vfs_handle_t dhandle)
458 {
459     struct vfs_handle *handle = dhandle;
460     struct vfs_mount *m = handle->mount;
461
462     assert(m->ops->closedir != NULL);
463     return m->ops->closedir(m->st, dhandle);
464 }
465
466 /**
467  * \brief Create a new empty directory
468  *
469  * \param path Fully-qualified absolute path to the new directory
470  *
471  * This call fails if the parent directory does not exist, or if the given
472  * directory already exists (as either a file or directory).
473  */
474 errval_t vfs_mkdir(const char *path)
475 {
476     const char *relpath = NULL;
477
478     // locate mount point
479     struct vfs_mount *m = find_mount(path, &relpath);
480     if (m == NULL) {
481         return FS_ERR_NOTFOUND;
482     }
483
484     // call fs ops func
485     assert(m->ops->mkdir != NULL);
486     return m->ops->mkdir(m->st, relpath);
487 }
488
489 /**
490  * \brief Remove an existing empty directory
491  *
492  * \param path Fully-qualified absolute path to the directory
493  *
494  * This call fails if the given directory already exists and is not a directory,
495  * or is a directory but is not empty.
496  */
497 errval_t vfs_rmdir(const char *path)
498 {
499     const char *relpath = NULL;
500
501     // locate mount point
502     struct vfs_mount *m = find_mount(path, &relpath);
503     if (m == NULL) {
504         return FS_ERR_NOTFOUND;
505     }
506
507     // check if this is the mountpoint itself
508     if (*relpath == '\0') {
509         return VFS_ERR_MOUNTPOINT_IN_USE;
510     }
511
512     // call fs ops func
513     assert(m->ops->rmdir != NULL);
514     return m->ops->rmdir(m->st, relpath);
515 }
516
517 static uint8_t vfs_initialized = 0;
518
519 /**
520  * \brief Initialise the VFS library
521  *
522  * This call initialises the VFS library. It must be called prior to any
523  * other VFS functions being used. It doesn't need to be a constructor
524  * We call it explicitly..
525  */
526 void vfs_init(void)
527 {
528     if (vfs_initialized) {
529         return;
530     }
531
532     assert(mounts == NULL);
533     errval_t err;
534
535     // init libc glue
536     vfs_fopen_init();
537
538     // mount ramfs on root, as a sensible default setup for the time being
539     err = vfs_mount("/", "ramfs://");
540     if (err_is_fail(err)) {
541         DEBUG_ERR(err, "error mounting ramfs");
542         // continue anyway...
543     }
544
545     vfs_initialized = 0x1;
546 }
547
548
549
550 void vfs_dummy(void);
551
552 __attribute__((used))
553 void vfs_dummy(void) {
554     //    debug_printf("vfs_dummy\n");
555 }