405 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			405 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2022-2022 Huawei Device Co., Ltd. All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without modification,
 | |
|  * are permitted provided that the following conditions are met:
 | |
|  *
 | |
|  * 1. Redistributions of source code must retain the above copyright notice, this list of
 | |
|  *    conditions and the following disclaimer.
 | |
|  *
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 | |
|  *    of conditions and the following disclaimer in the documentation and/or other materials
 | |
|  *    provided with the distribution.
 | |
|  *
 | |
|  * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 | |
|  *    to endorse or promote products derived from this software without specific prior written
 | |
|  *    permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | |
|  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | |
|  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 | |
|  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | |
|  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 | |
|  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 | |
|  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 | |
|  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 | |
|  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  */
 | |
| #include "vfs_mount.h"
 | |
| #include "vfs_files.h"
 | |
| #include "vfs_maps.h"
 | |
| #include "vfs_config.h"
 | |
| #include "stdlib.h"
 | |
| #include "string.h"
 | |
| #include "limits.h"
 | |
| #include "errno.h"
 | |
| #include "securec.h"
 | |
| #include "vfs_operations.h"
 | |
| #include "los_compiler.h"
 | |
| #include "los_debug.h"
 | |
| #include "los_fs.h"
 | |
| #include "los_mux.h"
 | |
| 
 | |
| struct MountPoint *g_mountPoints = NULL;
 | |
| 
 | |
| static void MpDeleteFromList(struct MountPoint *mp)
 | |
