arrakis merge: remove megaraid dependency for arm archs
[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 #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  */
193 errval_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  */
205 errval_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  */
234 errval_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  */
286 errval_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  */
304 errval_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  */
322 errval_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  */
340 errval_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  */
355 errval_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  */
370 errval_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  */
383 errval_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  */
400 errval_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  */
417 errval_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  */
454 errval_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  */
469 errval_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  */
486 errval_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  */
509 errval_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
529 static 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  */
538 void 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
562 void vfs_dummy(void);
563
564 __attribute__((used))
565 void vfs_dummy(void) {
566     //    debug_printf("vfs_dummy\n");
567 }