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