/* * Copyright (c) 2006-2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-05-05 Bernard Implement file APIs in dfs v2.0 */ #include "errno.h" #include "fcntl.h" #include #include "dfs_file.h" #include "dfs_dentry.h" #include "dfs_fs.h" #include "dfs_mnt.h" #include "dfs_private.h" #ifdef RT_USING_PAGECACHE #include "dfs_pcache.h" #endif #define DBG_TAG "DFS.file" #define DBG_LVL DBG_WARNING #include #define MAX_RW_COUNT 0xfffc0000 rt_inline int _first_path_len(const char *path) { int i = 0; if (path[i] == '/') { i++; while (path[i] != '\0' && path[i] != '/') { i++; } } return i; } static int _get_parent_path(const char *fullpath, char *path) { int len = 0; char *str = 0; str = strrchr(fullpath, '/'); /* skip last '/' */ if (str && *(str + 1) == '\0') { str = strrchr(str - 1, '/'); } if (str) { len = str - fullpath; if (len > 0) { rt_memcpy(path, fullpath, len); path[len] = '\0'; } } return len; } static int _try_readlink(const char *path, struct dfs_mnt *mnt, char *link) { int ret = -1; struct dfs_dentry *dentry = dfs_dentry_lookup(mnt, path, 0); if (dentry && dentry->vnode->type == FT_SYMLINK) { if (mnt->fs_ops->readlink) { if (dfs_is_mounted(mnt) == 0) { ret = mnt->fs_ops->readlink(dentry, link, DFS_PATH_MAX); } } } dfs_dentry_unref(dentry); return ret; } static char *_dfs_normalize_path(const char *path, int path_len, const char *link_fn, int link_len) { char *tmp_path, *fp; tmp_path = (char *)rt_malloc(path_len + link_len + 2); if (!tmp_path) { return RT_NULL; } memcpy(tmp_path, path, path_len); tmp_path[path_len] = '/'; memcpy(tmp_path + path_len + 1, link_fn, link_len); tmp_path[path_len + 1 + link_len] = '\0'; fp = dfs_normalize_path(NULL, tmp_path); rt_free(tmp_path); return fp; } static int _insert_link_path(const char *link_fn, int link_len, char *tmp_path, int *index) { int ret = -1; if (link_fn[0] != '/') { if (link_len + 1 <= *index) { *index -= link_len; rt_memcpy(tmp_path + *index, link_fn, link_len); *index -= 1; tmp_path[*index] = '/'; ret = 0; } } else if (link_len <= *index) { *index -= link_len; rt_memcpy(tmp_path + *index, link_fn, link_len); ret = 1; } return ret; } /* * rw_verify_area doesn't like huge counts. We limit * them to something that fits in "int" so that others * won't have to do range checks all the time. */ ssize_t rw_verify_area(struct dfs_file *file, off_t *ppos, size_t count) { off_t pos; ssize_t retval = -EINVAL; if ((size_t)count < 0) return retval; pos = *ppos; if (pos < 0) { if (count >= -pos) /* both values are in 0..LLONG_MAX */ return -EOVERFLOW; } return count > MAX_RW_COUNT ? MAX_RW_COUNT : count; } off_t dfs_file_get_fpos(struct dfs_file *file) { if (file) { if (file->vnode->type == FT_REGULAR) { rt_mutex_take(&file->pos_lock, RT_WAITING_FOREVER); } return file->fpos; } return 0; } void dfs_file_set_fpos(struct dfs_file *file, off_t fpos) { if (file) { if (file->vnode->type != FT_REGULAR) { rt_mutex_take(&file->pos_lock, RT_WAITING_FOREVER); } file->fpos = fpos; rt_mutex_release(&file->pos_lock); } } void dfs_file_init(struct dfs_file *file) { if (file) { rt_memset(file, 0x00, sizeof(struct dfs_file)); file->magic = DFS_FD_MAGIC; rt_mutex_init(&file->pos_lock, "fpos", RT_IPC_FLAG_PRIO); rt_atomic_store(&(file->ref_count), 1); } } void dfs_file_deinit(struct dfs_file *file) { if (file) { rt_mutex_detach(&file->pos_lock); } } static void dfs_file_unref(struct dfs_file *file) { rt_err_t ret = RT_EOK; ret = dfs_file_lock(); if (ret == RT_EOK) { if (rt_atomic_load(&(file->ref_count)) == 1) { /* should release this file */ if (file->dentry) { DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_unref(dentry(%s))", file->dentry->pathname); dfs_dentry_unref(file->dentry); file->dentry = RT_NULL; } else if (file->vnode) { if (file->vnode->ref_count > 1) { rt_atomic_sub(&(file->vnode->ref_count), 1); } else if (file->vnode->ref_count == 1) { rt_free(file->vnode); file->vnode = RT_NULL; } } LOG_I("release a file: %p", file); } dfs_file_unlock(); } } /* * this function is creat a nolink path. * * @param mnt * @param fullpath * @param mode * * @return new path. */ char *dfs_file_realpath(struct dfs_mnt **mnt, const char *fullpath, int mode) { int path_len = 0, index = 0; char *path = RT_NULL, *link_fn, *tmp_path; struct dfs_mnt *tmp_mnt; if (*mnt && fullpath) { int len, link_len; path = (char *)rt_malloc((DFS_PATH_MAX * 3) + 3); // path + \0 + link_fn + \0 + tmp_path + \0 if (!path) { return RT_NULL; } link_fn = path + DFS_PATH_MAX + 1; tmp_path = link_fn + (DFS_PATH_MAX + 1); len = rt_strlen(fullpath); if (len > DFS_PATH_MAX) { goto _ERR_RET; } index = (DFS_PATH_MAX - len); rt_strcpy(tmp_path + index, fullpath); if (mode == DFS_REALPATH_ONLY_LAST) { path_len = _get_parent_path(fullpath, path); index += path_len; } while ((len = _first_path_len(tmp_path + index)) > 0) { if (len + path_len > DFS_PATH_MAX) { goto _ERR_RET; } rt_memcpy(path + path_len, tmp_path + index, len); path[path_len + len] = '\0'; index += len; tmp_mnt = dfs_mnt_lookup(path); if (tmp_mnt == RT_NULL) { goto _ERR_RET; } *mnt = tmp_mnt; /* the last should by mode process. */ if ((tmp_path[index] == '\0') && (mode == DFS_REALPATH_EXCEPT_LAST)) { break; } link_len = _try_readlink(path, *mnt, link_fn); if (link_len > 0) { if (link_fn[0] == '/') { int ret = _insert_link_path(link_fn, link_len, tmp_path, &index); if (ret < 0) { goto _ERR_RET; } path_len = 0; } else { char *fp = _dfs_normalize_path(path, path_len, link_fn, link_len); if (fp) { int pos = rt_strncmp(path, fp, path_len); if (pos == 0) { int ret = _insert_link_path(fp + path_len, rt_strlen(fp + path_len), tmp_path, &index); if (ret < 0) { rt_free(fp); goto _ERR_RET; } } else { int pos; while(1) { while(path_len > 0 && path[path_len] != '/') { path_len--; } if (path_len > 0) { pos = rt_strncmp(path, fp, path_len); } else { pos = -1; } if (pos == 0 || path_len == 0) { int ret; ret = _insert_link_path(fp + path_len, rt_strlen(fp + path_len), tmp_path, &index); if (ret < 0) { rt_free(fp); goto _ERR_RET; } else { break; } } else { path_len--; } } } rt_free(fp); } } } else { path_len += len; } } return path; _ERR_RET: rt_free(path); path = RT_NULL; } return path; } /** * this function will open a file which specified by path with specified oflags. * * @param fd the file descriptor pointer to return the corresponding result. * @param path the specified file path. * @param oflags the oflags for open operator. * * @return 0 on successful, -1 on failed. */ int dfs_file_open(struct dfs_file *file, const char *path, int oflags, mode_t mode) { int ret = -RT_ERROR; char *fullpath = RT_NULL; struct dfs_dentry *dentry = RT_NULL; int fflags = dfs_fflags(oflags); if (mode == 0) { mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); /* 0666 */ } if (file && path) { fullpath = dfs_normalize_path(NULL, path); if (fullpath) { struct dfs_mnt *mnt = RT_NULL; DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath); mnt = dfs_mnt_lookup(fullpath); if (mnt) { char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST); if (tmp) { rt_free(fullpath); fullpath = tmp; } DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_lookup(mnt, %s)", fullpath); dentry = dfs_dentry_lookup(mnt, fullpath, oflags); if (dentry && dentry->vnode->type == FT_SYMLINK) { /* it's a symbol link but not follow */ if (oflags & O_NOFOLLOW) { /* no follow symbol link */ dfs_dentry_unref(dentry); dentry = RT_NULL; } else { struct dfs_dentry *target_dentry = RT_NULL; char *path = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_ONLY_LAST); if (path) { target_dentry = dfs_dentry_lookup(mnt, path, oflags); rt_free(path); } dfs_dentry_unref(dentry); dentry = target_dentry; } } if (dentry) { if (oflags & O_DIRECTORY) { if (dentry->vnode->type != FT_DIRECTORY) { dfs_dentry_unref(dentry); dentry = RT_NULL; } } else if (dentry->vnode->type == FT_DIRECTORY) { if (fflags & (DFS_F_FWRITE)) { dfs_dentry_unref(dentry); dentry = RT_NULL; } else { oflags |= O_DIRECTORY; } } } if (oflags & O_CREAT) { if (dentry) { oflags &= ~O_CREAT; if (oflags & O_EXCL) { oflags &= ~O_EXCL; /* the dentry already exists */ dfs_dentry_unref(dentry); ret = -EEXIST; goto _ERR_RET; } } else { /* create file/directory */ if (mnt->fs_ops->create_vnode) { struct dfs_vnode *vnode = RT_NULL; DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_create(%s)", fullpath); dfs_file_lock(); dentry = dfs_dentry_create(mnt, fullpath); if (dentry) { DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "fs_ops->create_vnode"); if (dfs_is_mounted(mnt) == 0) { vnode = mnt->fs_ops->create_vnode(dentry, oflags & O_DIRECTORY ? FT_DIRECTORY:FT_REGULAR, mode); } if (vnode) { /* set vnode */ dentry->vnode = vnode; /* the refcount of created vnode is 1. no need to reference */ dfs_dentry_insert(dentry); } else { DLOG(msg, mnt->fs_ops->name, "dfs_file", DLOG_MSG_RET, "create failed."); dfs_dentry_unref(dentry); dentry = RT_NULL; } } dfs_file_unlock(); } } } if (dentry) { rt_bool_t permission = RT_TRUE; file->dentry = dentry; file->vnode = dentry->vnode; file->fops = dentry->mnt->fs_ops->default_fops; file->flags = oflags; /* check permission */ if (!(oflags & O_CREAT)) { if (fflags & DFS_F_FWRITE) { if (!(file->vnode->mode & S_IWUSR)) { permission = RT_FALSE; } } if (fflags & DFS_F_FREAD) { if (!(file->vnode->mode & S_IRUSR)) { permission = RT_FALSE; } } if (oflags & O_EXEC) { if (!(file->vnode->mode & S_IXUSR)) { permission = RT_FALSE; } } } if (permission && file->fops->open) { DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "fops->open(file)"); if (dfs_is_mounted(file->vnode->mnt) == 0) { dfs_file_lock(); ret = file->fops->open(file); dfs_file_unlock(); } else { ret = -EINVAL; } if (ret < 0) { LOG_I("open %s failed in file system: %s", path, dentry->mnt->fs_ops->name); DLOG(msg, mnt->fs_ops->name, "dfs_file", DLOG_MSG_RET, "open failed."); dfs_file_unref(file); } else { /* for char/block device */ if ((S_ISCHR(file->vnode->mode)) || (S_ISBLK(file->vnode->mode))) { file->fops = file->vnode->fops; } } } else { DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "no permission or fops->open"); dfs_file_unref(file); ret = -EPERM; } } else { LOG_I("lookup file:%s failed in file system", path); ret = -ENOENT; } } } if (ret >= 0 && (oflags & O_TRUNC)) { /* trunc file */ if (!(fflags & DFS_F_FWRITE) || file->vnode->type == FT_DIRECTORY) { /* truncate on read a only file or a directory */ DLOG(msg, "dfs_file", "dfs_file", DLOG_MSG, "dfs_file_unref(file), trunc on RDOnly or directory"); ret = -RT_ERROR; } else { if (file->fops->truncate) { DLOG(msg, "dfs_file", dentry->mnt->fs_ops->name, DLOG_MSG, "fops->truncate(file, 0)"); if (dfs_is_mounted(file->vnode->mnt) == 0) { #ifdef RT_USING_PAGECACHE if (file->vnode->aspace) { dfs_aspace_clean(file->vnode->aspace); } #endif ret = file->fops->truncate(file, 0); } else { ret = -EINVAL; } } } if (ret < 0) { dfs_file_unref(file); } file->flags &= ~O_TRUNC; } } _ERR_RET: if (fullpath != NULL) { rt_free(fullpath); } return ret; } int dfs_file_close(struct dfs_file *file) { int ret = -RT_ERROR; if (file) { if (dfs_file_lock() == RT_EOK) { rt_atomic_t ref_count = rt_atomic_load(&(file->ref_count)); if (ref_count == 1 && file->fops && file->fops->close) { DLOG(msg, "dfs_file", file->dentry->mnt->fs_ops->name, DLOG_MSG, "fops->close(file)"); #ifdef RT_USING_PAGECACHE if (file->vnode->aspace) { dfs_aspace_flush(file->vnode->aspace); } #endif ret = file->fops->close(file); if (ret == 0) /* close file sucessfully */ { DLOG(msg, "dfs_file", "dfs_file", DLOG_MSG, "dfs_file_unref(file)"); dfs_file_unref(file); } else { LOG_W("close file:%s failed on low level file system", file->dentry->pathname); } } else { DLOG(msg, "dfs_file", "dfs_file", DLOG_MSG, "dfs_file_unref(file)"); dfs_file_unref(file); ret = 0; } dfs_file_unlock(); } } return ret; } ssize_t dfs_file_pread(struct dfs_file *file, void *buf, size_t len, off_t offset) { ssize_t ret = -EBADF; if (file) { /* check whether read */ if (!(dfs_fflags(file->flags) & DFS_F_FREAD)) { ret = -EPERM; } else if (!file->fops || !file->fops->read) { ret = -ENOSYS; } else if (file->vnode && file->vnode->type != FT_DIRECTORY) { off_t pos = offset; ret = rw_verify_area(file, &pos, len); if (ret > 0) { len = ret; if (dfs_is_mounted(file->vnode->mnt) == 0) { #ifdef RT_USING_PAGECACHE if (file->vnode->aspace && !(file->flags & O_DIRECT)) { ret = dfs_aspace_read(file, buf, len, &pos); } else #endif { ret = file->fops->read(file, buf, len, &pos); } } else { ret = -EINVAL; } } } } return ret; } ssize_t dfs_file_read(struct dfs_file *file, void *buf, size_t len) { ssize_t ret = -EBADF; if (file) { /* check whether read */ if (!(dfs_fflags(file->flags) & DFS_F_FREAD)) { ret = -EPERM; } else if (!file->fops || !file->fops->read) { ret = -ENOSYS; } else if (file->vnode && file->vnode->type != FT_DIRECTORY) { /* fpos lock */ off_t pos = dfs_file_get_fpos(file); ret = rw_verify_area(file, &pos, len); if (ret > 0) { len = ret; if (dfs_is_mounted(file->vnode->mnt) == 0) { #ifdef RT_USING_PAGECACHE if (file->vnode->aspace && !(file->flags & O_DIRECT)) { ret = dfs_aspace_read(file, buf, len, &pos); } else #endif { ret = file->fops->read(file, buf, len, &pos); } } else { ret = -EINVAL; } } /* fpos unlock */ dfs_file_set_fpos(file, pos); } } return ret; } ssize_t dfs_file_pwrite(struct dfs_file *file, const void *buf, size_t len, off_t offset) { ssize_t ret = -EBADF; if (file) { if (!(dfs_fflags(file->flags) & DFS_F_FWRITE)) { LOG_W("bad write flags."); ret = -EBADF; } else if (!file->fops || !file->fops->write) { LOG_W("no fops write."); ret = -ENOSYS; } else if (file->vnode && file->vnode->type != FT_DIRECTORY) { off_t pos = offset; ret = rw_verify_area(file, &pos, len); if (ret > 0) { len = ret; DLOG(msg, "dfs_file", file->dentry->mnt->fs_ops->name, DLOG_MSG, "dfs_file_write(fd, buf, %d)", len); if (dfs_is_mounted(file->vnode->mnt) == 0) { #ifdef RT_USING_PAGECACHE if (file->vnode->aspace && !(file->flags & O_DIRECT)) { ret = dfs_aspace_write(file, buf, len, &pos); } else #endif { ret = file->fops->write(file, buf, len, &pos); } if (file->flags & O_SYNC) { file->fops->flush(file); } } else { ret = -EINVAL; } } } } return ret; } ssize_t dfs_file_write(struct dfs_file *file, const void *buf, size_t len) { ssize_t ret = -EBADF; if (file) { if (!(dfs_fflags(file->flags) & DFS_F_FWRITE)) { LOG_W("bad write flags."); ret = -EBADF; } else if (!file->fops || !file->fops->write) { LOG_W("no fops write."); ret = -ENOSYS; } else if (file->vnode && file->vnode->type != FT_DIRECTORY) { off_t pos; if (!(file->flags & O_APPEND)) { /* fpos lock */ pos = dfs_file_get_fpos(file); } else { pos = file->vnode->size; } ret = rw_verify_area(file, &pos, len); if (ret > 0) { len = ret; DLOG(msg, "dfs_file", file->dentry->mnt->fs_ops->name, DLOG_MSG, "dfs_file_write(fd, buf, %d)", len); if (dfs_is_mounted(file->vnode->mnt) == 0) { #ifdef RT_USING_PAGECACHE if (file->vnode->aspace && !(file->flags & O_DIRECT)) { ret = dfs_aspace_write(file, buf, len, &pos); } else #endif { ret = file->fops->write(file, buf, len, &pos); } if (file->flags & O_SYNC) { file->fops->flush(file); } } else { ret = -EINVAL; } } if (!(file->flags & O_APPEND)) { /* fpos unlock */ dfs_file_set_fpos(file, pos); } } } return ret; } off_t generic_dfs_lseek(struct dfs_file *file, off_t offset, int whence) { off_t foffset; if (whence == SEEK_SET) foffset = offset; else if (whence == SEEK_CUR) foffset = file->fpos + offset; else if (whence == SEEK_END) foffset = file->vnode->size + offset; else return -EINVAL; return foffset; } off_t dfs_file_lseek(struct dfs_file *file, off_t offset, int wherece) { off_t retval = -EINVAL; if (file && file->fops->lseek) { if (dfs_is_mounted(file->vnode->mnt) == 0) { /* fpos lock */ off_t pos = dfs_file_get_fpos(file); retval = file->fops->lseek(file, offset, wherece); if (retval >= 0) { pos = retval; } /* fpos unlock */ dfs_file_set_fpos(file, pos); } } return retval; } int dfs_file_stat(const char *path, struct stat *buf) { int ret = -ENOENT; char *fullpath = RT_NULL; struct dfs_mnt *mnt = RT_NULL; struct dfs_dentry *dentry = RT_NULL; fullpath = dfs_normalize_path(NULL, path); if (fullpath) { DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath); mnt = dfs_mnt_lookup(fullpath); if (mnt) { char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_NONE); if (tmp) { rt_free(fullpath); fullpath = tmp; } DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dentry = dfs_dentry_lookup(mnt, %s)", fullpath); dentry = dfs_dentry_lookup(mnt, fullpath, 0); if (dentry) { DLOG(msg, "dentry", "dfs_file", DLOG_MSG_RET, "return dentry"); if (mnt->fs_ops->stat) { DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "fs_ops->stat(dentry, buf)"); if (dfs_is_mounted(mnt) == 0) { ret = mnt->fs_ops->stat(dentry, buf); } } /* unref dentry */ DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_unref(dentry)"); dfs_dentry_unref(dentry); dentry = RT_NULL; } } rt_free(fullpath); fullpath = RT_NULL; } else { ret = -ENOMEM; } return ret; } int dfs_file_lstat(const char *path, struct stat *buf) { int ret = -ENOENT; char *fullpath = RT_NULL; struct dfs_mnt *mnt = RT_NULL; struct dfs_dentry *dentry = RT_NULL; fullpath = dfs_normalize_path(NULL, path); if (fullpath) { DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath); mnt = dfs_mnt_lookup(fullpath); if (mnt) { char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST); if (tmp) { rt_free(fullpath); fullpath = tmp; } DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dentry = dfs_dentry_lookup(mnt, %s)", fullpath); dentry = dfs_dentry_lookup(mnt, fullpath, 0); if (dentry) { DLOG(msg, "dentry", "dfs_file", DLOG_MSG_RET, "return dentry"); if (mnt->fs_ops->stat) { DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "fs_ops->stat(dentry, buf)"); if (dfs_is_mounted(mnt) == 0) { ret = mnt->fs_ops->stat(dentry, buf); } } /* unref dentry */ DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_unref(dentry)"); dfs_dentry_unref(dentry); dentry = RT_NULL; } } rt_free(fullpath); fullpath = RT_NULL; } else { ret = -ENOMEM; } rt_set_errno(-ret); return ret; } int dfs_file_fstat(struct dfs_file *file, struct stat *buf) { size_t ret = -EBADF; if (file) { if (file->fops && file->fops->ioctl) { // ret = file->fops->fstat(file, buf); } else { ret = -ENOSYS; } } else { ret = -EBADF; } return ret; } int dfs_file_setattr(const char *path, struct dfs_attr *attr) { int ret = -RT_ERROR; char *fullpath = RT_NULL; struct dfs_mnt *mnt = RT_NULL; struct dfs_dentry *dentry = RT_NULL; fullpath = dfs_normalize_path(NULL, path); if (fullpath) { DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath); mnt = dfs_mnt_lookup(fullpath); if (mnt) { char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST); if (tmp) { rt_free(fullpath); fullpath = tmp; } DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dentry = dfs_dentry_lookup(mnt, %s)", fullpath); dentry = dfs_dentry_lookup(mnt, fullpath, 0); if (dentry) { DLOG(msg, "dentry", "dfs_file", DLOG_MSG_RET, "return dentry"); if (mnt->fs_ops->setattr) { DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "fs_ops->setattr(dentry, attr)"); if (dfs_is_mounted(mnt) == 0) { ret = mnt->fs_ops->setattr(dentry, attr); } } /* unref dentry */ DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_unref(dentry)"); dfs_dentry_unref(dentry); dentry = RT_NULL; } } rt_free(fullpath); fullpath = RT_NULL; } return ret; } int dfs_file_ioctl(struct dfs_file *file, int cmd, void *args) { size_t ret = 0; if (file) { if (file->fops && file->fops->ioctl) { if (dfs_is_mounted(file->vnode->mnt) == 0) { ret = file->fops->ioctl(file, cmd, args); } else { ret = -EINVAL; } } else { ret = -ENOSYS; } } else { ret = -EBADF; } return ret; } int dfs_file_fcntl(int fd, int cmd, unsigned long arg) { int ret = 0; struct dfs_file *file; file = fd_get(fd); if (file) { switch (cmd) { case F_DUPFD: ret = dfs_dup(fd, arg); break; case F_GETFD: ret = file->mode; break; case F_SETFD: file->mode = arg; break; case F_GETFL: ret = file->flags; break; case F_SETFL: { int flags = (int)(rt_base_t)arg; int mask = #ifdef O_ASYNC O_ASYNC | #endif #ifdef O_DIRECT O_DIRECT | #endif #ifdef O_NOATIME O_NOATIME | #endif O_APPEND | O_NONBLOCK; flags &= mask; file->flags &= ~mask; file->flags |= flags; break; } case F_GETLK: break; case F_SETLK: case F_SETLKW: break; #ifdef RT_USING_MUSLLIBC case F_DUPFD_CLOEXEC: ret = -EINVAL; break; #endif default: ret = -EPERM; break; } } else { ret = -EBADF; } return ret; } int dfs_file_fsync(struct dfs_file *file) { int ret = -EBADF; if (file) { if (file->fops->flush) { if (dfs_is_mounted(file->vnode->mnt) == 0) { #ifdef RT_USING_PAGECACHE if (file->vnode->aspace) { dfs_aspace_flush(file->vnode->aspace); } #endif ret = file->fops->flush(file); } else { ret = -EINVAL; } } } return ret; } int dfs_file_unlink(const char *path) { int ret = -RT_ERROR; char *fullpath = RT_NULL; struct dfs_mnt *mnt = RT_NULL; struct dfs_dentry *dentry = RT_NULL; fullpath = dfs_normalize_path(NULL, path); if (fullpath) { DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath); mnt = dfs_mnt_lookup(fullpath); if (mnt) { char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST); if (tmp) { rt_free(fullpath); fullpath = tmp; } if (strcmp(mnt->fullpath, fullpath) != 0) { DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_lookup(mnt, %s)", fullpath); dentry = dfs_dentry_lookup(mnt, fullpath, 0); if (dentry) { rt_bool_t has_child = RT_FALSE; has_child = dfs_mnt_has_child_mnt(mnt, fullpath); #ifdef RT_USING_PAGECACHE if (dentry->vnode->aspace) { dfs_aspace_clean(dentry->vnode->aspace); } #endif dfs_file_lock(); if (has_child == RT_FALSE) { /* no child mnt point, unlink it */ ret = -RT_ERROR; if (mnt->fs_ops->unlink) { if (dfs_is_mounted(mnt) == 0) { ret = mnt->fs_ops->unlink(dentry); } } } else { ret = -EBUSY; } dfs_file_unlock(); /* release this dentry */ dfs_dentry_unref(dentry); } else { /* no this entry */ ret = -ENOENT; } } else { /* it's a mount point, failed for busy */ ret = -EBUSY; } } else { ret = -ENOENT; } /* release fullpath */ rt_free(fullpath); } else { ret = -ENOMEM; } return ret; } int dfs_file_link(const char *oldname, const char *newname) { int ret = -1; struct stat stat; struct dfs_mnt *mnt = RT_NULL; char *old_fullpath, *new_fullpath; if (dfs_file_isdir(oldname) == 0) { return ret; } if (dfs_file_lstat(newname, &stat) >= 0) { return ret; } old_fullpath = dfs_normalize_path(NULL, oldname); if (old_fullpath) { DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", old_fullpath); mnt = dfs_mnt_lookup(old_fullpath); if (mnt == RT_NULL) { rt_free(old_fullpath); return -1; } char *tmp = dfs_file_realpath(&mnt, old_fullpath, DFS_REALPATH_EXCEPT_LAST); if (tmp) { rt_free(old_fullpath); old_fullpath = tmp; } } new_fullpath = dfs_normalize_path(NULL, newname); if (new_fullpath) { char *tmp = dfs_file_realpath(&mnt, new_fullpath, DFS_REALPATH_EXCEPT_LAST); if (tmp) { rt_free(new_fullpath); new_fullpath = tmp; } } if (old_fullpath && new_fullpath) { struct dfs_dentry *old_dentry, *new_dentry; DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_lookup(mnt, %s)", old_fullpath); old_dentry = dfs_dentry_lookup(mnt, old_fullpath, 0); DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_create(%s)", new_fullpath); new_dentry = dfs_dentry_create(mnt, new_fullpath); if (old_dentry && new_dentry) { if (mnt->fs_ops->link) { if (dfs_is_mounted(mnt) == 0) { ret = mnt->fs_ops->link(old_dentry, new_dentry); } } } dfs_dentry_unref(old_dentry); dfs_dentry_unref(new_dentry); } if (old_fullpath) { rt_free(old_fullpath); } if (new_fullpath) { rt_free(new_fullpath); } return ret; } /* symlink creates a symbolic link named `linkpath` which contains the string `target`. */ int dfs_file_symlink(const char *target, const char *linkpath) { int ret = -RT_ERROR; char *fullpath = RT_NULL, *parent = RT_NULL; struct dfs_mnt *mnt = RT_NULL; struct dfs_dentry *dentry = RT_NULL; if (target && linkpath) { if (linkpath[0] != '/') { fullpath = dfs_normalize_path(NULL, linkpath); } else { fullpath = (char*)linkpath; } /* linkpath should be not exist */ if (dfs_file_access(fullpath, O_RDONLY) != 0) { char *index; /* get parent path */ index = strrchr(fullpath, '/'); if (index) { int length = index - fullpath; if (length > 0) { parent = (char*) rt_malloc (length + 1); if (parent) { memcpy(parent, fullpath, length); parent[length] = '\0'; } } else { parent = (char*) rt_malloc (1 + 1); if (parent) { parent[0] = '/'; parent[1] = '\0'; } } } if (parent) { DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath); mnt = dfs_mnt_lookup(parent); if (mnt) { char *tmp = dfs_file_realpath(&mnt, parent, DFS_REALPATH_EXCEPT_LAST); if (tmp) { rt_free(parent); parent = tmp; } DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_lookup(mnt, %s)", fullpath); dentry = dfs_dentry_lookup(mnt, parent, DFS_REALPATH_EXCEPT_LAST); if (dentry) { if (dentry->mnt->fs_ops->symlink) { char *path = dfs_normalize_path(parent, target); if (path) { ret = rt_strncmp(parent, path, strlen(parent)); if (ret == 0) { tmp = path + strlen(parent); if (*tmp == '/') { tmp ++; } } else { tmp = path; } if (dfs_is_mounted(mnt) == 0) { ret = mnt->fs_ops->symlink(dentry, tmp, index + 1); } rt_free(path); } } else { ret = -ENOSYS; } dfs_dentry_unref(dentry); } else { ret = -ENOENT; } } else { ret = -ENOENT; } rt_free(parent); } } if (fullpath != linkpath) rt_free(fullpath); } else { ret = -EINVAL; } return ret; } int dfs_file_readlink(const char *path, char *buf, int bufsize) { int ret = -RT_ERROR; char *fullpath = RT_NULL; struct dfs_mnt *mnt = RT_NULL; struct dfs_dentry *dentry = RT_NULL; fullpath = dfs_normalize_path(NULL, path); if (fullpath) { DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath); mnt = dfs_mnt_lookup(fullpath); if (mnt) { char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST); if (tmp) { rt_free(fullpath); fullpath = tmp; } DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_lookup(mnt, %s)", fullpath); dentry = dfs_dentry_lookup(mnt, fullpath, 0); if (dentry) { if (mnt->fs_ops->readlink) { if (dfs_is_mounted(mnt) == 0) { ret = mnt->fs_ops->readlink(dentry, buf, bufsize); } } else { ret = -ENOSYS; } /* release this dentry */ dfs_dentry_unref(dentry); } else { /* no this entry */ ret = -ENOENT; } } else { ret = -ENOENT; } /* release fullpath */ rt_free(fullpath); } else { ret = -ENOMEM; } return ret; } int dfs_file_rename(const char *old_file, const char *new_file) { int ret = -1; struct dfs_mnt *mnt = RT_NULL; char *old_fullpath, *new_fullpath; old_fullpath = dfs_normalize_path(NULL, old_file); if (old_fullpath) { DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", old_fullpath); mnt = dfs_mnt_lookup(old_fullpath); if (mnt == RT_NULL) { rt_free(old_fullpath); return -1; } char *tmp = dfs_file_realpath(&mnt, old_fullpath, DFS_REALPATH_EXCEPT_LAST); if (tmp) { rt_free(old_fullpath); old_fullpath = tmp; } } new_fullpath = dfs_normalize_path(NULL, new_file); if (new_fullpath) { char *tmp = dfs_file_realpath(&mnt, new_fullpath, DFS_REALPATH_EXCEPT_LAST); if (tmp) { rt_free(new_fullpath); new_fullpath = tmp; } } if (old_fullpath && new_fullpath) { struct dfs_dentry *old_dentry, *new_dentry; DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_lookup(mnt, %s)", old_fullpath); old_dentry = dfs_dentry_lookup(mnt, old_fullpath, 0); DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_create(%s)", new_fullpath); new_dentry = dfs_dentry_create(mnt, new_fullpath); if (old_dentry && new_dentry) { if (mnt->fs_ops->rename) { if (dfs_is_mounted(mnt) == 0) { #ifdef RT_USING_PAGECACHE if (old_dentry->vnode->aspace) { dfs_aspace_clean(old_dentry->vnode->aspace); } #endif ret = mnt->fs_ops->rename(old_dentry, new_dentry); } } } dfs_dentry_unref(old_dentry); dfs_dentry_unref(new_dentry); } if (old_fullpath) { rt_free(old_fullpath); } if (new_fullpath) { rt_free(new_fullpath); } return ret; } int dfs_file_ftruncate(struct dfs_file *file, off_t length) { int ret = 0; if (file) { if (file->fops->truncate) { if (dfs_is_mounted(file->vnode->mnt) == 0) { #ifdef RT_USING_PAGECACHE if (file->vnode->aspace) { dfs_aspace_clean(file->vnode->aspace); } #endif ret = file->fops->truncate(file, length); } else { ret = -EINVAL; } } else { ret = -ENOSYS; } } else { ret = -EBADF; } return ret; } int dfs_file_flush(struct dfs_file *file) { int ret = 0; if (file) { if (file->fops->flush) { if (dfs_is_mounted(file->vnode->mnt) == 0) { #ifdef RT_USING_PAGECACHE if (file->vnode->aspace) { dfs_aspace_flush(file->vnode->aspace); } #endif ret = file->fops->flush(file); } else { ret = -EINVAL; } } else { ret = -ENOSYS; } } else { ret = -EBADF; } return ret; } int dfs_file_getdents(struct dfs_file *file, struct dirent *dirp, size_t nbytes) { int ret = -RT_ERROR; if (file) { if (file->vnode && S_ISDIR(file->vnode->mode)) { if (file->fops && file->fops->getdents) { DLOG(msg, "dfs_file", file->dentry->mnt->fs_ops->name, DLOG_MSG, "fops->getdents()"); if (dfs_is_mounted(file->vnode->mnt) == 0) { ret = file->fops->getdents(file, dirp, nbytes); } else { ret = -EINVAL; } } } } else { ret = -EBADF; } return ret; } /** * this function will check the path is it a directory. * * @param path the file path. * * @return 0 on is dir, -1 on not dir. */ int dfs_file_isdir(const char *path) { int ret = -RT_ERROR; char *fullpath = RT_NULL; struct dfs_mnt *mnt = RT_NULL; struct dfs_dentry *dentry = RT_NULL; fullpath = dfs_normalize_path(NULL, path); if (fullpath) { DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath); mnt = dfs_mnt_lookup(fullpath); if (mnt) { char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_NONE); if (tmp) { rt_free(fullpath); fullpath = tmp; } DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dentry = dfs_dentry_lookup(mnt, %s)", fullpath); dentry = dfs_dentry_lookup(mnt, fullpath, 0); if (dentry) { DLOG(msg, "dentry", "dfs_file", DLOG_MSG_RET, "return dentry"); if (mnt->fs_ops->stat) { struct stat stat = {0}; DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "fs_ops->stat(dentry, buf)"); if (dfs_is_mounted(mnt) == 0) { ret = mnt->fs_ops->stat(dentry, &stat); } if (ret == RT_EOK && S_ISDIR(stat.st_mode)) { ret = RT_EOK; } else { ret = -RT_ERROR; } } /* unref dentry */ DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_unref(dentry)"); dfs_dentry_unref(dentry); dentry = RT_NULL; } } rt_free(fullpath); fullpath = RT_NULL; } return ret; } int dfs_file_access(const char *path, mode_t mode) { int ret; struct dfs_file file; dfs_file_init(&file); if (dfs_file_open(&file, path, O_RDONLY, mode) >= 0) { ret = 0; dfs_file_close(&file); } else { ret = -1; } dfs_file_deinit(&file); return ret; } #ifdef RT_USING_SMART int dfs_file_mmap2(struct dfs_file *file, struct dfs_mmap2_args *mmap2) { int ret = RT_EOK; if (file && mmap2) { if (file->vnode->type == FT_REGULAR) { ret = dfs_file_mmap(file, mmap2); if (ret != 0) { ret = ret > 0 ? ret : -ret; rt_set_errno(ret); } } else if (file->vnode->type != FT_DEVICE || !file->vnode->fops->ioctl) { rt_set_errno(EINVAL); } else if (file->vnode->type == FT_DEVICE && file->vnode->fops->ioctl) { if (dfs_is_mounted(file->vnode->mnt) == 0) { ret = file->vnode->fops->ioctl(file, RT_FIOMMAP2, mmap2); } else { ret = EINVAL; } if (ret != 0) { ret = ret > 0 ? ret : -ret; rt_set_errno(ret); } } } return ret; } #endif #ifdef RT_USING_FINSH #define _COLOR_RED "\033[31m" #define _COLOR_GREEN "\033[32m" #define _COLOR_YELLOW "\033[33m" #define _COLOR_BLUE "\033[34m" #define _COLOR_CYAN "\033[36m" #define _COLOR_WHITE "\033[37m" #define _COLOR_NORMAL "\033[0m" void ls(const char *pathname) { struct dirent dirent; struct stat stat; int length; char *fullpath, *path; struct dfs_file file; if (pathname == NULL) { #ifdef DFS_USING_WORKDIR /* open current working directory */ path = rt_strdup(working_directory); #else path = rt_strdup("/"); #endif if (path == NULL) { return; /* out of memory */ } } else { path = dfs_normalize_path(NULL, (char *)pathname); if (path == NULL) { return; /* out of memory */ } } dfs_file_init(&file); /* list directory */ DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_open(%s, O_DIRECTORY, 0)", path); if (dfs_file_open(&file, path, O_DIRECTORY, 0) >= 0) { char *link_fn = (char *)rt_malloc(DFS_PATH_MAX); if (link_fn) { rt_kprintf("Directory %s:\n", path); do { memset(&dirent, 0, sizeof(struct dirent)); DLOG(group, "foreach_item"); DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_getdents(&dirent)"); length = dfs_file_getdents(&file, &dirent, sizeof(struct dirent)); if (length > 0) { DLOG(msg, "dfs_file", "dfs", DLOG_MSG_RET, "dirent.d_name=%s", dirent.d_name); memset(&stat, 0, sizeof(struct stat)); /* build full path for each file */ fullpath = dfs_normalize_path(path, dirent.d_name); if (fullpath == NULL) break; DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_lstat(%s, &stat)", fullpath); if (dfs_file_lstat(fullpath, &stat) == 0) { if (S_ISDIR(stat.st_mode)) { rt_kprintf(_COLOR_BLUE "%-20s" _COLOR_NORMAL, dirent.d_name); rt_kprintf("%-25s\n", ""); } else if (S_ISLNK(stat.st_mode)) { int ret = 0; rt_kprintf(_COLOR_CYAN "%-20s" _COLOR_NORMAL, dirent.d_name); ret = dfs_file_readlink(fullpath, link_fn, DFS_PATH_MAX); if (ret > 0) { char *link_path = link_fn; struct dfs_mnt *mnt = RT_NULL; mnt = dfs_mnt_lookup(fullpath); if (mnt) { char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST); if (tmp) { char *index; index = strrchr(fullpath, '/'); if (index) { int length = index - fullpath; char *parent = (char*) rt_malloc (length + 1); if (parent) { rt_memcpy(parent, fullpath, length); parent[length] = '\0'; ret = rt_strncmp(parent, link_fn, length); if (ret == 0) { link_path = link_fn + length; if (*link_path == '/') { link_path ++; } } rt_free(parent); } } rt_free(tmp); } } rt_kprintf("-> %s\n", link_path); } else { rt_kprintf(_COLOR_RED "-> link_error\n" _COLOR_NORMAL); } } else if (stat.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { rt_kprintf(_COLOR_GREEN "%-20s" _COLOR_NORMAL, dirent.d_name); rt_kprintf("%-25lu\n", (unsigned long)stat.st_size); } else if (S_ISCHR(stat.st_mode)) { rt_kprintf(_COLOR_YELLOW "%-20s" _COLOR_NORMAL, dirent.d_name); rt_kprintf("%-25s\n", ""); } else { rt_kprintf("%-20s", dirent.d_name); rt_kprintf("%-25lu\n", (unsigned long)stat.st_size); } } else { rt_kprintf(_COLOR_RED "%-20s\n" _COLOR_NORMAL, dirent.d_name); } rt_free(fullpath); } else { DLOG(msg, "dfs_file", "dfs", DLOG_MSG_RET, "return NULL"); } DLOG(group_end); } while (length > 0); rt_free(link_fn); } DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_close()"); dfs_file_close(&file); } else { rt_kprintf("No such directory\n"); } dfs_file_deinit(&file); DLOG(msg, "dfs_file", "dfs", DLOG_MSG_RET, "return"); rt_free(path); } void cat(const char *filename) { int length = 0; char buffer[81]; struct dfs_file file; if (filename && dfs_file_isdir(filename) == 0) { rt_kprintf("cat: %s Is a directory\n", filename); return; } dfs_file_init(&file); DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_open(%s, O_RDONLY, 0)", filename); if (dfs_file_open(&file, filename, O_RDONLY, 0) < 0) { rt_kprintf("Open %s failed\n", filename); dfs_file_deinit(&file); return; } do { rt_memset(buffer, 0x0, sizeof(buffer)); DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_read(fd, buffer, %d)", sizeof(buffer) - 1); length = dfs_file_read(&file, (void *)buffer, sizeof(buffer) - 1); if (length > 0) { buffer[length] = '\0'; rt_kprintf("%s", buffer); } } while (length > 0); rt_kprintf("\n"); DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_close()"); dfs_file_close(&file); dfs_file_deinit(&file); } #define BUF_SZ 4096 static void copyfile(const char *src, const char *dst) { int ret; struct dfs_file src_file, dst_file; rt_uint8_t *block_ptr; rt_int32_t read_bytes; block_ptr = (rt_uint8_t *)rt_malloc(BUF_SZ); if (block_ptr == NULL) { rt_kprintf("out of memory\n"); return; } dfs_file_init(&src_file); ret = dfs_file_open(&src_file, src, O_RDONLY, 0); if (ret < 0) { dfs_file_deinit(&src_file); rt_free(block_ptr); rt_kprintf("Read %s failed\n", src); return; } dfs_file_init(&dst_file); ret = dfs_file_open(&dst_file, dst, O_WRONLY | O_CREAT | O_TRUNC, 0); if (ret < 0) { dfs_file_deinit(&dst_file); dfs_file_close(&src_file); dfs_file_deinit(&src_file); rt_free(block_ptr); rt_kprintf("Write %s failed\n", dst); return; } do { read_bytes = dfs_file_read(&src_file, block_ptr, BUF_SZ); if (read_bytes > 0) { int length; length = dfs_file_write(&dst_file, block_ptr, read_bytes); if (length != read_bytes) { /* write failed. */ rt_kprintf("Write file data failed, errno=%d\n", length); break; } } } while (read_bytes > 0); dfs_file_close(&dst_file); dfs_file_deinit(&dst_file); dfs_file_close(&src_file); dfs_file_deinit(&src_file); rt_free(block_ptr); } extern int mkdir(const char *path, mode_t mode); static void copydir(const char *src, const char *dst) { struct dirent dirent; struct stat stat; int length; struct dfs_file file; dfs_file_init(&file); if (dfs_file_open(&file, src, O_DIRECTORY, 0) < 0) { rt_kprintf("open %s failed\n", src); dfs_file_deinit(&file); return ; } do { rt_memset(&dirent, 0, sizeof(struct dirent)); length = dfs_file_getdents(&file, &dirent, sizeof(struct dirent)); if (length > 0) { char *src_entry_full = NULL; char *dst_entry_full = NULL; if (strcmp(dirent.d_name, "..") == 0 || strcmp(dirent.d_name, ".") == 0) continue; /* build full path for each file */ if ((src_entry_full = dfs_normalize_path(src, dirent.d_name)) == NULL) { rt_kprintf("out of memory!\n"); break; } if ((dst_entry_full = dfs_normalize_path(dst, dirent.d_name)) == NULL) { rt_kprintf("out of memory!\n"); rt_free(src_entry_full); break; } rt_memset(&stat, 0, sizeof(struct stat)); if (dfs_file_lstat(src_entry_full, &stat) != 0) { rt_kprintf("open file: %s failed\n", dirent.d_name); continue; } if (S_ISDIR(stat.st_mode)) { mkdir(dst_entry_full, 0); copydir(src_entry_full, dst_entry_full); } else { copyfile(src_entry_full, dst_entry_full); } rt_free(src_entry_full); rt_free(dst_entry_full); } } while (length > 0); dfs_file_close(&file); dfs_file_deinit(&file); } static const char *_get_path_lastname(const char *path) { char *ptr; if ((ptr = (char *)strrchr(path, '/')) == NULL) return path; /* skip the '/' then return */ return ++ptr; } void copy(const char *src, const char *dst) { #define FLAG_SRC_TYPE 0x03 #define FLAG_SRC_IS_DIR 0x01 #define FLAG_SRC_IS_FILE 0x02 #define FLAG_SRC_NON_EXSIT 0x00 #define FLAG_DST_TYPE 0x0C #define FLAG_DST_IS_DIR 0x04 #define FLAG_DST_IS_FILE 0x08 #define FLAG_DST_NON_EXSIT 0x00 struct stat stat; uint32_t flag = 0; /* check the staus of src and dst */ if (dfs_file_lstat(src, &stat) < 0) { rt_kprintf("copy failed, bad %s\n", src); return; } if (S_ISDIR(stat.st_mode)) flag |= FLAG_SRC_IS_DIR; else flag |= FLAG_SRC_IS_FILE; if (dfs_file_stat(dst, &stat) < 0) { flag |= FLAG_DST_NON_EXSIT; } else { if (S_ISDIR(stat.st_mode)) flag |= FLAG_DST_IS_DIR; else flag |= FLAG_DST_IS_FILE; } //2. check status if ((flag & FLAG_SRC_IS_DIR) && (flag & FLAG_DST_IS_FILE)) { rt_kprintf("cp faild, cp dir to file is not permitted!\n"); return ; } //3. do copy if (flag & FLAG_SRC_IS_FILE) { if (flag & FLAG_DST_IS_DIR) { char *fdst; fdst = dfs_normalize_path(dst, _get_path_lastname(src)); if (fdst == NULL) { rt_kprintf("out of memory\n"); return; } copyfile(src, fdst); rt_free(fdst); } else { copyfile(src, dst); } } else //flag & FLAG_SRC_IS_DIR { if (flag & FLAG_DST_IS_DIR) { char *fdst; fdst = dfs_normalize_path(dst, _get_path_lastname(src)); if (fdst == NULL) { rt_kprintf("out of memory\n"); return; } mkdir(fdst, 0); copydir(src, fdst); rt_free(fdst); } else if ((flag & FLAG_DST_TYPE) == FLAG_DST_NON_EXSIT) { mkdir(dst, 0); copydir(src, dst); } else { copydir(src, dst); } } } FINSH_FUNCTION_EXPORT(copy, copy file or dir) #endif