openharmony_kernel_liteos_m/components/fs/vfs/vfs_fs.c

1427 lines
34 KiB
C

/*
* Copyright (c) 2022-2023 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.
*/
#define _GNU_SOURCE 1
#include "los_fs.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/uio.h>
#include "errno.h"
#include "fcntl.h"
#include "los_mux.h"
#include "los_debug.h"
#include "los_sched.h"
#include "limits.h"
#include "securec.h"
#include "vfs_config.h"
#include "vfs_files.h"
#include "vfs_maps.h"
#include "vfs_mount.h"
#include "vfs_operations.h"
#ifdef LOSCFG_NET_LWIP_SACK
#include "lwipopts.h"
#include "lwip/sockets.h"
#define CONFIG_NSOCKET_DESCRIPTORS LWIP_CONFIG_NUM_SOCKETS
#else
#define CONFIG_NSOCKET_DESCRIPTORS 0
#endif
#ifdef LOSCFG_RANDOM_DEV
#include "hks_client.h"
#define RANDOM_DEV_FD CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS
#define RANDOM_DEV_PATH "/dev/random"
#endif
#if (LOSCFG_POSIX_PIPE_API == 1)
#include "pipe_impl.h"
#ifdef LOSCFG_RANDOM_DEV
#define PIPE_DEV_FD (RANDOM_DEV_FD + 1)
#else
#define PIPE_DEV_FD (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS)
#endif
int PollQueryFd(int fd, struct PollTable *table)
{
if (fd >= PIPE_DEV_FD) {
return PipePoll(fd, table);
}
return -ENODEV;
}
#endif
#define FREE_AND_SET_NULL(ptr) do { \
LOSCFG_FS_FREE_HOOK(ptr); \
ptr = NULL; \
} while (0)
#define LOS_FCNTL (O_NONBLOCK | O_NDELAY | O_APPEND | O_SYNC)
#define IOV_MAX_CNT 4
UINT32 g_fsMutex;
static UINT32 g_dirNum = 0;
int LOS_FsLock(void)
{
if (!OsCheckKernelRunning()) {
return LOS_OK;
}
if (LOS_MuxPend(g_fsMutex, (UINT32)LOSCFG_FS_LOCK_TIMEOUT) != LOS_OK) {
PRINT_ERR("LOS_FsLock failed!");
return (int)LOS_NOK;
}
return LOS_OK;
}
void LOS_FsUnlock(void)
{
if (!OsCheckKernelRunning()) {
return;
}
(void)LOS_MuxPost(g_fsMutex);
}
#ifdef LOSCFG_RANDOM_DEV
/**
* @brief Get canonical form of a given path based on cwd(Current working directory).
*
* @param cwd Indicates the current working directory.
* @param path Indicates the path to be canonicalization.
* @param buf Indicates the pointer to the buffer where the result will be return.
* @param bufSize Indicates the size of the buffer.
* @return Returns the length of the canonical path.
*
* @attention if path is an absolute path, cwd is ignored. if cwd if not specified, it is assumed to be root('/').
* if the buffer is not big enough the result will be truncated, but the return value will always be the
* length of the canonical path.
*/
static size_t GetCanonicalPath(const char *cwd, const char *path, char *buf, size_t bufSize)
{
size_t offset;
if (!path) {
path = "";
}
if ((!cwd) || (path[0] == '/')) {
cwd = "";
}
offset = strlen("///") + 1; // three '/' and one '\0'
size_t tmpLen = strlen(cwd) + strlen(path) + offset;
char *tmpBuf = (char *)LOSCFG_FS_MALLOC_HOOK(tmpLen);
if (tmpBuf == NULL) {
return LOS_OK;
}
if (-1 == sprintf_s(tmpBuf, tmpLen, "/%s/%s/", cwd, path)) {
LOSCFG_FS_FREE_HOOK(tmpBuf);
return LOS_OK;
}
char *p;
/* replace /./ to / */
offset = strlen("/./") - 1;
while ((p = strstr(tmpBuf, "/./")) != NULL) {
if (EOK != memmove_s(p, tmpLen - (p - tmpBuf), p + offset, tmpLen - (p - tmpBuf) - offset)) {
LOSCFG_FS_FREE_HOOK(tmpBuf);
return LOS_OK;
}
}
/* replace // to / */
while ((p = strstr(tmpBuf, "//")) != NULL) {
if (EOK != memmove_s(p, tmpLen - (p - tmpBuf), p + 1, tmpLen - (p - tmpBuf) - 1)) {
LOSCFG_FS_FREE_HOOK(tmpBuf);
return LOS_OK;
}
}
/* handle /../ (e.g., replace /aa/bb/../ to /aa/) */
offset = strlen("/../") - 1;
while ((p = strstr(tmpBuf, "/../")) != NULL) {
char *start = p;
while (start > tmpBuf && *(start - 1) != '/') {
--start;
}
if (EOK != memmove_s(start, tmpLen - (start - tmpBuf), p + offset, tmpLen - (p - tmpBuf) - offset)) {
LOSCFG_FS_FREE_HOOK(tmpBuf);
return LOS_OK;
}
}
size_t totalLen = strlen(tmpBuf);
/* strip the last / */
if (totalLen > 1 && tmpBuf[totalLen - 1] == '/') {
tmpBuf[--totalLen] = 0;
}
if ((!buf) || (bufSize == 0)) {
LOSCFG_FS_FREE_HOOK(tmpBuf);
return totalLen;
}
if (EOK != memcpy_s(buf, bufSize, tmpBuf, (((totalLen + 1) > bufSize) ? bufSize : (totalLen + 1)))) {
LOSCFG_FS_FREE_HOOK(tmpBuf);
return LOS_OK;
}
buf[bufSize - 1] = 0;
LOSCFG_FS_FREE_HOOK(tmpBuf);
return totalLen;
}
#endif
static int VfsPathCheck(const char *path, bool isFile)
{
size_t len;
if ((path == NULL) || (path[0] == '\0')) {
VFS_ERRNO_SET(EINVAL);
return (int)LOS_NOK;
}
len = strlen(path);
if (len >= PATH_MAX) {
VFS_ERRNO_SET(ENAMETOOLONG);
return (int)LOS_NOK;
}
if (isFile && path[len - 1] == '/') {
VFS_ERRNO_SET(EINVAL);
return (int)LOS_NOK;
}
return LOS_OK;
}
static int VfsOpen(const char *path, int flags)
{
size_t len;
struct File *file = NULL;
int fd = -1;
const char *pathInMp = NULL;
struct MountPoint *mp = NULL;
if (VfsPathCheck(path, TRUE) != LOS_OK) {
return fd;
}
if (LOS_FsLock() != LOS_OK) {
VFS_ERRNO_SET(EAGAIN);
return fd;
}
mp = VfsMpFind(path, &pathInMp);
if ((mp == NULL) || (pathInMp == NULL) || (*pathInMp == '\0') ||
(mp->mFs->fsFops == NULL) || (mp->mFs->fsFops->open == NULL)) {
/* path is not in any mountpoint */
VFS_ERRNO_SET(ENOENT);
LOS_FsUnlock();
return fd;
}
if ((mp->mWriteEnable == FALSE) &&
(flags & (O_CREAT | O_WRONLY | O_RDWR))) {
/* can't create file in read only mp */
VFS_ERRNO_SET(EACCES);
LOS_FsUnlock();
return fd;
}
file = VfsFileGet();
if (file == NULL) {
VFS_ERRNO_SET(ENFILE);
LOS_FsUnlock();
return fd;
}
len = strlen(path) + 1;
file->fullPath = LOSCFG_FS_MALLOC_HOOK(len);
if (file->fullPath == NULL) {
VFS_ERRNO_SET(ENOMEM);
VfsFilePut(file);
LOS_FsUnlock();
return (int)LOS_NOK;
}
(void)strcpy_s((char *)file->fullPath, len, path);
file->fFlags = (UINT32)flags;
file->fOffset = 0;
file->fData = NULL;
file->fFops = mp->mFs->fsFops;
file->fMp = mp;
file->fOwner = LOS_CurTaskIDGet();
if (file->fFops->open(file, pathInMp, flags) == 0) {
mp->mRefs++;
fd = FileToFd(file);
file->fStatus = FILE_STATUS_READY; /* file now ready to use */
} else {
LOSCFG_FS_FREE_HOOK((void *)file->fullPath);
VfsFilePut(file);
}
LOS_FsUnlock();
return fd;
}
/* attach to a file and then set new status */
static struct File *VfsAttachFile(int fd, UINT32 status)
{
struct File *file = NULL;
if ((fd < MIN_START_FD) || (fd >= CONFIG_NFILE_DESCRIPTORS)) {
VFS_ERRNO_SET(EBADF);
return NULL;
}
if (LOS_FsLock() != LOS_OK) {
VFS_ERRNO_SET(EFAULT);
return NULL;
}
file = FdToFile(fd);
if ((file == NULL) || (file->fMp == NULL)) {
VFS_ERRNO_SET(EBADF);
LOS_FsUnlock();
return NULL;
}
if (file->fStatus != FILE_STATUS_READY) {
VFS_ERRNO_SET(EBADF);
LOS_FsUnlock();
return NULL;
}
file->fStatus = status;
return file;
}
static struct File *VfsAttachFileReady(int fd)
{
return VfsAttachFile(fd, FILE_STATUS_READY);
}
static struct File *VfsAttachFileWithStatus(int fd, int status)
{
return VfsAttachFile(fd, (UINT32)status);
}
static void VfsDetachFile(const struct File *file)
{
(void)file;
LOS_FsUnlock();
}
static int VfsClose(int fd)
{
struct File *file = NULL;
int ret = (int)LOS_NOK;
file = VfsAttachFileWithStatus(fd, FILE_STATUS_CLOSING);
if (file == NULL) {
return ret;
}
if ((file->fFops != NULL) && (file->fFops->close != NULL)) {
ret = file->fFops->close(file);
} else {
VFS_ERRNO_SET(ENOTSUP);
}
if ((ret == 0) && (file->fMp != NULL)) {
file->fMp->mRefs--;
}
if (file->fullPath != NULL) {
LOSCFG_FS_FREE_HOOK((void *)file->fullPath);
}
VfsFilePut(file);
VfsDetachFile(file);
return ret;
}
static ssize_t VfsRead(int fd, char *buff, size_t bytes)
{
struct File *file = NULL;
ssize_t ret = (ssize_t)-1;
if (buff == NULL) {
VFS_ERRNO_SET(EFAULT);
return ret;
}
if (bytes == 0) {
return 0;
}
file = VfsAttachFileReady(fd);
if (file == NULL) {
return ret;
}
if ((file->fFlags & O_ACCMODE) == O_WRONLY) {
VFS_ERRNO_SET(EACCES);
} else if ((file->fFops != NULL) && (file->fFops->read != NULL)) {
ret = file->fFops->read(file, buff, bytes);
} else {
VFS_ERRNO_SET(ENOTSUP);
}
/* else ret will be -1 */
VfsDetachFile(file);
return ret;
}
static ssize_t VfsWrite(int fd, const void *buff, size_t bytes)
{
struct File *file = NULL;
ssize_t ret = (ssize_t)LOS_NOK;
if ((buff == NULL) || (bytes == 0)) {
VFS_ERRNO_SET(EINVAL);
return ret;
}
file = VfsAttachFileReady(fd);
if (file == NULL) {
return ret;
}
if ((file->fFlags & O_ACCMODE) == O_RDONLY) {
VFS_ERRNO_SET(EACCES);
} else if ((file->fFops != NULL) && (file->fFops->write != NULL)) {
ret = file->fFops->write(file, buff, bytes);
} else {
VFS_ERRNO_SET(ENOTSUP);
}
/* else ret will be -1 */
VfsDetachFile(file);
return ret;
}
static int VfsIoctl(int fd, int func, va_list ap)
{
unsigned long arg;
struct File *file = NULL;
int ret = (int)LOS_NOK;
arg = va_arg(ap, unsigned long);
file = VfsAttachFileReady(fd);
if (file == NULL) {
return ret;
}
if ((file->fFops != NULL) && (file->fFops->ioctl != NULL)) {
ret = file->fFops->ioctl(file, func, arg);
} else {
VFS_ERRNO_SET(ENOTSUP);
}
VfsDetachFile(file);
return ret;
}
static int VfsVfcntl(struct File *filep, int cmd, va_list ap)
{
int ret;
UINT32 flags;
if ((filep == NULL) || (filep->fFops == NULL)) {
return -EBADF;
}
if (cmd == F_GETFL) {
ret = (int)(filep->fFlags);
} else if (cmd == F_SETFL) {
flags = (UINT32)va_arg(ap, int);
flags &= LOS_FCNTL;
filep->fFlags &= ~LOS_FCNTL;
filep->fFlags |= flags;
ret = LOS_OK;
} else {
ret = -ENOSYS;
}
return ret;
}
static int MapToPosixRet(int ret)
{
return ((ret) < 0 ? -1 : (ret));
}
/* POSIX interface */
int open(const char *path, int flags, ...)
{
if (path == NULL) {
errno = EINVAL;
return (int)LOS_NOK;
}
#ifdef LOSCFG_RANDOM_DEV
unsigned flagMask = O_RDONLY | O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_LARGEFILE \
| O_TRUNC | O_EXCL | O_DIRECTORY;
if ((unsigned)flags & ~flagMask) {
errno = EINVAL;
return (int)LOS_NOK;
}
size_t pathLen = strlen(path) + 1;
if ((unsigned)pathLen > PATH_MAX) {
errno = EINVAL;
return (int)LOS_NOK;
}
char *canonicalPath = (char *)LOSCFG_FS_MALLOC_HOOK(pathLen);
if (!canonicalPath) {
errno = ENOMEM;
return (int)LOS_NOK;
}
if (GetCanonicalPath(NULL, path, canonicalPath, pathLen) == 0) {
FREE_AND_SET_NULL(canonicalPath);
errno = ENOMEM;
return (int)LOS_NOK;
}
if (strcmp(canonicalPath, RANDOM_DEV_PATH) == 0) {
FREE_AND_SET_NULL(canonicalPath);
if ((O_ACCMODE & (unsigned)flags) != O_RDONLY) {
errno = EPERM;
return (int)LOS_NOK;
}
if ((unsigned)flags & O_DIRECTORY) {
errno = ENOTDIR;
return (int)LOS_NOK;
}
return RANDOM_DEV_FD;
}
if ((strcmp(canonicalPath, "/") == 0) ||
(strcmp(canonicalPath, "/dev") == 0)) {
FREE_AND_SET_NULL(canonicalPath);
if ((unsigned)flags & O_DIRECTORY) {
errno = EPERM;
return (int)LOS_NOK;
}
errno = EISDIR;
return (int)LOS_NOK;
}
FREE_AND_SET_NULL(canonicalPath);
#endif
#if (LOSCFG_POSIX_PIPE_API == 1)
if (!strncmp(path, PIPE_DEV_PATH, strlen(PIPE_DEV_PATH))) {
return PipeOpen(path, flags, PIPE_DEV_FD);
}
#endif
int ret = VfsOpen(path, flags);
return MapToPosixRet(ret);
}
#if (LOSCFG_LIBC_NEWLIB == 1)
FUNC_ALIAS(open, _open, (const char *path, int flags, ...), int);
#endif
int close(int fd)
{
#ifdef LOSCFG_RANDOM_DEV
if (fd == RANDOM_DEV_FD) {
return LOS_OK;
}
#endif
#ifdef LOSCFG_NET_LWIP_SACK
if (fd >= CONFIG_NFILE_DESCRIPTORS && fd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS)) {
return closesocket(fd);
}
#endif /* LOSCFG_NET_LWIP_SACK */
#if (LOSCFG_POSIX_PIPE_API == 1)
if (fd >= PIPE_DEV_FD) {
return PipeClose(fd);
}
#endif
int ret = (int)LOS_NOK;
if (fd >= MIN_START_FD && fd < CONFIG_NFILE_DESCRIPTORS) {
ret = VfsClose(fd);
}
return MapToPosixRet(ret);
}
#if (LOSCFG_LIBC_NEWLIB == 1)
FUNC_ALIAS(close, _close, (int fd), int);
#endif
ssize_t read(int fd, void *buff, size_t bytes)
{
#ifdef LOSCFG_RANDOM_DEV
if (fd == RANDOM_DEV_FD) {
if (nbyte == 0) {
return FS_SUCCESS;
}
if (buf == NULL) {
errno = EINVAL;
return FS_FAILURE;
}
if (nbyte > 1024) { /* 1024, max random_size */
nbyte = 1024; /* hks_generate_random: random_size must <= 1024 */
}
struct hks_blob key = {HKS_BLOB_TYPE_RAW, (uint8_t *)buf, nbyte};
if (hks_generate_random(&key) != 0) {
errno = EIO;
return FS_FAILURE;
}
return (ssize_t)nbyte;
}
#endif
#ifdef LOSCFG_NET_LWIP_SACK
if (fd >= CONFIG_NFILE_DESCRIPTORS && fd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS)) {
return recv(fd, buff, bytes, 0);
}
#endif /* LOSCFG_NET_LWIP_SACK */
#if (LOSCFG_POSIX_PIPE_API == 1)
if (fd >= PIPE_DEV_FD) {
return PipeRead(fd, buff, bytes);
}
#endif
ssize_t ret = (ssize_t)LOS_NOK;
if (fd >= MIN_START_FD && fd < CONFIG_NFILE_DESCRIPTORS) {
ret = VfsRead(fd, buff, bytes);
}
return MapToPosixRet(ret);
}
#if (LOSCFG_LIBC_NEWLIB == 1)
FUNC_ALIAS(read, _read, (int fd, void *buff, size_t bytes), ssize_t);
#endif
ssize_t write(int fd, const void *buff, size_t bytes)
{
#ifdef LOSCFG_RANDOM_DEV
if (fd == RANDOM_DEV_FD) {
errno = EBADF; /* "/dev/random" is readonly */
return (ssize_t)LOS_NOK;
}
#endif
#ifdef LOSCFG_NET_LWIP_SACK
if (fd >= CONFIG_NFILE_DESCRIPTORS &&
fd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS)) {
return send(fd, buff, bytes, 0);
}
#endif /* LOSCFG_NET_LWIP_SACK */
#if (LOSCFG_POSIX_PIPE_API == 1)
if (fd >= PIPE_DEV_FD) {
return PipeWrite(fd, buff, bytes);
}
#endif
ssize_t ret = (ssize_t)LOS_NOK;
if (fd >= MIN_START_FD && fd < CONFIG_NFILE_DESCRIPTORS) {
ret = VfsWrite(fd, buff, bytes);
}
return MapToPosixRet(ret);
}
#if (LOSCFG_LIBC_NEWLIB == 1)
FUNC_ALIAS(write, _write, (int fd, const void *buff, size_t bytes), ssize_t);
#endif
off_t lseek(int fd, off_t off, int whence)
{
struct File *file;
off_t ret = (off_t)LOS_NOK;
file = VfsAttachFileReady(fd);
if (file == NULL) {
return ret;
}
if ((file->fFops == NULL) || (file->fFops->lseek == NULL)) {
ret = file->fOffset;
} else {
ret = file->fFops->lseek(file, off, whence);
}
VfsDetachFile(file);
return ret;
}
#if (LOSCFG_LIBC_NEWLIB == 1)
FUNC_ALIAS(lseek, _lseek, (int fd, off_t off, int whence), off_t);
#endif
int stat(const char *path, struct stat *stat)
{
struct MountPoint *mp = NULL;
const char *pathInMp = NULL;
int ret = (int)LOS_NOK;
if (VfsPathCheck(path, FALSE) != LOS_OK) {
return MapToPosixRet(ret);
}
if (stat == NULL) {
VFS_ERRNO_SET(EINVAL);
return MapToPosixRet(ret);
}
if (LOS_FsLock() != LOS_OK) {
VFS_ERRNO_SET(EAGAIN);
return MapToPosixRet(ret);
}
mp = VfsMpFind(path, &pathInMp);
if ((mp == NULL) || (pathInMp == NULL) || (*pathInMp == '\0')) {
VFS_ERRNO_SET(ENOENT);
LOS_FsUnlock();
return MapToPosixRet(ret);
}
if (mp->mFs->fsFops->stat != NULL) {
ret = mp->mFs->fsFops->stat(mp, pathInMp, stat);
} else {
VFS_ERRNO_SET(ENOTSUP);
}
LOS_FsUnlock();
return MapToPosixRet(ret);
}
#if (LOSCFG_LIBC_NEWLIB == 1)
FUNC_ALIAS(stat, _stat, (const char *path, struct stat *stat), int);
#endif
int statfs(const char *path, struct statfs *buf)
{
struct MountPoint *mp = NULL;
const char *pathInMp = NULL;
int ret = (int)LOS_NOK;
if (VfsPathCheck(path, FALSE) != LOS_OK) {
return MapToPosixRet(ret);
}
if (buf == NULL) {
VFS_ERRNO_SET(EINVAL);
return MapToPosixRet(ret);
}
if (LOS_FsLock() != LOS_OK) {
VFS_ERRNO_SET(EAGAIN);
return MapToPosixRet(ret);
}
mp = VfsMpFind(path, &pathInMp);
if ((mp == NULL) || (pathInMp == NULL) || (*pathInMp == '\0')) {
VFS_ERRNO_SET(ENOENT);
LOS_FsUnlock();
return MapToPosixRet(ret);
}
if (mp->mFs->fsMops->statfs != NULL) {
ret = mp->mFs->fsMops->statfs(pathInMp, buf);
} else {
VFS_ERRNO_SET(ENOTSUP);
}
LOS_FsUnlock();
return MapToPosixRet(ret);
}
int unlink(const char *path)
{
struct MountPoint *mp = NULL;
const char *pathInMp = NULL;
int ret = (int)LOS_NOK;
if (VfsPathCheck(path, FALSE) != LOS_OK) {
return MapToPosixRet(ret);
}
if (LOS_FsLock() != LOS_OK) {
VFS_ERRNO_SET(EAGAIN);
return MapToPosixRet(ret);
}
mp = VfsMpFind(path, &pathInMp);
if ((mp == NULL) || (pathInMp == NULL) || (*pathInMp == '\0') ||
(mp->mFs->fsFops->unlink == NULL)) {
VFS_ERRNO_SET(ENOENT);
LOS_FsUnlock();
return MapToPosixRet(ret);
}
ret = mp->mFs->fsFops->unlink(mp, pathInMp);
LOS_FsUnlock();
return MapToPosixRet(ret);
}
#if (LOSCFG_LIBC_NEWLIB == 1)
FUNC_ALIAS(unlink, _unlink, (const char *path), int);
#endif
int rename(const char *oldpath, const char *newpath)
{
struct MountPoint *mpOld = NULL;
struct MountPoint *mpNew = NULL;
const char *pathInMpOld = NULL;
const char *pathInMpNew = NULL;
int ret = (int)LOS_NOK;
if (VfsPathCheck(oldpath, FALSE) != LOS_OK) {
return MapToPosixRet(ret);
}
if (VfsPathCheck(newpath, FALSE) != LOS_OK) {
return MapToPosixRet(ret);
}
if (LOS_FsLock() != LOS_OK) {
VFS_ERRNO_SET(EAGAIN);
return MapToPosixRet(ret);
}
mpOld = VfsMpFind(oldpath, &pathInMpOld);
if (pathInMpOld == NULL) {
VFS_ERRNO_SET(EINVAL);
LOS_FsUnlock();
return MapToPosixRet(ret);
}
if ((mpOld == NULL) || (*pathInMpOld == '\0') ||
(mpOld->mFs->fsFops->unlink == NULL)) {
VFS_ERRNO_SET(EINVAL);
LOS_FsUnlock();
return MapToPosixRet(ret);
}
mpNew = VfsMpFind(newpath, &pathInMpNew);
if ((mpNew == NULL) || (pathInMpNew == NULL) || (*pathInMpNew == '\0') || (mpNew->mFs->fsFops->unlink == NULL)) {
VFS_ERRNO_SET(EINVAL);
LOS_FsUnlock();
return MapToPosixRet(ret);
}
if (mpOld != mpNew) {
VFS_ERRNO_SET(EXDEV);
LOS_FsUnlock();
return MapToPosixRet(ret);
}
if (mpOld->mFs->fsFops->rename != NULL) {
ret = mpOld->mFs->fsFops->rename(mpOld, pathInMpOld, pathInMpNew);
} else {
VFS_ERRNO_SET(ENOTSUP);
}
LOS_FsUnlock();
return MapToPosixRet(ret);
}
int fsync(int fd)
{
struct File *file;
int ret = (int)LOS_NOK;
file = VfsAttachFileReady(fd);
if (file == NULL) {
return MapToPosixRet(ret);
}
if (file->fMp->mWriteEnable == FALSE) {
VFS_ERRNO_SET(EACCES);
VfsDetachFile(file);
return MapToPosixRet(ret);
}
if ((file->fFops != NULL) && (file->fFops->sync != NULL)) {
ret = file->fFops->sync(file);
} else {
VFS_ERRNO_SET(ENOTSUP);
}
VfsDetachFile(file);
return MapToPosixRet(ret);
}
DIR *opendir(const char *path)
{
struct MountPoint *mp = NULL;
const char *pathInMp = NULL;
struct Dir *dir = NULL;
UINT32 ret;
if (VfsPathCheck(path, FALSE) != LOS_OK) {
return NULL;
}
dir = (struct Dir *)LOSCFG_FS_MALLOC_HOOK(sizeof(struct Dir));
if (dir == NULL) {
VFS_ERRNO_SET(ENOMEM);
return NULL;
}
if (LOS_FsLock() != LOS_OK) {
VFS_ERRNO_SET(EAGAIN);
LOSCFG_FS_FREE_HOOK(dir);
return NULL;
}
if (g_dirNum >= LOSCFG_MAX_OPEN_DIRS) {
VFS_ERRNO_SET(ENFILE);
LOS_FsUnlock();
LOSCFG_FS_FREE_HOOK(dir);
return NULL;
}
mp = VfsMpFind(path, &pathInMp);
if ((mp == NULL) || (pathInMp == NULL)) {
VFS_ERRNO_SET(ENOENT);
LOS_FsUnlock();
LOSCFG_FS_FREE_HOOK(dir);
return NULL;
}
if (mp->mFs->fsFops->opendir == NULL) {
VFS_ERRNO_SET(ENOTSUP);
LOS_FsUnlock();
LOSCFG_FS_FREE_HOOK(dir);
return NULL;
}
dir->dMp = mp;
dir->dOffset = 0;
ret = (UINT32)mp->mFs->fsFops->opendir(dir, pathInMp);
if (ret == 0) {
mp->mRefs++;
g_dirNum++;
} else {
LOSCFG_FS_FREE_HOOK(dir);
dir = NULL;
}
LOS_FsUnlock();
return (DIR *)dir;
}
struct dirent *readdir(DIR *dir)
{
struct dirent *ret = NULL;
struct Dir *d = (struct Dir *)dir;
if ((dir == NULL) || (d->dMp == NULL)) {
VFS_ERRNO_SET(EINVAL);
return NULL;
}
if (LOS_FsLock() != LOS_OK) {
VFS_ERRNO_SET(EAGAIN);
return NULL;
}
if ((d->dMp->mFs != NULL) && (d->dMp->mFs->fsFops != NULL) &&
(d->dMp->mFs->fsFops->readdir != NULL)) {
if (d->dMp->mFs->fsFops->readdir(d, &d->dDent) == 0) {
ret = &d->dDent;
}
} else {
VFS_ERRNO_SET(ENOTSUP);
}
LOS_FsUnlock();
return ret;
}
int closedir(DIR *dir)
{
struct MountPoint *mp = NULL;
int ret = (int)LOS_NOK;
struct Dir *d = (struct Dir *)dir;
if ((d == NULL) || (d->dMp == NULL)) {
VFS_ERRNO_SET(EBADF);
return MapToPosixRet(ret);
}
mp = d->dMp;
if (LOS_FsLock() != LOS_OK) {
VFS_ERRNO_SET(EAGAIN);
return MapToPosixRet(ret);
}
if ((d->dMp->mFs != NULL) && (d->dMp->mFs->fsFops != NULL) &&
(d->dMp->mFs->fsFops->closedir != NULL)) {
ret = d->dMp->mFs->fsFops->closedir(d);
} else {
VFS_ERRNO_SET(ENOTSUP);
}
if (ret == 0) {
mp->mRefs--;
g_dirNum--;
} else {
VFS_ERRNO_SET(EBADF);
}
LOS_FsUnlock();
LOSCFG_FS_FREE_HOOK(d);
d = NULL;
return MapToPosixRet(ret);
}
int mkdir(const char *path, mode_t mode)
{
struct MountPoint *mp = NULL;
const char *pathInMp = NULL;
int ret = (int)LOS_NOK;
(void)mode;
if (VfsPathCheck(path, FALSE) != LOS_OK) {
return MapToPosixRet(ret);
}
if (LOS_FsLock() != LOS_OK) {
VFS_ERRNO_SET(EAGAIN);
return MapToPosixRet(ret);
}
mp = VfsMpFind(path, &pathInMp);
if ((mp == NULL) || (pathInMp == NULL) || (*pathInMp == '\0')) {
VFS_ERRNO_SET(ENOENT);
LOS_FsUnlock();
return MapToPosixRet(ret);
}
if (mp->mFs->fsFops->mkdir != NULL) {
ret = mp->mFs->fsFops->mkdir(mp, pathInMp);
} else {
VFS_ERRNO_SET(ENOTSUP);
}
LOS_FsUnlock();
return MapToPosixRet(ret);
}
int rmdir(const char *path)
{
struct MountPoint *mp = NULL;
const char *pathInMp = NULL;
int ret = (int)LOS_NOK;
if (path == NULL) {
VFS_ERRNO_SET(EINVAL);
return MapToPosixRet(ret);
}
if (LOS_FsLock() != LOS_OK) {
VFS_ERRNO_SET(EAGAIN);
return MapToPosixRet(ret);
}
mp = VfsMpFind(path, &pathInMp);
if ((mp == NULL) || (pathInMp == NULL) || (*pathInMp == '\0') ||
(mp->mFs->fsFops->rmdir == NULL)) {
VFS_ERRNO_SET(ENOENT);
LOS_FsUnlock();
return MapToPosixRet(ret);
}
ret = mp->mFs->fsFops->rmdir(mp, pathInMp);
LOS_FsUnlock();
return MapToPosixRet(ret);
}
int lstat(const char *path, struct stat *buffer)
{
return stat(path, buffer);
}
int fstat(int fd, struct stat *buf)
{
struct File *filep;
int ret;
filep = VfsAttachFileReady(fd);
if ((filep == NULL) || (filep->fMp == NULL) || filep->fullPath == NULL) {
return (int)LOS_NOK;
}
ret = stat(filep->fullPath, buf);
VfsDetachFile(filep);
return ret;
}
#if (LOSCFG_LIBC_NEWLIB == 1)
FUNC_ALIAS(fstat, _fstat, (int fd, struct stat *buf), int);
#endif
int fcntl(int fd, int cmd, ...)
{
struct File *filep = NULL;
int ret;
va_list ap;
va_start(ap, cmd);
if (fd < CONFIG_NFILE_DESCRIPTORS) {
filep = VfsAttachFileReady(fd);
ret = VfsVfcntl(filep, cmd, ap);
VfsDetachFile(filep);
} else {
#ifndef LOSCFG_NET_LWIP_SACK
ret = -EBADF;
#else
int arg = va_arg(ap, int);
ret = lwip_fcntl(fd, (long)cmd, arg);
va_end(ap);
return ret;
#endif /* LOSCFG_NET_LWIP_SACK */
}
if (ret < 0) {
VFS_ERRNO_SET(-ret);
ret = (int)LOS_NOK;
}
va_end(ap);
return ret;
}
int ioctl(int fd, int req, ...)
{
int ret;
va_list ap;
va_start(ap, req);
if (fd < CONFIG_NFILE_DESCRIPTORS) {
ret = VfsIoctl(fd, req, ap);
} else {
#ifndef LOSCFG_NET_LWIP_SACK
ret = -EBADF;
#else
UINTPTR arg = va_arg(ap, UINTPTR);
ret = lwip_ioctl(fd, (long)req, (void *)arg);
#endif /* LOSCFG_NET_LWIP_SACK */
}
va_end(ap);
return ret;
}
ssize_t readv(int fd, const struct iovec *iovBuf, int iovcnt)
{
int i;
errno_t ret;
char *buf = NULL;
char *curBuf = NULL;
char *readBuf = NULL;
size_t bufLen = 0;
size_t bytesToRead;
ssize_t totalBytesRead;
size_t totalLen;
const struct iovec *iov = (const struct iovec *)iovBuf;
if ((iov == NULL) || (iovcnt <= 0) || (iovcnt > IOV_MAX_CNT)) {
return (ssize_t)LOS_NOK;
}
for (i = 0; i < iovcnt; ++i) {
if ((SSIZE_MAX - bufLen) < iov[i].iov_len) {
return (ssize_t)LOS_NOK;
}
bufLen += iov[i].iov_len;
}
if (bufLen == 0) {
return (ssize_t)LOS_NOK;
}
totalLen = bufLen * sizeof(char);
buf = (char *)LOSCFG_FS_MALLOC_HOOK(totalLen);
if (buf == NULL) {
return (ssize_t)LOS_NOK;
}
totalBytesRead = read(fd, buf, bufLen);
if ((size_t)totalBytesRead < totalLen) {
totalLen = (size_t)totalBytesRead;
}
curBuf = buf;
for (i = 0; i < iovcnt; ++i) {
readBuf = (char *)iov[i].iov_base;
bytesToRead = iov[i].iov_len;
size_t lenToRead = totalLen < bytesToRead ? totalLen : bytesToRead;
ret = memcpy_s(readBuf, bytesToRead, curBuf, lenToRead);
if (ret != EOK) {
LOSCFG_FS_FREE_HOOK(buf);
return (ssize_t)LOS_NOK;
}
if (totalLen < (size_t)bytesToRead) {
break;
}
curBuf += bytesToRead;
totalLen -= bytesToRead;
}
LOSCFG_FS_FREE_HOOK(buf);
return totalBytesRead;
}
ssize_t writev(int fd, const struct iovec *iovBuf, int iovcnt)
{
int i;
errno_t ret;
char *buf = NULL;
char *curBuf = NULL;
char *writeBuf = NULL;
size_t bufLen = 0;
size_t bytesToWrite;
ssize_t totalBytesWritten;
size_t totalLen;
const struct iovec *iov = iovBuf;
if ((iov == NULL) || (iovcnt <= 0) || (iovcnt > IOV_MAX_CNT)) {
return (ssize_t)LOS_NOK;
}
for (i = 0; i < iovcnt; ++i) {
if ((SSIZE_MAX - bufLen) < iov[i].iov_len) {
VFS_ERRNO_SET(EINVAL);
return (ssize_t)LOS_NOK;
}
bufLen += iov[i].iov_len;
}
if (bufLen == 0) {
return (ssize_t)LOS_NOK;
}
totalLen = bufLen * sizeof(char);
buf = (char *)LOSCFG_FS_MALLOC_HOOK(totalLen);
if (buf == NULL) {
return (ssize_t)LOS_NOK;
}
curBuf = buf;
for (i = 0; i < iovcnt; ++i) {
writeBuf = (char *)iov[i].iov_base;
bytesToWrite = iov[i].iov_len;
if (((ssize_t)totalLen <= 0) || ((ssize_t)bytesToWrite <= 0)) {
continue;
}
ret = memcpy_s(curBuf, totalLen, writeBuf, bytesToWrite);
if (ret != EOK) {
LOSCFG_FS_FREE_HOOK(buf);
return (ssize_t)LOS_NOK;
}
curBuf += bytesToWrite;
totalLen -= bytesToWrite;
}
totalBytesWritten = write(fd, buf, bufLen);
LOSCFG_FS_FREE_HOOK(buf);
return totalBytesWritten;
}
int remove(const char *filename)
{
int ret = unlink(filename);
if (ret == -EISDIR) {
ret = rmdir(filename);
}
return ret;
}
int access(const char *path, int amode)
{
int result;
mode_t mode;
struct stat buf;
result = stat(path, &buf);
if (result != 0) {
return (int)LOS_NOK;
}
mode = buf.st_mode;
if ((unsigned int)amode & R_OK) {
if ((mode & (S_IROTH | S_IRGRP | S_IRUSR)) == 0) {
VFS_ERRNO_SET(EACCES);
return (int)LOS_NOK;
}
}
if ((unsigned int)amode & W_OK) {
if ((mode & (S_IWOTH | S_IWGRP | S_IWUSR)) == 0) {
VFS_ERRNO_SET(EACCES);
return (int)LOS_NOK;
}
}
if ((unsigned int)amode & X_OK) {
if ((mode & (S_IXOTH | S_IXGRP | S_IXUSR)) == 0) {
VFS_ERRNO_SET(EACCES);
return (int)LOS_NOK;
}
}
return 0;
}
int ftruncate(int fd, off_t length)
{
int ret = (int)LOS_NOK;
struct File *file = NULL;
if (length <= 0) {
VFS_ERRNO_SET(EINVAL);
return ret;
}
file = VfsAttachFileReady(fd);
if (file == NULL) {
return ret;
}
if (file->fMp->mWriteEnable == FALSE) {
VFS_ERRNO_SET(EACCES);
VfsDetachFile(file);
return ret;
}
if ((file->fFlags & O_ACCMODE) == O_RDONLY) {
VFS_ERRNO_SET(EACCES);
} else if ((file->fFops != NULL) && (file->fFops->truncate != NULL)) {
ret = file->fFops->truncate(file, length);
} else {
VFS_ERRNO_SET(ENOTSUP);
}
/* else ret will be -1 */
VfsDetachFile(file);
return ret;
}
ssize_t pread(int fd, void *buff, size_t bytes, off_t off)
{
ssize_t ret = (ssize_t)LOS_NOK;
off_t savepos, pos;
if (fd < 0 || fd >= CONFIG_NFILE_DESCRIPTORS) {
return MapToPosixRet((int)ret);
}
if (buff == NULL) {
VFS_ERRNO_SET(EFAULT);
return MapToPosixRet((int)ret);
}
if (bytes == 0) {
return 0;
}
if (LOS_FsLock() != LOS_OK) {
VFS_ERRNO_SET(EAGAIN);
return MapToPosixRet((int)ret);
}
savepos = lseek(fd, 0, SEEK_CUR);
if (savepos == (off_t)-1) {
LOS_FsUnlock();
return MapToPosixRet((int)ret);
}
pos = lseek(fd, off, SEEK_SET);
if (pos == (off_t)-1) {
LOS_FsUnlock();
return MapToPosixRet((int)ret);
}
ret = read(fd, buff, bytes);
pos = lseek(fd, savepos, SEEK_SET);
if ((pos == (off_t)-1) && (ret >= 0)) {
LOS_FsUnlock();
return MapToPosixRet((int)LOS_NOK);
}
LOS_FsUnlock();
return MapToPosixRet((int)ret);
}
ssize_t pwrite(int fd, const void *buff, size_t bytes, off_t off)
{
ssize_t ret = (ssize_t)LOS_NOK;
off_t savepos, pos;
if ((fd < 0) || (fd >= CONFIG_NFILE_DESCRIPTORS)) {
return MapToPosixRet((int)ret);
}
if (buff == NULL) {
VFS_ERRNO_SET(EFAULT);
return MapToPosixRet((int)ret);
}
if (bytes == 0) {
return 0;
}
if (LOS_FsLock() != LOS_OK) {
VFS_ERRNO_SET(EAGAIN);
return MapToPosixRet((int)ret);
}
savepos = lseek(fd, 0, SEEK_CUR);
if (savepos == (off_t)-1) {
LOS_FsUnlock();
return MapToPosixRet((int)ret);
}
pos = lseek(fd, off, SEEK_SET);
if (pos == (off_t)-1) {
LOS_FsUnlock();
return MapToPosixRet((int)ret);
}
ret = write(fd, buff, bytes);
pos = lseek(fd, savepos, SEEK_SET);
if ((pos == (off_t)-1) && (ret >= 0)) {
LOS_FsUnlock();
return MapToPosixRet((int)LOS_NOK);
}
LOS_FsUnlock();
return MapToPosixRet((int)ret);
}