1 /****************************************************************************
2 * fs/mount/fs_mount.c
3 *
4 * Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved.
5 * Based on NuttX originally from nuttx source (nuttx/fs/ and nuttx/drivers/)
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 ****************************************************************************/
20
21 /****************************************************************************
22 * Included Files
23 ****************************************************************************/
24
25 #include "vfs_config.h"
26
27 #include "sys/mount.h"
28 #include "string.h"
29 #include "errno.h"
30 #include "assert.h"
31 #include "vnode.h"
32 #include "stdlib.h"
33 #ifdef LOSCFG_DRIVERS_MTD
34 #include "mtd_partition.h"
35 #endif
36 #ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
37 #include "errcode_fat.h"
38 #endif
39 #include "los_tables.h"
40 #ifdef LOSCFG_DRIVERS_RANDOM
41 #include "hisoc/random.h"
42 #else
43 #include "stdlib.h"
44 #endif
45 #include "path_cache.h"
46 #include "fs/mount.h"
47 #include "fs/driver.h"
48 #include "fs/fs.h"
49 #ifdef LOSCFG_FS_ZPFS
50 #include "zpfs.h"
51 #endif
52 #ifdef LOSCFG_MNT_CONTAINER
53 #include "los_mnt_container_pri.h"
54 #endif
55
56 /* At least one filesystem must be defined, or this file will not compile.
57 * It may be desire-able to make filesystems dynamically registered at
58 * some time in the future, but at present, this file needs to know about
59 * every configured filesystem.
60 */
61
62 #ifdef CONFIG_FS_READABLE
63
64 /****************************************************************************
65 * Pre-processor Definitions
66 ****************************************************************************/
67 /* Configuration ************************************************************/
68 /* In the canonical case, a file system is bound to a block driver. However,
69 * some less typical cases a block driver is not required. Examples are
70 * pseudo file systems (like BINFS or PROCFS) and MTD file systems (like NXFFS).
71 *
72 * These file systems all require block drivers:
73 */
74
75 #define BLKDRVR_NOT_MOUNTED 2
76
77 extern struct fsmap_t g_fsmap[];
78 LOS_HAL_TABLE_BEGIN(g_fsmap, fsmap);
79
80 extern struct fsmap_t g_fsmap_end;
81 LOS_HAL_TABLE_END(g_fsmap_end, fsmap);
82
83 /****************************************************************************
84 * Public Data
85 ****************************************************************************/
86
87 /****************************************************************************
88 * Private Functions
89 ****************************************************************************/
90
91 /****************************************************************************
92 * Name: mount_findfs
93 *
94 * Description:
95 * find the specified filesystem
96 *
97 ****************************************************************************/
98
mount_findfs(const char * filesystemtype)99 static const struct fsmap_t *mount_findfs(const char *filesystemtype)
100 {
101 struct fsmap_t *m = NULL;
102
103 for (m = &g_fsmap[0]; m != &g_fsmap_end; ++m)
104 {
105 if (m->fs_filesystemtype &&
106 strcmp(filesystemtype, m->fs_filesystemtype) == 0)
107 {
108 return m;
109 }
110 }
111
112 return (const struct fsmap_t *)NULL;
113 }
114
115
116 /****************************************************************************
117 * Public Functions
118 ****************************************************************************/
119
120 /****************************************************************************
121 * Name: mount
122 *
123 * Description:
124 * mount() attaches the filesystem specified by the 'source' block device
125 * name into the root file system at the path specified by 'target.'
126 *
127 * Return:
128 * Zero is returned on success; -1 is returned on an error and errno is
129 * set appropriately:
130 *
131 * EACCES A component of a path was not searchable or mounting a read-only
132 * filesystem was attempted without giving the MS_RDONLY flag.
133 * EBUSY 'source' is already mounted.
134 * EFAULT One of the pointer arguments points outside the user address
135 * space.
136 * EINVAL 'source' had an invalid superblock.
137 * ENODEV 'filesystemtype' not configured
138 * ENOENT A pathname was empty or had a nonexistent component.
139 * ENOMEM Could not allocate a memory to copy filenames or data into.
140 * ENOTBLK 'source' is not a block device
141 *
142 ****************************************************************************/
143
mount(const char * source,const char * target,const char * filesystemtype,unsigned long mountflags,const void * data)144 int mount(const char *source, const char *target,
145 const char *filesystemtype, unsigned long mountflags,
146 const void *data)
147 {
148 int ret;
149 int errcode = 0;
150 struct Mount* mnt = NULL;
151 struct Vnode *device = NULL;
152 struct Vnode *mountpt_vnode = NULL;
153 const struct fsmap_t *fsmap = NULL;
154 const struct MountOps *mops = NULL;
155 LIST_HEAD *mount_list = NULL;
156 #ifdef LOSCFG_DRIVERS_MTD
157 mtd_partition *partition = NULL;
158 #endif
159
160 if (filesystemtype == NULL)
161 {
162 errcode = -EINVAL;
163 goto errout;
164 }
165
166 /* Verify required pointer arguments */
167
168 DEBUGASSERT(target && filesystemtype);
169
170 /* Find the specified filesystem. Try the block driver file systems first */
171
172 if ((fsmap = mount_findfs(filesystemtype)) == NULL || (fsmap->is_bdfs && !source))
173 {
174 PRINT_ERR("Failed to find file system %s\n", filesystemtype);
175 errcode = -ENODEV;
176 goto errout;
177 }
178
179 mops = fsmap->fs_mops;
180
181 if (fsmap->is_bdfs && source)
182 {
183 /* Make sure that a block driver argument was provided */
184
185 DEBUGASSERT(source);
186
187 /* Find the block driver */
188
189 ret = find_blockdriver(source, mountflags, &device);
190 if (ret < 0)
191 {
192 PRINT_ERR("Failed to find block driver %s\n", source);
193 errcode = ret;
194 goto errout;
195 }
196 }
197
198 VnodeHold();
199 ret = VnodeLookup(target, &mountpt_vnode, 0);
200
201 /* The mount point must be an existed vnode. */
202
203 if (ret != OK)
204 {
205 PRINT_ERR("Failed to find valid mountpoint %s\n", target);
206 errcode = -EINVAL;
207 goto errout_with_lock;
208 }
209 if (mountpt_vnode->flag & VNODE_FLAG_MOUNT_NEW)
210 {
211 #ifdef LOSCFG_MNT_CONTAINER
212 struct Mount *tMnt = NULL;
213 LOS_DL_LIST_FOR_EACH_ENTRY(tMnt, GetMountList(), struct Mount, mountList)
214 {
215 if (tMnt->vnodeCovered == mountpt_vnode)
216 {
217 PRINT_ERR("can't mount to %s, already mounted.\n", target);
218 errcode = -EINVAL;
219 goto errout_with_lock;
220 }
221 }
222 #else
223 PRINT_ERR("can't mount to %s, already mounted.\n", target);
224 errcode = -EINVAL;
225 goto errout_with_lock;
226 #endif
227 }
228
229 #ifdef LOSCFG_MNT_CONTAINER
230 struct Mount *cacheMnt = NULL;
231 if (source != NULL)
232 {
233 LOS_DL_LIST_FOR_EACH_ENTRY(cacheMnt, GetMountCache(), struct Mount, mountList)
234 {
235 if (strcmp(cacheMnt->devName, source) == 0)
236 {
237 struct Mount *newMnt = (struct Mount *)zalloc(sizeof(struct Mount));
238 if (newMnt == NULL)
239 {
240 PRINT_ERR("New mount alloc failed no memory!\n");
241 errcode = -EINVAL;
242 goto errout;
243 }
244 *newMnt = *cacheMnt;
245 LOS_ListTailInsert(GetMountList(), &(newMnt->mountList));
246 cacheMnt->vnodeCovered->mntCount++;
247 VnodeDrop();
248 return OK;
249 }
250 }
251 }
252 #endif
253 /* Bind the block driver to an instance of the file system. The file
254 * system returns a reference to some opaque, fs-dependent structure
255 * that encapsulates this binding.
256 */
257
258 if (mops->Mount == NULL)
259 {
260 /* The filesystem does not support the bind operation ??? */
261
262 PRINTK("ERROR: Filesystem does not support bind\n");
263 errcode = -ENOSYS;
264 goto errout_with_lock;
265 }
266
267 /* Increment reference count for the reference we pass to the file system */
268 #ifdef LOSCFG_DRIVERS_MTD
269 if (fsmap->is_mtd_support && (device != NULL))
270 {
271 partition = (mtd_partition *)((struct drv_data *)device->data)->priv;
272 partition->mountpoint_name = (char *)zalloc(strlen(target) + 1);
273 if (partition->mountpoint_name == NULL)
274 {
275 errcode = -ENOMEM;
276 goto errout_with_lock;
277 }
278 (void)strncpy_s(partition->mountpoint_name, strlen(target) + 1, target, strlen(target));
279 partition->mountpoint_name[strlen(target)] = '\0';
280 }
281 #endif
282
283 mnt = MountAlloc(mountpt_vnode, (struct MountOps*)mops);
284
285 #ifdef LOSCFG_FS_ZPFS
286 if (strcmp(filesystemtype, ZPFS_NAME) == 0)
287 {
288 ret = ZpfsPrepare(source, target, mnt);
289 if (ret < 0)
290 {
291 errcode = ret;
292 goto errout_with_mountpt;
293 }
294 }
295 #endif
296
297 mnt->mountFlags = mountflags;
298
299 mountpt_vnode->useCount++;
300 ret = mops->Mount(mnt, device, data);
301 mountpt_vnode->useCount--;
302 if (ret != 0)
303 {
304 /* The vnode is unhappy with the blkdrvr for some reason. Back out
305 * the count for the reference we failed to pass and exit with an
306 * error.
307 */
308
309 PRINT_ERR("Bind method failed: %d\n", ret);
310 errcode = ret;
311 #ifdef LOSCFG_DRIVERS_MTD
312 if (fsmap->is_mtd_support && (device != NULL) && (partition != NULL))
313 {
314 free(partition->mountpoint_name);
315 partition->mountpoint_name = NULL;
316 }
317 #endif
318 goto errout_with_mountpt;
319 }
320 mnt->vnodeBeCovered->flag |= VNODE_FLAG_MOUNT_ORIGIN;
321 mnt->vnodeCovered->flag |= VNODE_FLAG_MOUNT_NEW;
322 mnt->vnodeCovered->filePath = strdup(mountpt_vnode->filePath);
323 mnt->vnodeDev = device;
324 mnt->ops = mops;
325 if (target && (strlen(target) != 0))
326 {
327 ret = strcpy_s(mnt->pathName, PATH_MAX, target);
328 if (ret != EOK)
329 {
330 PRINT_ERR("Failed to copy mount point pathname, errno %d\n", ret);
331 }
332 }
333
334 if (source && (strlen(source) != 0))
335 {
336 ret = strcpy_s(mnt->devName, PATH_MAX, source);
337 if (ret != EOK)
338 {
339 PRINT_ERR("Failed to copy dev name, errno %d\n", ret);
340 }
341 }
342
343 //* We have it, now populate it with driver specific information. */
344
345 mount_list = GetMountList();
346 LOS_ListAdd(mount_list, &mnt->mountList);
347
348 #ifdef LOSCFG_MNT_CONTAINER
349 if (source != NULL)
350 {
351 struct Mount *newMnt = (struct Mount *)zalloc(sizeof(struct Mount));
352 if (newMnt == NULL)
353 {
354 PRINT_ERR("New mount alloc failed no memory!\n");
355 errcode = -EINVAL;
356 goto errout;
357 }
358 *newMnt = *mnt;
359 LOS_ListTailInsert(GetMountCache(), &(newMnt->mountList));
360 }
361 #endif
362
363 if (!strcmp("/", target))
364 {
365 ChangeRoot(mnt->vnodeCovered);
366 }
367
368 VnodeDrop();
369
370 /* We can release our reference to the blkdrver_vnode, if the filesystem
371 * wants to retain the blockdriver vnode (which it should), then it must
372 * have called vnode_addref(). There is one reference on mountpt_vnode
373 * that will persist until umount() is called.
374 */
375
376 return OK;
377
378 /* A lot of goto's! But they make the error handling much simpler */
379
380 errout_with_mountpt:
381 if (mnt)
382 {
383 free(mnt);
384 }
385 errout_with_lock:
386 VnodeDrop();
387 errout:
388 set_errno(-errcode);
389 return VFS_ERROR;
390 }
391 #endif /* CONFIG_FS_READABLE */
392