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