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