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