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