refactor: add a simple ramfs for rootfs. Twifs should have more specific job in future.
[lunaix-os.git] / lunaix-os / kernel / fs / mount.c
1 #include <lunaix/fs.h>
2 #include <lunaix/mm/valloc.h>
3 #include <lunaix/process.h>
4 #include <lunaix/types.h>
5
6 static struct llist_header all_mnts = { .next = &all_mnts, .prev = &all_mnts };
7
8 struct v_mount*
9 vfs_create_mount(struct v_mount* parent, struct v_dnode* mnt_point)
10 {
11     struct v_mount* mnt = vzalloc(sizeof(struct v_mount));
12     if (!mnt) {
13         return NULL;
14     }
15
16     llist_init_head(&mnt->submnts);
17     llist_append(&all_mnts, &mnt->list);
18     mutex_init(&mnt->lock);
19
20     mnt_mkbusy(parent);
21     mnt->parent = parent;
22     mnt->mnt_point = mnt_point;
23     mnt->super_block = mnt_point->super_block;
24
25     if (parent) {
26         mutex_lock(&mnt->parent->lock);
27         llist_append(&parent->submnts, &mnt->sibmnts);
28         mutex_unlock(&mnt->parent->lock);
29     }
30
31     atomic_fetch_add(&mnt_point->ref_count, 1);
32
33     return mnt;
34 }
35
36 int
37 __vfs_do_unmount(struct v_mount* mnt)
38 {
39     int errno = 0;
40     struct v_superblock* sb = mnt->super_block;
41
42     if ((errno = sb->fs->unmount(sb))) {
43         return errno;
44     }
45
46     llist_delete(&mnt->list);
47     llist_delete(&mnt->sibmnts);
48
49     // detached the inodes from cache, and let lru policy to recycle them
50     for (size_t i = 0; i < VFS_HASHTABLE_SIZE; i++) {
51         struct hbucket* bucket = &sb->i_cache[i];
52         if (!bucket) {
53             continue;
54         }
55         bucket->head->pprev = 0;
56     }
57
58     mnt_chillax(mnt->parent);
59
60     vfs_sb_free(sb);
61     vfs_d_free(mnt->mnt_point);
62     vfree(mnt);
63
64     return errno;
65 }
66
67 void
68 mnt_mkbusy(struct v_mount* mnt)
69 {
70     while (mnt) {
71         mutex_lock(&mnt->lock);
72         mnt->busy_counter++;
73         mutex_unlock(&mnt->lock);
74
75         mnt = mnt->parent;
76     }
77 }
78
79 void
80 mnt_chillax(struct v_mount* mnt)
81 {
82     while (mnt) {
83         mutex_lock(&mnt->lock);
84         mnt->busy_counter--;
85         mutex_unlock(&mnt->lock);
86
87         mnt = mnt->parent;
88     }
89 }
90
91 int
92 vfs_mount_root(const char* fs_name, struct device* device)
93 {
94     int errno = 0;
95     if (vfs_sysroot->mnt && (errno = vfs_unmount_at(vfs_sysroot))) {
96         return errno;
97     }
98     return vfs_mount_at(fs_name, device, vfs_sysroot);
99 }
100
101 int
102 vfs_mount(const char* target, const char* fs_name, struct device* device)
103 {
104     int errno;
105     struct v_dnode* mnt;
106
107     if (!(errno =
108             vfs_walk(__current->cwd, target, &mnt, NULL, VFS_WALK_MKPARENT))) {
109         errno = vfs_mount_at(fs_name, device, mnt);
110     }
111
112     return errno;
113 }
114
115 int
116 vfs_unmount(const char* target)
117 {
118     int errno;
119     struct v_dnode* mnt;
120
121     if (!(errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
122         errno = vfs_unmount_at(mnt);
123     }
124
125     return errno;
126 }
127
128 int
129 vfs_mount_at(const char* fs_name,
130              struct device* device,
131              struct v_dnode* mnt_point)
132 {
133     if (mnt_point->inode && !(mnt_point->inode->itype & VFS_IFDIR)) {
134         return ENOTDIR;
135     }
136
137     struct filesystem* fs = fsm_get(fs_name);
138     if (!fs) {
139         return ENODEV;
140     }
141
142     struct v_mount* parent_mnt = mnt_point->mnt;
143     struct v_superblock* sb = vfs_sb_alloc();
144     sb->dev = device;
145
146     int errno = 0;
147     if (!(errno = fs->mount(sb, mnt_point))) {
148         mnt_point->super_block = sb;
149         sb->fs = fs;
150         sb->root = mnt_point;
151
152         if (!(mnt_point->mnt = vfs_create_mount(parent_mnt, mnt_point))) {
153             errno = ENOMEM;
154             goto cleanup;
155         }
156     } else {
157         goto cleanup;
158     }
159
160     return errno;
161
162 cleanup:
163     vfs_sb_free(sb);
164     return errno;
165 }
166
167 int
168 vfs_unmount_at(struct v_dnode* mnt_point)
169 {
170     int errno = 0;
171     struct v_superblock* sb = mnt_point->super_block;
172     if (!sb) {
173         return EINVAL;
174     }
175
176     if (sb->root != mnt_point) {
177         return EINVAL;
178     }
179
180     if (mnt_point->mnt->busy_counter) {
181         return EBUSY;
182     }
183
184     if (!(errno = __vfs_do_unmount(mnt_point->mnt))) {
185         atomic_fetch_sub(&mnt_point->ref_count, 1);
186     }
187
188     return errno;
189 }
190
191 __DEFINE_LXSYSCALL3(int,
192                     mount,
193                     const char*,
194                     source,
195                     const char*,
196                     target,
197                     const char*,
198                     fstype)
199 {
200     struct v_dnode *dev, *mnt;
201     int errno = 0;
202
203     if ((errno = vfs_walk(__current->cwd, source, &dev, NULL, 0))) {
204         goto done;
205     }
206
207     if ((errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
208         goto done;
209     }
210
211     if (mnt->ref_count > 1) {
212         errno = EBUSY;
213         goto done;
214     }
215
216     // By our convention.
217     // XXX could we do better?
218     struct device* device = (struct device*)dev->data;
219
220     if (!(dev->inode->itype & VFS_IFVOLDEV) || !device) {
221         errno = ENOTDEV;
222         goto done;
223     }
224
225     errno = vfs_mount_at(fstype, device, mnt);
226
227 done:
228     return DO_STATUS(errno);
229 }
230
231 __DEFINE_LXSYSCALL1(int, unmount, const char*, target)
232 {
233     return vfs_unmount(target);
234 }