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