feat: capability list to enable greater flexibility of devices
[lunaix-os.git] / lunaix-os / kernel / fs / mount.c
1 #include <lunaix/foptions.h>
2 #include <lunaix/fs.h>
3 #include <lunaix/mm/valloc.h>
4 #include <lunaix/process.h>
5 #include <lunaix/spike.h>
6 #include <lunaix/syscall_utils.h>
7 #include <lunaix/syslog.h>
8 #include <lunaix/types.h>
9
10 LOG_MODULE("fs")
11
12 struct llist_header all_mnts = { .next = &all_mnts, .prev = &all_mnts };
13
14 struct v_mount*
15 vfs_create_mount(struct v_mount* parent, struct v_dnode* mnt_point)
16 {
17     struct v_mount* mnt = vzalloc(sizeof(struct v_mount));
18     if (!mnt) {
19         return NULL;
20     }
21
22     llist_init_head(&mnt->submnts);
23     llist_append(&all_mnts, &mnt->list);
24     mutex_init(&mnt->lock);
25
26     mnt->parent = parent;
27     mnt->mnt_point = mnt_point;
28     mnt->super_block = mnt_point->super_block;
29
30     if (parent) {
31         mnt_mkbusy(parent);
32         mutex_lock(&mnt->parent->lock);
33         llist_append(&parent->submnts, &mnt->sibmnts);
34         mutex_unlock(&mnt->parent->lock);
35     }
36     
37     atomic_fetch_add(&mnt_point->ref_count, 1);
38
39     return mnt;
40 }
41
42 int
43 __vfs_do_unmount(struct v_mount* mnt)
44 {
45     int errno = 0;
46     struct v_superblock* sb = mnt->super_block;
47
48     if ((errno = sb->fs->unmount(sb))) {
49         return errno;
50     }
51
52     llist_delete(&mnt->list);
53     llist_delete(&mnt->sibmnts);
54
55     // detached the inodes from cache, and let lru policy to recycle them
56     for (size_t i = 0; i < VFS_HASHTABLE_SIZE; i++) {
57         struct hbucket* bucket = &sb->i_cache[i];
58         if (!bucket) {
59             continue;
60         }
61         bucket->head->pprev = 0;
62     }
63
64     mnt_chillax(mnt->parent);
65
66     mnt->mnt_point->mnt = mnt->parent;
67
68     vfs_sb_free(sb);
69     atomic_fetch_sub(&mnt->mnt_point->ref_count, 1);
70     vfree(mnt);
71
72     return errno;
73 }
74
75 void
76 mnt_mkbusy(struct v_mount* mnt)
77 {
78     mutex_lock(&mnt->lock);
79     mnt->busy_counter++;
80     mutex_unlock(&mnt->lock);
81 }
82
83 void
84 mnt_chillax(struct v_mount* mnt)
85 {
86     mutex_lock(&mnt->lock);
87     mnt->busy_counter--;
88     mutex_unlock(&mnt->lock);
89 }
90
91 int
92 vfs_mount_root(const char* fs_name, struct device* device)
93 {
94     extern struct v_dnode* vfs_sysroot;
95     int errno = 0;
96     if (vfs_sysroot->mnt && (errno = vfs_unmount_at(vfs_sysroot))) {
97         return errno;
98     }
99     return vfs_mount_at(fs_name, device, vfs_sysroot, 0);
100 }
101
102 int
103 vfs_mount(const char* target,
104           const char* fs_name,
105           struct device* device,
106           int options)
107 {
108     int errno;
109     struct v_dnode* mnt;
110
111     if (!(errno =
112             vfs_walk(__current->cwd, target, &mnt, NULL, VFS_WALK_MKPARENT))) {
113         errno = vfs_mount_at(fs_name, device, mnt, options);
114     }
115
116     return errno;
117 }
118
119 int
120 vfs_unmount(const char* target)
121 {
122     int errno;
123     struct v_dnode* mnt;
124
125     if (!(errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
126         errno = vfs_unmount_at(mnt);
127     }
128
129     return errno;
130 }
131
132 int
133 vfs_mount_at(const char* fs_name,
134              struct device* device,
135              struct v_dnode* mnt_point,
136              int options)
137 {
138     if (device && device->dev_type != DEV_IFVOL) {
139         return ENOTBLK;
140     }
141
142     if (mnt_point->inode && (mnt_point->inode->itype & F_MFILE)) {
143         return ENOTDIR;
144     }
145
146     struct filesystem* fs = fsm_get(fs_name);
147     if (!fs) {
148         return ENODEV;
149     }
150
151     if (fs->types == FSTYPE_ROFS) {
152         options |= MNT_RO;
153     }
154
155     char* dev_name = "sys";
156     struct v_mount* parent_mnt = mnt_point->mnt;
157     struct v_superblock *sb = vfs_sb_alloc(), *old_sb = mnt_point->super_block;
158     sb->dev = device;
159     mnt_point->super_block = sb;
160
161     if (device) {
162         dev_name = device->name_val;
163     }
164
165     int errno = 0;
166     if (!(errno = fs->mount(sb, mnt_point))) {
167         sb->fs = fs;
168         sb->root = mnt_point;
169
170         if (!(mnt_point->mnt = vfs_create_mount(parent_mnt, mnt_point))) {
171             errno = ENOMEM;
172             goto cleanup;
173         }
174
175         kprintf("mount: dev=%s, fs=%s, mode=%d", dev_name, fs_name, options);
176
177         mnt_point->mnt->flags = options;
178     } else {
179         goto cleanup;
180     }
181
182     return errno;
183
184 cleanup:
185     ERROR("mount: dev=%s, fs=%s, mode=%d, err=%d",
186           dev_name,
187           fs_name,
188           options,
189           errno);
190     mnt_point->super_block = old_sb;
191     vfs_sb_free(sb);
192     return errno;
193 }
194
195 int
196 vfs_unmount_at(struct v_dnode* mnt_point)
197 {
198     int errno = 0;
199     struct v_superblock* sb = mnt_point->super_block;
200     if (!sb) {
201         return EINVAL;
202     }
203
204     if (sb->root != mnt_point) {
205         return EINVAL;
206     }
207
208     if (mnt_point->mnt->busy_counter) {
209         return EBUSY;
210     }
211
212     if (!(errno = __vfs_do_unmount(mnt_point->mnt))) {
213         atomic_fetch_sub(&mnt_point->ref_count, 1);
214     }
215
216     return errno;
217 }
218
219 int
220 vfs_check_writable(struct v_dnode* dnode)
221 {
222     if ((dnode->mnt->flags & MNT_RO)) {
223         return EROFS;
224     }
225     return 0;
226 }
227
228 __DEFINE_LXSYSCALL4(int,
229                     mount,
230                     const char*,
231                     source,
232                     const char*,
233                     target,
234                     const char*,
235                     fstype,
236                     int,
237                     options)
238 {
239     struct v_dnode *dev = NULL, *mnt = NULL;
240     int errno = 0;
241
242     // It is fine if source is not exist, as some mounting don't require it
243     vfs_walk(__current->cwd, source, &dev, NULL, 0);
244
245     if ((errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
246         goto done;
247     }
248
249     if (mnt->ref_count > 1) {
250         errno = EBUSY;
251         goto done;
252     }
253
254     if (mnt->mnt->mnt_point == mnt) {
255         errno = EBUSY;
256         goto done;
257     }
258
259     // By our convention.
260     // XXX could we do better?
261     struct device* device = NULL;
262
263     if (dev) {
264         if (!(dev->inode->itype & VFS_IFVOLDEV)) {
265             errno = ENOTDEV;
266             goto done;
267         }
268         device = (struct device*)dev->inode->data;
269     }
270
271     errno = vfs_mount_at(fstype, device, mnt, options);
272
273 done:
274     return DO_STATUS(errno);
275 }
276
277 __DEFINE_LXSYSCALL1(int, unmount, const char*, target)
278 {
279     return vfs_unmount(target);
280 }