1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <inttypes.h>
6#include <fcntl.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/stat.h>
10
11#include <fbl/ref_ptr.h>
12#include <fbl/unique_ptr.h>
13#include <lib/fdio/namespace.h>
14#include <lib/fdio/vfs.h>
15#include <fs/vfs.h>
16#include <lib/async/dispatcher.h>
17#include <lib/memfs/cpp/vnode.h>
18#include <lib/memfs/memfs.h>
19#include <lib/sync/completion.h>
20#include <zircon/device/vfs.h>
21
22#include "dnode.h"
23
24struct memfs_filesystem {
25    memfs::Vfs vfs;
26
27    explicit memfs_filesystem(size_t pages_limit): vfs(pages_limit) { }
28};
29
30zx_status_t memfs_create_filesystem(async_dispatcher_t* dispatcher,
31                                    memfs_filesystem_t** out_fs,
32                                    zx_handle_t* out_root) {
33    return memfs_create_filesystem_with_page_limit(dispatcher, UINT64_MAX, out_fs, out_root);
34}
35
36zx_status_t memfs_create_filesystem_with_page_limit(async_dispatcher_t* dispatcher,
37                                                    size_t max_num_pages,
38                                                    memfs_filesystem_t** out_fs,
39                                                    zx_handle_t* out_root) {
40    ZX_DEBUG_ASSERT(dispatcher != nullptr);
41    ZX_DEBUG_ASSERT(out_fs != nullptr);
42    ZX_DEBUG_ASSERT(out_root != nullptr);
43
44    zx::channel client, server;
45    zx_status_t status = zx::channel::create(0, &client, &server);
46    if (status != ZX_OK) {
47        return status;
48    }
49
50    fbl::unique_ptr<memfs_filesystem_t> fs = fbl::make_unique<memfs_filesystem_t>(max_num_pages);
51    fs->vfs.SetDispatcher(dispatcher);
52
53    fbl::RefPtr<memfs::VnodeDir> root;
54    if ((status = memfs::CreateFilesystem("<tmp>", &fs->vfs, &root)) != ZX_OK) {
55        return status;
56    }
57    if ((status = fs->vfs.ServeDirectory(fbl::move(root), fbl::move(server))) != ZX_OK) {
58        return status;
59    }
60
61    *out_fs = fs.release();
62    *out_root = client.release();
63    return ZX_OK;
64}
65
66zx_status_t memfs_install_at(async_dispatcher_t* dispatcher, const char* path) {
67    return memfs_install_at_with_page_limit(dispatcher, UINT64_MAX, path);
68}
69
70zx_status_t memfs_install_at_with_page_limit(async_dispatcher_t* dispatcher,
71                                             size_t max_num_pages, const char* path) {
72    fdio_ns_t* ns;
73    zx_status_t status = fdio_ns_get_installed(&ns);
74    if (status != ZX_OK) {
75        return status;
76    }
77
78    memfs_filesystem_t* fs;
79    zx_handle_t root;
80    status = memfs_create_filesystem_with_page_limit(dispatcher, max_num_pages, &fs, &root);
81    if (status != ZX_OK) {
82        return status;
83    }
84
85    status = fdio_ns_bind(ns, path, root);
86    if (status != ZX_OK) {
87        memfs_free_filesystem(fs, nullptr);
88        zx_handle_close(root);
89        return status;
90    }
91
92    return ZX_OK;
93}
94
95void memfs_free_filesystem(memfs_filesystem_t* fs, sync_completion_t* unmounted) {
96    ZX_DEBUG_ASSERT(fs != nullptr);
97    fs->vfs.Shutdown([fs, unmounted](zx_status_t status) {
98        delete fs;
99        if (unmounted) {
100            sync_completion_signal(unmounted);
101        }
102    });
103}
104