| {
 | |
|     struct MountPoint *prev = NULL;
 | |
| 
 | |
|     /* delete mp from mount list */
 | |
|     if (g_mountPoints == mp) {
 | |
|         g_mountPoints = mp->mNext;
 | |
|     } else {
 | |
|         for (prev = g_mountPoints; prev != NULL; prev = prev->mNext) {
 | |
|             if (prev->mNext != mp) {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             prev->mNext = mp->mNext;
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #if (LOSCFG_FS_SUPPORT_MOUNT_TARGET_RECURSIVE == 1)
 | |
| struct MountPoint *VfsMpFind(const char *path, const char **pathInMp)
 | |
| {
 | |
|     struct MountPoint *mp = g_mountPoints;
 | |
|     struct MountPoint *bestMp = NULL;
 | |
|     int bestMatches = 0;
 | |
| 
 | |
|     if (pathInMp != NULL) {
 | |
|         *pathInMp = NULL;
 | |
|     }
 | |
|     while ((mp != NULL) && (mp->mPath != NULL)) {
 | |
|         const char *mPath = mp->mPath;
 | |
|         const char *iPath = path;
 | |
|         const char *t = NULL;
 | |
|         int matches = 0;
 | |
|         do {
 | |
|             while ((*mPath == '/') && (*(mPath + 1) != '/')) {
 | |
|                 mPath++;
 | |
|             }
 | |
|             while ((*iPath == '/') && (*(iPath + 1) != '/')) {
 | |
|                 iPath++;
 | |
|             }
 | |
| 
 | |
|             t = strchr(mPath, '/');
 | |
|             if (t == NULL) {
 | |
|                 t = strchr(mPath, '\0');
 | |
|             }
 | |
|             if ((t == mPath) || (t == NULL)) {
 | |
|                 break;
 | |
|             }
 | |
|             if (strncmp(mPath, iPath, (size_t)(t - mPath)) != 0) {
 | |
|                 goto next; /* this mount point do not match, check next */
 | |
|             }
 | |
| 
 | |
|             iPath += (t - mPath);
 | |
|             if ((*iPath != '\0') && (*iPath != '/')) {
 | |
|                 goto next;
 | |
|             }
 | |
| 
 | |
|             matches += (t - mPath);
 | |
|             mPath += (t - mPath);
 | |
|         } while (*mPath != '\0');
 | |
| 
 | |
|         if (matches > bestMatches) {
 | |
|             bestMatches = matches;
 | |
|             bestMp = mp;
 | |
| 
 | |
|             while ((*iPath == '/') && (*(iPath + 1) != '/')) {
 | |
|                 iPath++;
 | |
|             }
 | |
| 
 | |
|             if (pathInMp != NULL) {
 | |
|                 *pathInMp = path;
 | |
|             }
 | |
|         }
 | |
|     next:
 | |
|         mp = mp->mNext;
 | |
|     }
 | |
| 
 | |
|     return bestMp;
 | |
| }
 | |
| #else
 | |
| struct MountPoint *VfsMpFind(const char *path, const char **pathInMp)
 | |
| {
 | |
|     struct MountPoint *mp = g_mountPoints;
 | |
|     const char *iPath = path;
 | |
|     const char *mPath = NULL;
 | |
|     const char *target = NULL;
 | |
| 
 | |
|     if (pathInMp != NULL) {
 | |
|         *pathInMp = NULL;
 | |
|     }
 | |
|     while (*iPath == '/') {
 | |
|         ++iPath;
 | |
|     }
 | |
| 
 | |
|     while ((mp != NULL) && (mp->mPath != NULL)) {
 | |
|         mPath = mp->mPath;
 | |
|         target = iPath;
 | |
| 
 | |
|         while (*mPath == '/') {
 | |
|             ++mPath;
 | |
|         }
 | |
| 
 | |
|         while ((*mPath != '\0') && (*mPath != '/') &&
 | |
|                (*target != '\0') && (*target != '/')) {
 | |
|             if (*mPath != *target) {
 | |
|                 break;
 | |
|             }
 | |
|             ++mPath;
 | |
|             ++target;
 | |
|         }
 | |
|         if (((*mPath == '\0') || (*mPath == '/')) &&
 | |
|             ((*target == '\0') || (*target == '/'))) {
 | |
|             if (pathInMp != NULL) {
 | |
|                 *pathInMp = path;
 | |
|             }
 | |
|             return mp;
 | |
|         }
 | |
|         mp = mp->mNext;
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| STATIC struct MountPoint *VfsMountPointInit(const char *source, const char *target,
 | |
|         const char *fsType, unsigned long mountflags)
 | |
| {
 | |
|     struct MountPoint *mp = NULL;
 | |
|     const char *pathInMp = NULL;
 | |
|     struct FsMap *mFs = NULL;
 | |
|     size_t ssize = 0;
 | |
|     size_t tsize;
 | |
| 
 | |
|     /* find mp by target, to see if it was mounted */
 | |
|     mp = VfsMpFind(target, &pathInMp);
 | |
|     if (mp != NULL && pathInMp != NULL) {
 | |
|         errno = EINVAL;
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     /* Find fsMap corresponding to the fsType */
 | |
|     mFs = VfsFsMapGet(fsType);
 | |
|     if ((mFs == NULL) || (mFs->fsMops == NULL) || (mFs->fsMops->mount == NULL)) {
 | |
|         errno = ENODEV;
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (source != NULL) {
 | |
|         ssize = strlen(source) + 1;
 | |
|     }
 | |
| 
 | |
|     tsize = strlen(target) + 1;
 | |
| 
 | |
|     mp = (struct MountPoint *)LOSCFG_FS_MALLOC_HOOK(sizeof(struct MountPoint) + ssize + tsize);
 | |
|     if (mp == NULL) {
 | |
|         errno = ENOMEM;
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     mp->mFs = mFs;
 | |
|     mp->mDev = NULL;
 | |
|     mp->mRefs = 0;
 | |
|     mp->mWriteEnable = (mountflags & MS_RDONLY) ? FALSE : TRUE;
 | |
|     mp->mFs->fsRefs++;
 | |
| 
 | |
|     if (source != NULL && strcpy_s((char *)mp + sizeof(struct MountPoint), ssize, source) != EOK) {
 | |
|         LOSCFG_FS_FREE_HOOK(mp);
 | |
|         errno = ENOMEM;
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (strcpy_s((char *)mp + sizeof(struct MountPoint) + ssize, tsize, target) != EOK) {
 | |
|         LOSCFG_FS_FREE_HOOK(mp);
 | |
|         errno = ENOMEM;
 | |
|         return NULL;
 | |
|     }
 | |
|     mp->mDev = source ? (char *)mp + sizeof(struct MountPoint) : NULL;
 | |
|     mp->mPath = (char *)mp + sizeof(struct MountPoint) + ssize;
 | |
| 
 | |
|     return mp;
 | |
| }
 | |
| 
 | |
| STATIC int VfsRemount(const char *source, const char *target,
 | |
|                       const char *fsType, unsigned long mountflags,
 | |
|                       const void *data)
 | |
| {
 | |
|     (VOID)source;
 | |
|     (VOID)fsType;
 | |
|     struct MountPoint *mp;
 | |
| 
 | |
|     mp = VfsMpFind(target, NULL);
 | |
|     if (mp == NULL) {
 | |
|         errno = EINVAL;
 | |
|         return (int)LOS_NOK;
 | |
|     }
 | |
| 
 | |
|     LOS_ASSERT(mp->mFs != NULL);
 | |
|     LOS_ASSERT(mp->mFs->fsMops != NULL);
 | |
|     LOS_ASSERT(mp->mFs->fsMops->mount != NULL);
 | |
| 
 | |
|     return mp->mFs->fsMops->mount(mp, mountflags, data);
 | |
| }
 | |
| 
 | |
| STATIC int VfsMountPathCheck(const char *target)
 | |
| {
 | |
|     /* target must begin with '/', for example /system, /data, etc. */
 | |
|     if ((target == NULL) || (target[0] != '/')) {
 | |
|         errno = EINVAL;
 | |
|         return (int)LOS_NOK;
 | |
|     }
 | |
| 
 | |
|     if (strlen(target) >= PATH_MAX) {
 | |
|         errno = ENAMETOOLONG;
 | |
|         return (int)LOS_NOK;
 | |
|     }
 | |
| 
 | |
|     return LOS_OK;
 | |
| }
 | |
| 
 | |
| int mount(const char *source, const char *target,
 | |
|                 const char *fsType, unsigned long mountflags,
 | |
|                 const void *data)
 | |
| {
 | |
|     int ret;
 | |
|     struct MountPoint *mp = NULL;
 | |
| 
 | |
|     if (VfsMountPathCheck(target) != LOS_OK) {
 | |
|         return (int)LOS_NOK;
 | |
|     }
 | |
| 
 | |
|     (void)LOS_FsLock();
 | |
| 
 | |
|     if (mountflags & MS_REMOUNT) {
 | |
|         ret = VfsRemount(source, target, fsType, mountflags, data);
 | |
|         LOS_FsUnlock();
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     mp = VfsMountPointInit(source, target, fsType, mountflags);
 | |
|     if (mp == NULL) {
 | |
|         LOS_FsUnlock();
 | |
|         return (int)LOS_NOK;
 | |
|     }
 | |
| 
 | |
|     ret = mp->mFs->fsMops->mount(mp, mountflags, data);
 | |
|     if (ret != 0) {
 | |
|         /* errno is set */
 | |
|         PRINT_ERR("mount failed, target %s.\n", target);
 | |
|         goto errout;
 | |
|     }
 | |
| 
 | |
|     mp->mNext = g_mountPoints;
 | |
|     g_mountPoints = mp;
 | |
|     LOS_FsUnlock();
 | |
|     return LOS_OK;
 | |
| 
 | |
| errout:
 | |
|     LOSCFG_FS_FREE_HOOK(mp);
 | |
|     LOS_FsUnlock();
 | |
|     return (int)LOS_NOK;
 | |
| }
 | |
| 
 | |
| int umount(const char *target)
 | |
| {
 | |
|     struct MountPoint *mp = NULL;
 | |
|     int ret = (int)LOS_NOK;
 | |
| 
 | |
|     (void)LOS_FsLock();
 | |
|     if (VfsMountPathCheck(target) != LOS_OK) {
 | |
|         goto errout;
 | |
|     }
 | |
| 
 | |
|     mp = VfsMpFind(target, NULL);
 | |
|     if ((mp == NULL) || (mp->mRefs != 0)) {
 | |
|         goto errout;
 | |
|     }
 | |
| 
 | |
|     if ((mp->mFs == NULL) || (mp->mFs->fsMops == NULL) ||
 | |
|         (mp->mFs->fsMops->umount == NULL)) {
 | |
|         goto errout;
 | |
|     }
 | |
| 
 | |
|     ret = mp->mFs->fsMops->umount(mp);
 | |
|     if (ret != 0) {
 | |
|         /* errno is set */
 | |
|         goto errout;
 | |
|     }
 | |
| 
 | |
|     /* delete mp from mount list */
 | |
|     MpDeleteFromList(mp);
 | |
|     mp->mFs->fsRefs--;
 | |
|     LOSCFG_FS_FREE_HOOK(mp);
 | |
| 
 | |
|     LOS_FsUnlock();
 | |
|     return LOS_OK;
 | |
| 
 | |
| errout:
 | |
|     PRINT_ERR("umount2 failed, target %s.\n", target);
 | |
|     LOS_FsUnlock();
 | |
|     return (int)LOS_NOK;
 | |
| }
 | |
| 
 | |
| static void CloseFdsInMp(const struct MountPoint *mp)
 | |
| {
 | |
|     for (int fd = 0; fd < NR_OPEN_DEFAULT; fd++) {
 | |
|         struct File *f = FdToFile(fd);
 | |
|         if (f == NULL) {
 | |
|             continue;
 | |
|         }
 | |
|         if ((f->fMp == mp) &&
 | |
|             (f->fFops != NULL) &&
 | |
|             (f->fFops->close != NULL)) {
 | |
|             (void)f->fFops->close(f);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| int umount2(const char *target, int flag)
 | |
| {
 | |
|     struct MountPoint *mp = NULL;
 | |
|     int ret = (int)LOS_NOK;
 | |
| 
 | |
|     (void)LOS_FsLock();
 | |
|     if (VfsMountPathCheck(target) != LOS_OK) {
 | |
|         goto errout;
 | |
|     }
 | |
| 
 | |
|     mp = VfsMpFind(target, NULL);
 | |
|     if ((mp == NULL) || (mp->mRefs != 0) ||
 | |
|         (mp->mFs == NULL) || (mp->mFs->fsMops == NULL) ||
 | |
|         (mp->mFs->fsMops->umount2 == NULL)) {
 | |
|         goto errout;
 | |
|     }
 | |
| 
 | |
|     /* Close all files under the mount point */
 | |
|     if ((UINT32)flag & MNT_FORCE) {
 | |
|         CloseFdsInMp(mp);
 | |
|     }
 | |
| 
 | |
|     ret = mp->mFs->fsMops->umount2(mp, flag);
 | |
|     if (ret != 0) {
 | |
|         /* errno is set */
 | |
|         goto errout;
 | |
|     }
 | |
| 
 | |
|     /* delete mp from mount list */
 | |
|     MpDeleteFromList(mp);
 | |
|     mp->mFs->fsRefs--;
 | |
|     LOSCFG_FS_FREE_HOOK(mp);
 | |
| 
 | |
|     LOS_FsUnlock();
 | |
|     return LOS_OK;
 | |
| 
 | |
| errout:
 | |
|     PRINT_ERR("umount2 failed, target %s.\n", target);
 | |
|     LOS_FsUnlock();
 | |
|     return (int)LOS_NOK;
 | |
| }
 |