regression: test serial port r/w.
[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     vfs_sb_free(sb);
67     vfs_d_free(mnt->mnt_point);
68     vfree(mnt);
69
70     return errno;
71 }
72
73 void
74 mnt_mkbusy(struct v_mount* mnt)
75 {
76     mutex_lock(&mnt->lock);
77     mnt->busy_counter++;
78     mutex_unlock(&mnt->lock);
79 }
80
81 void
82 mnt_chillax(struct v_mount* mnt)
83 {
84     mutex_lock(&mnt->lock);
85     mnt->busy_counter--;
86     mutex_unlock(&mnt->lock);
87 }
88
89 int
90 vfs_mount_root(const char* fs_name, struct device* device)
91 {
92     extern struct v_dnode* vfs_sysroot;
93     int errno = 0;
94     if (vfs_sysroot->mnt && (errno = vfs_unmount_at(vfs_sysroot))) {
95         return errno;
96     }
97     return vfs_mount_at(fs_name, device, vfs_sysroot, 0);
98 }
99
100 int
101 vfs_mount(const char* target,
102           const char* fs_name,
103           struct device* device,
104           int options)
105 {
106     int errno;
107     struct v_dnode* mnt;
108
109     if (!(errno =
110             vfs_walk(__current->cwd, target, &mnt, NULL, VFS_WALK_MKPARENT))) {
111         errno = vfs_mount_at(fs_name, device, mnt, options);
112     }
113
114     return errno;
115 }
116
117 int
118 vfs_unmount(const char* target)
119 {
120     int errno;
121     struct v_dnode* mnt;
122
123     if (!(errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
124         errno = vfs_unmount_at(mnt);
125     }
126
127     return errno;
128 }
129
130 int
131 vfs_mount_at(const char* fs_name,
132              struct device* device,
133              struct v_dnode* mnt_point,
134              int options)
135 {
136     if (device && device->dev_type != DEV_IFVOL) {
137         return ENOTBLK;
138     }
139
140     if (mnt_point->inode && !(mnt_point->inode->itype & VFS_IFDIR)) {
141         return ENOTDIR;
142     }
143
144     struct filesystem* fs = fsm_get(fs_name);
145     if (!fs) {
146         return ENODEV;
147     }
148
149     if (fs->types == FSTYPE_ROFS) {
150         options |= MNT_RO;
151     }
152
153     char* dev_name = "sys";
154     struct v_mount* parent_mnt = mnt_point->mnt;
155     struct v_superblock *sb = vfs_sb_alloc(), *old_sb = mnt_point->super_block;
156     sb->dev = device;
157     mnt_point->super_block = sb;
158
159     if (device) {
160         dev_name = device->name_val;
161     }
162
163     int errno = 0;
164     if (!(errno = fs->mount(sb, mnt_point))) {
165         sb->fs = fs;
166         sb->root = mnt_point;
167
168         if (!(mnt_point->mnt = vfs_create_mount(parent_mnt, mnt_point))) {
169             errno = ENOMEM;
170             goto cleanup;
171         }
172
173         kprintf("mount: dev=%s, fs=%s, mode=%d\n", dev_name, fs_name, options);
174
175         mnt_point->mnt->flags = options;
176     } else {
177         goto cleanup;
178     }
179
180     return errno;
181
182 cleanup:
183     kprintf(KERROR "mount: dev=%s, fs=%s, mode=%d, err=%d\n",
184             dev_name,
185             fs_name,
186             options,
187             errno);
188     mnt_point->super_block = old_sb;
189     vfs_sb_free(sb);
190     return errno;
191 }
192
193 int
194 vfs_unmount_at(struct v_dnode* mnt_point)
195 {
196     int errno = 0;
197     struct v_superblock* sb = mnt_point->super_block;
198     if (!sb) {
199         return EINVAL;
200     }
201
202     if (sb->root != mnt_point) {
203         return EINVAL;
204     }
205
206     if (mnt_point->mnt->busy_counter) {
207         return EBUSY;
208     }
209
210     if (!(errno = __vfs_do_unmount(mnt_point->mnt))) {
211         atomic_fetch_sub(&mnt_point->ref_count, 1);
212     }
213
214     return errno;
215 }
216
217 int
218 vfs_check_writable(struct v_dnode* dnode)
219 {
220     if ((dnode->mnt->flags & MNT_RO)) {
221         return EROFS;
222     }
223     return 0;
224 }
225
226 __DEFINE_LXSYSCALL4(int,
227                     mount,
228                     const char*,
229                     source,
230                     const char*,
231                     target,
232                     const char*,
233                     fstype,
234                     int,
235                     options)
236 {
237     struct v_dnode *dev, *mnt;
238     int errno = 0;
239
240     if ((errno = vfs_walk(__current->cwd, source, &dev, NULL, 0))) {
241         goto done;
242     }
243
244     if ((errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
245         goto done;
246     }
247
248     if (mnt->ref_count > 1) {
249         errno = EBUSY;
250         goto done;
251     }
252
253     // By our convention.
254     // XXX could we do better?
255     struct device* device = (struct device*)dev->inode->data;
256
257     if (!(dev->inode->itype & VFS_IFVOLDEV) || !device) {
258         errno = ENOTDEV;
259         goto done;
260     }
261
262     errno = vfs_mount_at(fstype, device, mnt, options);
263
264 done:
265     return DO_STATUS(errno);
266 }
267
268 __DEFINE_LXSYSCALL1(int, unmount, const char*, target)
269 {
270     return vfs_unmount(target);
271 }