@@ -0,0 +1,572 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2005-02-22 Bernard The first version.
|
||||
* 2017-12-11 Bernard Use rt_free to instead of free in fd_is_open().
|
||||
* 2018-03-20 Heyuanjie dynamic allocation FD
|
||||
*/
|
||||
|
||||
#include <dfs.h>
|
||||
#include <dfs_fs.h>
|
||||
#include <dfs_file.h>
|
||||
#include "dfs_private.h"
|
||||
#ifdef RT_USING_LWP
|
||||
#include <lwp.h>
|
||||
#endif
|
||||
|
||||
#ifdef RT_USING_POSIX_STDIO
|
||||
#include <libc.h>
|
||||
#endif /* RT_USING_POSIX_STDIO */
|
||||
|
||||
/* Global variables */
|
||||
const struct dfs_filesystem_ops *filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX];
|
||||
struct dfs_filesystem filesystem_table[DFS_FILESYSTEMS_MAX];
|
||||
|
||||
/* device filesystem lock */
|
||||
static struct rt_mutex fslock;
|
||||
|
||||
#ifdef DFS_USING_WORKDIR
|
||||
char working_directory[DFS_PATH_MAX] = {"/"};
|
||||
#endif
|
||||
|
||||
static struct dfs_fdtable _fdtab;
|
||||
|
||||
/**
|
||||
* @addtogroup DFS
|
||||
*/
|
||||
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* this function will initialize device file system.
|
||||
*/
|
||||
int dfs_init(void)
|
||||
{
|
||||
static rt_bool_t init_ok = RT_FALSE;
|
||||
|
||||
if (init_ok)
|
||||
{
|
||||
rt_kprintf("dfs already init.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* clear filesystem operations table */
|
||||
rt_memset((void *)filesystem_operation_table, 0, sizeof(filesystem_operation_table));
|
||||
/* clear filesystem table */
|
||||
rt_memset(filesystem_table, 0, sizeof(filesystem_table));
|
||||
/* clean fd table */
|
||||
rt_memset(&_fdtab, 0, sizeof(_fdtab));
|
||||
|
||||
/* create device filesystem lock */
|
||||
rt_mutex_init(&fslock, "fslock", RT_IPC_FLAG_PRIO);
|
||||
|
||||
#ifdef DFS_USING_WORKDIR
|
||||
/* set current working directory */
|
||||
rt_memset(working_directory, 0, sizeof(working_directory));
|
||||
working_directory[0] = '/';
|
||||
#endif
|
||||
|
||||
#ifdef RT_USING_DFS_DEVFS
|
||||
{
|
||||
extern int devfs_init(void);
|
||||
|
||||
/* if enable devfs, initialize and mount it as soon as possible */
|
||||
devfs_init();
|
||||
|
||||
dfs_mount(NULL, "/dev", "devfs", 0, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
init_ok = RT_TRUE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_PREV_EXPORT(dfs_init);
|
||||
|
||||
/**
|
||||
* this function will lock device file system.
|
||||
*
|
||||
* @note please don't invoke it on ISR.
|
||||
*/
|
||||
void dfs_lock(void)
|
||||
{
|
||||
rt_err_t result = -RT_EBUSY;
|
||||
|
||||
while (result == -RT_EBUSY)
|
||||
{
|
||||
result = rt_mutex_take(&fslock, RT_WAITING_FOREVER);
|
||||
}
|
||||
|
||||
if (result != RT_EOK)
|
||||
{
|
||||
RT_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will lock device file system.
|
||||
*
|
||||
* @note please don't invoke it on ISR.
|
||||
*/
|
||||
void dfs_unlock(void)
|
||||
{
|
||||
rt_mutex_release(&fslock);
|
||||
}
|
||||
|
||||
#ifdef DFS_USING_POSIX
|
||||
static int fd_alloc(struct dfs_fdtable *fdt, int startfd)
|
||||
{
|
||||
int idx;
|
||||
|
||||
/* find an empty fd entry */
|
||||
for (idx = startfd; idx < (int)fdt->maxfd; idx++)
|
||||
{
|
||||
if (fdt->fds[idx] == RT_NULL)
|
||||
break;
|
||||
if (fdt->fds[idx]->ref_count == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* allocate a larger FD container */
|
||||
if (idx == (int)fdt->maxfd && fdt->maxfd < DFS_FD_MAX)
|
||||
{
|
||||
int cnt, index;
|
||||
struct dfs_fd **fds;
|
||||
|
||||
/* increase the number of FD with 4 step length */
|
||||
cnt = fdt->maxfd + 4;
|
||||
cnt = cnt > DFS_FD_MAX ? DFS_FD_MAX : cnt;
|
||||
|
||||
fds = (struct dfs_fd **)rt_realloc(fdt->fds, cnt * sizeof(struct dfs_fd *));
|
||||
if (fds == NULL) goto __exit; /* return fdt->maxfd */
|
||||
|
||||
/* clean the new allocated fds */
|
||||
for (index = (int)fdt->maxfd; index < cnt; index ++)
|
||||
{
|
||||
fds[index] = NULL;
|
||||
}
|
||||
|
||||
fdt->fds = fds;
|
||||
fdt->maxfd = cnt;
|
||||
}
|
||||
|
||||
/* allocate 'struct dfs_fd' */
|
||||
if (idx < (int)fdt->maxfd && fdt->fds[idx] == RT_NULL)
|
||||
{
|
||||
fdt->fds[idx] = (struct dfs_fd *)rt_calloc(1, sizeof(struct dfs_fd));
|
||||
if (fdt->fds[idx] == RT_NULL)
|
||||
idx = fdt->maxfd;
|
||||
}
|
||||
|
||||
__exit:
|
||||
return idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup Fd
|
||||
* This function will allocate a file descriptor.
|
||||
*
|
||||
* @return -1 on failed or the allocated file descriptor.
|
||||
*/
|
||||
int fd_new(void)
|
||||
{
|
||||
struct dfs_fd *d;
|
||||
int idx;
|
||||
struct dfs_fdtable *fdt;
|
||||
|
||||
fdt = dfs_fdtable_get();
|
||||
/* lock filesystem */
|
||||
dfs_lock();
|
||||
|
||||
/* find an empty fd entry */
|
||||
idx = fd_alloc(fdt, 0);
|
||||
|
||||
/* can't find an empty fd entry */
|
||||
if (idx == (int)fdt->maxfd)
|
||||
{
|
||||
idx = -(1 + DFS_FD_OFFSET);
|
||||
LOG_E("DFS fd new is failed! Could not found an empty fd entry.");
|
||||
goto __result;
|
||||
}
|
||||
|
||||
d = fdt->fds[idx];
|
||||
d->ref_count = 1;
|
||||
d->magic = DFS_FD_MAGIC;
|
||||
|
||||
__result:
|
||||
dfs_unlock();
|
||||
return idx + DFS_FD_OFFSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup Fd
|
||||
*
|
||||
* This function will return a file descriptor structure according to file
|
||||
* descriptor.
|
||||
*
|
||||
* @return NULL on on this file descriptor or the file descriptor structure
|
||||
* pointer.
|
||||
*/
|
||||
struct dfs_fd *fd_get(int fd)
|
||||
{
|
||||
struct dfs_fd *d;
|
||||
struct dfs_fdtable *fdt;
|
||||
|
||||
#ifdef RT_USING_POSIX_STDIO
|
||||
if ((0 <= fd) && (fd <= 2))
|
||||
fd = libc_stdio_get_console();
|
||||
#endif /* RT_USING_POSIX_STDIO */
|
||||
|
||||
fdt = dfs_fdtable_get();
|
||||
fd = fd - DFS_FD_OFFSET;
|
||||
if (fd < 0 || fd >= (int)fdt->maxfd)
|
||||
return NULL;
|
||||
|
||||
dfs_lock();
|
||||
d = fdt->fds[fd];
|
||||
|
||||
/* check dfs_fd valid or not */
|
||||
if ((d == NULL) || (d->magic != DFS_FD_MAGIC))
|
||||
{
|
||||
dfs_unlock();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* increase the reference count */
|
||||
d->ref_count ++;
|
||||
dfs_unlock();
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup Fd
|
||||
*
|
||||
* This function will put the file descriptor.
|
||||
*/
|
||||
void fd_put(struct dfs_fd *fd)
|
||||
{
|
||||
RT_ASSERT(fd != NULL);
|
||||
|
||||
dfs_lock();
|
||||
|
||||
fd->ref_count --;
|
||||
|
||||
/* clear this fd entry */
|
||||
if (fd->ref_count == 0)
|
||||
{
|
||||
int index;
|
||||
struct dfs_fdtable *fdt;
|
||||
|
||||
fdt = dfs_fdtable_get();
|
||||
for (index = 0; index < (int)fdt->maxfd; index ++)
|
||||
{
|
||||
if (fdt->fds[index] == fd)
|
||||
{
|
||||
rt_free(fd);
|
||||
fdt->fds[index] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
dfs_unlock();
|
||||
}
|
||||
|
||||
#endif /* DFS_USING_POSIX */
|
||||
|
||||
/**
|
||||
* @ingroup Fd
|
||||
*
|
||||
* This function will return whether this file has been opend.
|
||||
*
|
||||
* @param pathname the file path name.
|
||||
*
|
||||
* @return 0 on file has been open successfully, -1 on open failed.
|
||||
*/
|
||||
int fd_is_open(const char *pathname)
|
||||
{
|
||||
char *fullpath;
|
||||
unsigned int index;
|
||||
struct dfs_filesystem *fs;
|
||||
struct dfs_fd *fd;
|
||||
struct dfs_fdtable *fdt;
|
||||
|
||||
fdt = dfs_fdtable_get();
|
||||
fullpath = dfs_normalize_path(NULL, pathname);
|
||||
if (fullpath != NULL)
|
||||
{
|
||||
char *mountpath;
|
||||
fs = dfs_filesystem_lookup(fullpath);
|
||||
if (fs == NULL)
|
||||
{
|
||||
/* can't find mounted file system */
|
||||
rt_free(fullpath);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get file path name under mounted file system */
|
||||
if (fs->path[0] == '/' && fs->path[1] == '\0')
|
||||
mountpath = fullpath;
|
||||
else
|
||||
mountpath = fullpath + strlen(fs->path);
|
||||
|
||||
dfs_lock();
|
||||
|
||||
for (index = 0; index < fdt->maxfd; index++)
|
||||
{
|
||||
fd = fdt->fds[index];
|
||||
if (fd == NULL || fd->fops == NULL || fd->path == NULL) continue;
|
||||
|
||||
if (fd->fs == fs && strcmp(fd->path, mountpath) == 0)
|
||||
{
|
||||
/* found file in file descriptor table */
|
||||
rt_free(fullpath);
|
||||
dfs_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
dfs_unlock();
|
||||
|
||||
rt_free(fullpath);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will return a sub-path name under directory.
|
||||
*
|
||||
* @param directory the parent directory.
|
||||
* @param filename the filename.
|
||||
*
|
||||
* @return the subdir pointer in filename
|
||||
*/
|
||||
const char *dfs_subdir(const char *directory, const char *filename)
|
||||
{
|
||||
const char *dir;
|
||||
|
||||
if (strlen(directory) == strlen(filename)) /* it's a same path */
|
||||
return NULL;
|
||||
|
||||
dir = filename + strlen(directory);
|
||||
if ((*dir != '/') && (dir != filename))
|
||||
{
|
||||
dir --;
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
RTM_EXPORT(dfs_subdir);
|
||||
|
||||
/**
|
||||
* this function will normalize a path according to specified parent directory
|
||||
* and file name.
|
||||
*
|
||||
* @param directory the parent path
|
||||
* @param filename the file name
|
||||
*
|
||||
* @return the built full file path (absolute path)
|
||||
*/
|
||||
char *dfs_normalize_path(const char *directory, const char *filename)
|
||||
{
|
||||
char *fullpath;
|
||||
char *dst0, *dst, *src;
|
||||
|
||||
/* check parameters */
|
||||
RT_ASSERT(filename != NULL);
|
||||
|
||||
#ifdef DFS_USING_WORKDIR
|
||||
if (directory == NULL) /* shall use working directory */
|
||||
directory = &working_directory[0];
|
||||
#else
|
||||
if ((directory == NULL) && (filename[0] != '/'))
|
||||
{
|
||||
rt_kprintf(NO_WORKING_DIR);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (filename[0] != '/') /* it's a absolute path, use it directly */
|
||||
{
|
||||
fullpath = (char *)rt_malloc(strlen(directory) + strlen(filename) + 2);
|
||||
|
||||
if (fullpath == NULL)
|
||||
return NULL;
|
||||
|
||||
/* join path and file name */
|
||||
rt_snprintf(fullpath, strlen(directory) + strlen(filename) + 2,
|
||||
"%s/%s", directory, filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
fullpath = rt_strdup(filename); /* copy string */
|
||||
|
||||
if (fullpath == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
src = fullpath;
|
||||
dst = fullpath;
|
||||
|
||||
dst0 = dst;
|
||||
while (1)
|
||||
{
|
||||
char c = *src;
|
||||
|
||||
if (c == '.')
|
||||
{
|
||||
if (!src[1]) src ++; /* '.' and ends */
|
||||
else if (src[1] == '/')
|
||||
{
|
||||
/* './' case */
|
||||
src += 2;
|
||||
|
||||
while ((*src == '/') && (*src != '\0'))
|
||||
src ++;
|
||||
continue;
|
||||
}
|
||||
else if (src[1] == '.')
|
||||
{
|
||||
if (!src[2])
|
||||
{
|
||||
/* '..' and ends case */
|
||||
src += 2;
|
||||
goto up_one;
|
||||
}
|
||||
else if (src[2] == '/')
|
||||
{
|
||||
/* '../' case */
|
||||
src += 3;
|
||||
|
||||
while ((*src == '/') && (*src != '\0'))
|
||||
src ++;
|
||||
goto up_one;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* copy up the next '/' and erase all '/' */
|
||||
while ((c = *src++) != '\0' && c != '/')
|
||||
*dst ++ = c;
|
||||
|
||||
if (c == '/')
|
||||
{
|
||||
*dst ++ = '/';
|
||||
while (c == '/')
|
||||
c = *src++;
|
||||
|
||||
src --;
|
||||
}
|
||||
else if (!c)
|
||||
break;
|
||||
|
||||
continue;
|
||||
|
||||
up_one:
|
||||
dst --;
|
||||
if (dst < dst0)
|
||||
{
|
||||
rt_free(fullpath);
|
||||
return NULL;
|
||||
}
|
||||
while (dst0 < dst && dst[-1] != '/')
|
||||
dst --;
|
||||
}
|
||||
|
||||
*dst = '\0';
|
||||
|
||||
/* remove '/' in the end of path if exist */
|
||||
dst --;
|
||||
if ((dst != fullpath) && (*dst == '/'))
|
||||
*dst = '\0';
|
||||
|
||||
/* final check fullpath is not empty, for the special path of lwext "/.." */
|
||||
if ('\0' == fullpath[0])
|
||||
{
|
||||
fullpath[0] = '/';
|
||||
fullpath[1] = '\0';
|
||||
}
|
||||
|
||||
return fullpath;
|
||||
}
|
||||
RTM_EXPORT(dfs_normalize_path);
|
||||
|
||||
/**
|
||||
* This function will get the file descriptor table of current process.
|
||||
*/
|
||||
struct dfs_fdtable *dfs_fdtable_get(void)
|
||||
{
|
||||
struct dfs_fdtable *fdt;
|
||||
#ifdef RT_USING_LWP
|
||||
struct rt_lwp *lwp;
|
||||
|
||||
lwp = (struct rt_lwp *)rt_thread_self()->lwp;
|
||||
if (lwp)
|
||||
fdt = &lwp->fdt;
|
||||
else
|
||||
fdt = &_fdtab;
|
||||
#else
|
||||
fdt = &_fdtab;
|
||||
#endif
|
||||
|
||||
return fdt;
|
||||
}
|
||||
|
||||
#ifdef RT_USING_FINSH
|
||||
#include <finsh.h>
|
||||
int list_fd(void)
|
||||
{
|
||||
int index;
|
||||
struct dfs_fdtable *fd_table;
|
||||
|
||||
fd_table = dfs_fdtable_get();
|
||||
if (!fd_table) return -1;
|
||||
|
||||
rt_enter_critical();
|
||||
|
||||
rt_kprintf("fd type ref magic path\n");
|
||||
rt_kprintf("-- ------ --- ----- ------\n");
|
||||
for (index = 0; index < (int)fd_table->maxfd; index ++)
|
||||
{
|
||||
struct dfs_fd *fd = fd_table->fds[index];
|
||||
|
||||
if (fd && fd->fops)
|
||||
{
|
||||
rt_kprintf("%2d ", index + DFS_FD_OFFSET);
|
||||
if (fd->type == FT_DIRECTORY) rt_kprintf("%-7.7s ", "dir");
|
||||
else if (fd->type == FT_REGULAR) rt_kprintf("%-7.7s ", "file");
|
||||
else if (fd->type == FT_SOCKET) rt_kprintf("%-7.7s ", "socket");
|
||||
else if (fd->type == FT_USER) rt_kprintf("%-7.7s ", "user");
|
||||
else if (fd->type == FT_DEVICE) rt_kprintf("%-7.7s ", "device");
|
||||
else rt_kprintf("%-8.8s ", "unknown");
|
||||
rt_kprintf("%3d ", fd->ref_count);
|
||||
rt_kprintf("%04x ", fd->magic);
|
||||
if (fd->fs && fd->fs->path && rt_strlen(fd->fs->path) > 1)
|
||||
{
|
||||
rt_kprintf("%s", fd->fs->path);
|
||||
}
|
||||
if (fd->path)
|
||||
{
|
||||
rt_kprintf("%s\n", fd->path);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
rt_exit_critical();
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT(list_fd, list file descriptor);
|
||||
#endif
|
||||
/*@}*/
|
||||
|
||||
@@ -0,0 +1,850 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2005-02-22 Bernard The first version.
|
||||
* 2011-12-08 Bernard Merges rename patch from iamcacy.
|
||||
* 2015-05-27 Bernard Fix the fd clear issue.
|
||||
* 2019-01-24 Bernard Remove file repeatedly open check.
|
||||
*/
|
||||
|
||||
#include <dfs.h>
|
||||
#include <dfs_file.h>
|
||||
#include <dfs_private.h>
|
||||
|
||||
/**
|
||||
* @addtogroup FileApi
|
||||
*/
|
||||
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* this function will open a file which specified by path with specified flags.
|
||||
*
|
||||
* @param fd the file descriptor pointer to return the corresponding result.
|
||||
* @param path the specified file path.
|
||||
* @param flags the flags for open operator.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int dfs_file_open(struct dfs_fd *fd, const char *path, int flags)
|
||||
{
|
||||
struct dfs_filesystem *fs;
|
||||
char *fullpath;
|
||||
int result;
|
||||
|
||||
/* parameter check */
|
||||
if (fd == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* make sure we have an absolute path */
|
||||
fullpath = dfs_normalize_path(NULL, path);
|
||||
if (fullpath == NULL)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
LOG_D("open file:%s", fullpath);
|
||||
|
||||
/* find filesystem */
|
||||
fs = dfs_filesystem_lookup(fullpath);
|
||||
if (fs == NULL)
|
||||
{
|
||||
rt_free(fullpath); /* release path */
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
LOG_D("open in filesystem:%s", fs->ops->name);
|
||||
fd->fs = fs; /* set file system */
|
||||
fd->fops = fs->ops->fops; /* set file ops */
|
||||
|
||||
/* initialize the fd item */
|
||||
fd->type = FT_REGULAR;
|
||||
fd->flags = flags;
|
||||
fd->size = 0;
|
||||
fd->pos = 0;
|
||||
fd->data = fs;
|
||||
|
||||
if (!(fs->ops->flags & DFS_FS_FLAG_FULLPATH))
|
||||
{
|
||||
if (dfs_subdir(fs->path, fullpath) == NULL)
|
||||
fd->path = rt_strdup("/");
|
||||
else
|
||||
fd->path = rt_strdup(dfs_subdir(fs->path, fullpath));
|
||||
rt_free(fullpath);
|
||||
LOG_D("Actual file path: %s", fd->path);
|
||||
}
|
||||
else
|
||||
{
|
||||
fd->path = fullpath;
|
||||
}
|
||||
|
||||
/* specific file system open routine */
|
||||
if (fd->fops->open == NULL)
|
||||
{
|
||||
/* clear fd */
|
||||
rt_free(fd->path);
|
||||
fd->path = NULL;
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
if ((result = fd->fops->open(fd)) < 0)
|
||||
{
|
||||
/* clear fd */
|
||||
rt_free(fd->path);
|
||||
fd->path = NULL;
|
||||
|
||||
LOG_D("%s open failed", fullpath);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fd->flags |= DFS_F_OPEN;
|
||||
if (flags & O_DIRECTORY)
|
||||
{
|
||||
fd->type = FT_DIRECTORY;
|
||||
fd->flags |= DFS_F_DIRECTORY;
|
||||
}
|
||||
|
||||
LOG_D("open successful");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will close a file descriptor.
|
||||
*
|
||||
* @param fd the file descriptor to be closed.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int dfs_file_close(struct dfs_fd *fd)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (fd == NULL)
|
||||
return -ENXIO;
|
||||
|
||||
if (fd->fops->close != NULL)
|
||||
result = fd->fops->close(fd);
|
||||
|
||||
/* close fd error, return */
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
rt_free(fd->path);
|
||||
fd->path = NULL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will perform a io control on a file descriptor.
|
||||
*
|
||||
* @param fd the file descriptor.
|
||||
* @param cmd the command to send to file descriptor.
|
||||
* @param args the argument to send to file descriptor.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int dfs_file_ioctl(struct dfs_fd *fd, int cmd, void *args)
|
||||
{
|
||||
if (fd == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* regular file system fd */
|
||||
if (fd->type == FT_REGULAR)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case F_GETFL:
|
||||
return fd->flags; /* return flags */
|
||||
case F_SETFL:
|
||||
{
|
||||
int flags = (int)(rt_base_t)args;
|
||||
int mask = O_NONBLOCK | O_APPEND;
|
||||
|
||||
flags &= mask;
|
||||
fd->flags &= ~mask;
|
||||
fd->flags |= flags;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd->fops->ioctl != NULL)
|
||||
return fd->fops->ioctl(fd, cmd, args);
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will read specified length data from a file descriptor to a
|
||||
* buffer.
|
||||
*
|
||||
* @param fd the file descriptor.
|
||||
* @param buf the buffer to save the read data.
|
||||
* @param len the length of data buffer to be read.
|
||||
*
|
||||
* @return the actual read data bytes or 0 on end of file or failed.
|
||||
*/
|
||||
int dfs_file_read(struct dfs_fd *fd, void *buf, size_t len)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (fd == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (fd->fops->read == NULL)
|
||||
return -ENOSYS;
|
||||
|
||||
if ((result = fd->fops->read(fd, buf, len)) < 0)
|
||||
fd->flags |= DFS_F_EOF;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will fetch directory entries from a directory descriptor.
|
||||
*
|
||||
* @param fd the directory descriptor.
|
||||
* @param dirp the dirent buffer to save result.
|
||||
* @param nbytes the available room in the buffer.
|
||||
*
|
||||
* @return the read dirent, others on failed.
|
||||
*/
|
||||
int dfs_file_getdents(struct dfs_fd *fd, struct dirent *dirp, size_t nbytes)
|
||||
{
|
||||
/* parameter check */
|
||||
if (fd == NULL || fd->type != FT_DIRECTORY)
|
||||
return -EINVAL;
|
||||
|
||||
if (fd->fops->getdents != NULL)
|
||||
return fd->fops->getdents(fd, dirp, nbytes);
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will unlink (remove) a specified path file from file system.
|
||||
*
|
||||
* @param path the specified path file to be unlinked.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int dfs_file_unlink(const char *path)
|
||||
{
|
||||
int result;
|
||||
char *fullpath;
|
||||
struct dfs_filesystem *fs;
|
||||
|
||||
/* Make sure we have an absolute path */
|
||||
fullpath = dfs_normalize_path(NULL, path);
|
||||
if (fullpath == NULL)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* get filesystem */
|
||||
if ((fs = dfs_filesystem_lookup(fullpath)) == NULL)
|
||||
{
|
||||
result = -ENOENT;
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
/* Check whether file is already open */
|
||||
if (fd_is_open(fullpath) == 0)
|
||||
{
|
||||
result = -EBUSY;
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
if (fs->ops->unlink != NULL)
|
||||
{
|
||||
if (!(fs->ops->flags & DFS_FS_FLAG_FULLPATH))
|
||||
{
|
||||
if (dfs_subdir(fs->path, fullpath) == NULL)
|
||||
result = fs->ops->unlink(fs, "/");
|
||||
else
|
||||
result = fs->ops->unlink(fs, dfs_subdir(fs->path, fullpath));
|
||||
}
|
||||
else
|
||||
result = fs->ops->unlink(fs, fullpath);
|
||||
}
|
||||
else result = -ENOSYS;
|
||||
|
||||
__exit:
|
||||
rt_free(fullpath);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will write some specified length data to file system.
|
||||
*
|
||||
* @param fd the file descriptor.
|
||||
* @param buf the data buffer to be written.
|
||||
* @param len the data buffer length
|
||||
*
|
||||
* @return the actual written data length.
|
||||
*/
|
||||
int dfs_file_write(struct dfs_fd *fd, const void *buf, size_t len)
|
||||
{
|
||||
if (fd == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (fd->fops->write == NULL)
|
||||
return -ENOSYS;
|
||||
|
||||
return fd->fops->write(fd, buf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will flush buffer on a file descriptor.
|
||||
*
|
||||
* @param fd the file descriptor.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int dfs_file_flush(struct dfs_fd *fd)
|
||||
{
|
||||
if (fd == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (fd->fops->flush == NULL)
|
||||
return -ENOSYS;
|
||||
|
||||
return fd->fops->flush(fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will seek the offset for specified file descriptor.
|
||||
*
|
||||
* @param fd the file descriptor.
|
||||
* @param offset the offset to be sought.
|
||||
*
|
||||
* @return the current position after seek.
|
||||
*/
|
||||
int dfs_file_lseek(struct dfs_fd *fd, off_t offset)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (fd == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (fd->fops->lseek == NULL)
|
||||
return -ENOSYS;
|
||||
|
||||
result = fd->fops->lseek(fd, offset);
|
||||
|
||||
/* update current position */
|
||||
if (result >= 0)
|
||||
fd->pos = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will get file information.
|
||||
*
|
||||
* @param path the file path.
|
||||
* @param buf the data buffer to save stat description.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int dfs_file_stat(const char *path, struct stat *buf)
|
||||
{
|
||||
int result;
|
||||
char *fullpath;
|
||||
struct dfs_filesystem *fs;
|
||||
|
||||
fullpath = dfs_normalize_path(NULL, path);
|
||||
if (fullpath == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((fs = dfs_filesystem_lookup(fullpath)) == NULL)
|
||||
{
|
||||
LOG_E("can't find mounted filesystem on this path:%s", fullpath);
|
||||
rt_free(fullpath);
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if ((fullpath[0] == '/' && fullpath[1] == '\0') ||
|
||||
(dfs_subdir(fs->path, fullpath) == NULL))
|
||||
{
|
||||
/* it's the root directory */
|
||||
buf->st_dev = 0;
|
||||
|
||||
buf->st_mode = S_IRUSR | S_IRGRP | S_IROTH |
|
||||
S_IWUSR | S_IWGRP | S_IWOTH;
|
||||
buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
|
||||
|
||||
buf->st_size = 0;
|
||||
buf->st_mtime = 0;
|
||||
|
||||
/* release full path */
|
||||
rt_free(fullpath);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fs->ops->stat == NULL)
|
||||
{
|
||||
rt_free(fullpath);
|
||||
LOG_E("the filesystem didn't implement this function");
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/* get the real file path and get file stat */
|
||||
if (fs->ops->flags & DFS_FS_FLAG_FULLPATH)
|
||||
result = fs->ops->stat(fs, fullpath, buf);
|
||||
else
|
||||
result = fs->ops->stat(fs, dfs_subdir(fs->path, fullpath), buf);
|
||||
}
|
||||
|
||||
rt_free(fullpath);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will rename an old path name to a new path name.
|
||||
*
|
||||
* @param oldpath the old path name.
|
||||
* @param newpath the new path name.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int dfs_file_rename(const char *oldpath, const char *newpath)
|
||||
{
|
||||
int result;
|
||||
struct dfs_filesystem *oldfs, *newfs;
|
||||
char *oldfullpath, *newfullpath;
|
||||
|
||||
result = RT_EOK;
|
||||
newfullpath = NULL;
|
||||
oldfullpath = NULL;
|
||||
|
||||
oldfullpath = dfs_normalize_path(NULL, oldpath);
|
||||
if (oldfullpath == NULL)
|
||||
{
|
||||
result = -ENOENT;
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
newfullpath = dfs_normalize_path(NULL, newpath);
|
||||
if (newfullpath == NULL)
|
||||
{
|
||||
result = -ENOENT;
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
oldfs = dfs_filesystem_lookup(oldfullpath);
|
||||
newfs = dfs_filesystem_lookup(newfullpath);
|
||||
|
||||
if (oldfs == newfs)
|
||||
{
|
||||
if (oldfs->ops->rename == NULL)
|
||||
{
|
||||
result = -ENOSYS;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (oldfs->ops->flags & DFS_FS_FLAG_FULLPATH)
|
||||
result = oldfs->ops->rename(oldfs, oldfullpath, newfullpath);
|
||||
else
|
||||
/* use sub directory to rename in file system */
|
||||
result = oldfs->ops->rename(oldfs,
|
||||
dfs_subdir(oldfs->path, oldfullpath),
|
||||
dfs_subdir(newfs->path, newfullpath));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = -EXDEV;
|
||||
}
|
||||
|
||||
__exit:
|
||||
rt_free(oldfullpath);
|
||||
rt_free(newfullpath);
|
||||
|
||||
/* not at same file system, return EXDEV */
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function is will cause the regular file referenced by fd
|
||||
* to be truncated to a size of precisely length bytes.
|
||||
*
|
||||
* @param fd the file descriptor.
|
||||
* @param length the length to be truncated.
|
||||
*
|
||||
* @return the status of truncated.
|
||||
*/
|
||||
int dfs_file_ftruncate(struct dfs_fd *fd, off_t length)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* fd is null or not a regular file system fd, or length is invalid */
|
||||
if (fd == NULL || fd->type != FT_REGULAR || length < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (fd->fops->ioctl == NULL)
|
||||
return -ENOSYS;
|
||||
|
||||
result = fd->fops->ioctl(fd, RT_FIOFTRUNCATE, (void*)&length);
|
||||
|
||||
/* update current size */
|
||||
if (result == 0)
|
||||
fd->size = length;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef RT_USING_FINSH
|
||||
#include <finsh.h>
|
||||
|
||||
static struct dfs_fd fd;
|
||||
static struct dirent dirent;
|
||||
void ls(const char *pathname)
|
||||
{
|
||||
struct stat stat;
|
||||
int length;
|
||||
char *fullpath, *path;
|
||||
|
||||
fullpath = NULL;
|
||||
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 = (char *)pathname;
|
||||
}
|
||||
|
||||
/* list directory */
|
||||
if (dfs_file_open(&fd, path, O_DIRECTORY) == 0)
|
||||
{
|
||||
rt_kprintf("Directory %s:\n", path);
|
||||
do
|
||||
{
|
||||
rt_memset(&dirent, 0, sizeof(struct dirent));
|
||||
length = dfs_file_getdents(&fd, &dirent, sizeof(struct dirent));
|
||||
if (length > 0)
|
||||
{
|
||||
rt_memset(&stat, 0, sizeof(struct stat));
|
||||
|
||||
/* build full path for each file */
|
||||
fullpath = dfs_normalize_path(path, dirent.d_name);
|
||||
if (fullpath == NULL)
|
||||
break;
|
||||
|
||||
if (dfs_file_stat(fullpath, &stat) == 0)
|
||||
{
|
||||
rt_kprintf("%-20s", dirent.d_name);
|
||||
if (S_ISDIR(stat.st_mode))
|
||||
{
|
||||
rt_kprintf("%-25s\n", "<DIR>");
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("%-25lu\n", (unsigned long)stat.st_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
rt_kprintf("BAD file: %s\n", dirent.d_name);
|
||||
rt_free(fullpath);
|
||||
}
|
||||
}
|
||||
while (length > 0);
|
||||
|
||||
dfs_file_close(&fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("No such directory\n");
|
||||
}
|
||||
if (pathname == NULL)
|
||||
rt_free(path);
|
||||
}
|
||||
FINSH_FUNCTION_EXPORT(ls, list directory contents);
|
||||
|
||||
void rm(const char *filename)
|
||||
{
|
||||
if (dfs_file_unlink(filename) < 0)
|
||||
{
|
||||
rt_kprintf("Delete %s failed\n", filename);
|
||||
}
|
||||
}
|
||||
FINSH_FUNCTION_EXPORT(rm, remove files or directories);
|
||||
|
||||
void cat(const char *filename)
|
||||
{
|
||||
uint32_t length;
|
||||
char buffer[81];
|
||||
|
||||
if (dfs_file_open(&fd, filename, O_RDONLY) < 0)
|
||||
{
|
||||
rt_kprintf("Open %s failed\n", filename);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
rt_memset(buffer, 0, sizeof(buffer));
|
||||
length = dfs_file_read(&fd, buffer, sizeof(buffer) - 1);
|
||||
if (length > 0)
|
||||
{
|
||||
rt_kprintf("%s", buffer);
|
||||
}
|
||||
}
|
||||
while (length > 0);
|
||||
rt_kprintf("\n");
|
||||
|
||||
dfs_file_close(&fd);
|
||||
}
|
||||
FINSH_FUNCTION_EXPORT(cat, print file);
|
||||
|
||||
#ifdef DFS_USING_POSIX
|
||||
#define BUF_SZ 4096
|
||||
static void copyfile(const char *src, const char *dst)
|
||||
{
|
||||
struct dfs_fd src_fd;
|
||||
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;
|
||||
}
|
||||
|
||||
if (dfs_file_open(&src_fd, src, O_RDONLY) < 0)
|
||||
{
|
||||
rt_free(block_ptr);
|
||||
rt_kprintf("Read %s failed\n", src);
|
||||
|
||||
return;
|
||||
}
|
||||
if (dfs_file_open(&fd, dst, O_WRONLY | O_CREAT) < 0)
|
||||
{
|
||||
rt_free(block_ptr);
|
||||
dfs_file_close(&src_fd);
|
||||
|
||||
rt_kprintf("Write %s failed\n", dst);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
read_bytes = dfs_file_read(&src_fd, block_ptr, BUF_SZ);
|
||||
if (read_bytes > 0)
|
||||
{
|
||||
int length;
|
||||
|
||||
length = dfs_file_write(&fd, 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(&src_fd);
|
||||
dfs_file_close(&fd);
|
||||
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_fd cpfd;
|
||||
if (dfs_file_open(&cpfd, src, O_DIRECTORY) < 0)
|
||||
{
|
||||
rt_kprintf("open %s failed\n", src);
|
||||
return ;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
rt_memset(&dirent, 0, sizeof(struct dirent));
|
||||
|
||||
length = dfs_file_getdents(&cpfd, &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_stat(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(&cpfd);
|
||||
}
|
||||
|
||||
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_stat(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 /* DFS_USING_POSIX */
|
||||
|
||||
#endif /* RT_USING_FINSH */
|
||||
/* @} */
|
||||
|
||||
@@ -0,0 +1,648 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2005-02-22 Bernard The first version.
|
||||
* 2010-06-30 Bernard Optimize for RT-Thread RTOS
|
||||
* 2011-03-12 Bernard fix the filesystem lookup issue.
|
||||
* 2017-11-30 Bernard fix the filesystem_operation_table issue.
|
||||
* 2017-12-05 Bernard fix the fs type search issue in mkfs.
|
||||
*/
|
||||
|
||||
#include <dfs_fs.h>
|
||||
#include <dfs_file.h>
|
||||
#include "dfs_private.h"
|
||||
|
||||
/**
|
||||
* @addtogroup FsApi
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* this function will register a file system instance to device file system.
|
||||
*
|
||||
* @param ops the file system instance to be registered.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int dfs_register(const struct dfs_filesystem_ops *ops)
|
||||
{
|
||||
int ret = RT_EOK;
|
||||
const struct dfs_filesystem_ops **empty = NULL;
|
||||
const struct dfs_filesystem_ops **iter;
|
||||
|
||||
/* lock filesystem */
|
||||
dfs_lock();
|
||||
/* check if this filesystem was already registered */
|
||||
for (iter = &filesystem_operation_table[0];
|
||||
iter < &filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX]; iter ++)
|
||||
{
|
||||
/* find out an empty filesystem type entry */
|
||||
if (*iter == NULL)
|
||||
(empty == NULL) ? (empty = iter) : 0;
|
||||
else if (strcmp((*iter)->name, ops->name) == 0)
|
||||
{
|
||||
rt_set_errno(-EEXIST);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* save the filesystem's operations */
|
||||
if (empty == NULL)
|
||||
{
|
||||
rt_set_errno(-ENOSPC);
|
||||
LOG_E("There is no space to register this file system (%s).", ops->name);
|
||||
ret = -1;
|
||||
}
|
||||
else if (ret == RT_EOK)
|
||||
{
|
||||
*empty = ops;
|
||||
}
|
||||
|
||||
dfs_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will return the file system mounted on specified path.
|
||||
*
|
||||
* @param path the specified path string.
|
||||
*
|
||||
* @return the found file system or NULL if no file system mounted on
|
||||
* specified path
|
||||
*/
|
||||
struct dfs_filesystem *dfs_filesystem_lookup(const char *path)
|
||||
{
|
||||
struct dfs_filesystem *iter;
|
||||
struct dfs_filesystem *fs = NULL;
|
||||
uint32_t fspath, prefixlen;
|
||||
|
||||
prefixlen = 0;
|
||||
|
||||
RT_ASSERT(path);
|
||||
|
||||
/* lock filesystem */
|
||||
dfs_lock();
|
||||
|
||||
/* lookup it in the filesystem table */
|
||||
for (iter = &filesystem_table[0];
|
||||
iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
|
||||
{
|
||||
if ((iter->path == NULL) || (iter->ops == NULL))
|
||||
continue;
|
||||
|
||||
fspath = strlen(iter->path);
|
||||
if ((fspath < prefixlen)
|
||||
|| (strncmp(iter->path, path, fspath) != 0))
|
||||
continue;
|
||||
|
||||
/* check next path separator */
|
||||
if (fspath > 1 && (strlen(path) > fspath) && (path[fspath] != '/'))
|
||||
continue;
|
||||
|
||||
fs = iter;
|
||||
prefixlen = fspath;
|
||||
}
|
||||
|
||||
dfs_unlock();
|
||||
|
||||
return fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will return the mounted path for specified device.
|
||||
*
|
||||
* @param device the device object which is mounted.
|
||||
*
|
||||
* @return the mounted path or NULL if none device mounted.
|
||||
*/
|
||||
const char *dfs_filesystem_get_mounted_path(struct rt_device *device)
|
||||
{
|
||||
const char *path = NULL;
|
||||
struct dfs_filesystem *iter;
|
||||
|
||||
dfs_lock();
|
||||
for (iter = &filesystem_table[0];
|
||||
iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
|
||||
{
|
||||
/* find the mounted device */
|
||||
if (iter->ops == NULL) continue;
|
||||
else if (iter->dev_id == device)
|
||||
{
|
||||
path = iter->path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* release filesystem_table lock */
|
||||
dfs_unlock();
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will fetch the partition table on specified buffer.
|
||||
*
|
||||
* @param part the returned partition structure.
|
||||
* @param buf the buffer contains partition table.
|
||||
* @param pindex the index of partition table to fetch.
|
||||
*
|
||||
* @return RT_EOK on successful or -RT_ERROR on failed.
|
||||
*/
|
||||
int dfs_filesystem_get_partition(struct dfs_partition *part,
|
||||
uint8_t *buf,
|
||||
uint32_t pindex)
|
||||
{
|
||||
#define DPT_ADDRESS 0x1be /* device partition offset in Boot Sector */
|
||||
#define DPT_ITEM_SIZE 16 /* partition item size */
|
||||
|
||||
uint8_t *dpt;
|
||||
uint8_t type;
|
||||
|
||||
RT_ASSERT(part != NULL);
|
||||
RT_ASSERT(buf != NULL);
|
||||
|
||||
dpt = buf + DPT_ADDRESS + pindex * DPT_ITEM_SIZE;
|
||||
|
||||
/* check if it is a valid partition table */
|
||||
if ((*dpt != 0x80) && (*dpt != 0x00))
|
||||
return -EIO;
|
||||
|
||||
/* get partition type */
|
||||
type = *(dpt + 4);
|
||||
if (type == 0)
|
||||
return -EIO;
|
||||
|
||||
/* set partition information
|
||||
* size is the number of 512-Byte */
|
||||
part->type = type;
|
||||
part->offset = *(dpt + 8) | *(dpt + 9) << 8 | *(dpt + 10) << 16 | *(dpt + 11) << 24;
|
||||
part->size = *(dpt + 12) | *(dpt + 13) << 8 | *(dpt + 14) << 16 | *(dpt + 15) << 24;
|
||||
|
||||
rt_kprintf("found part[%d], begin: %d, size: ",
|
||||
pindex, part->offset * 512);
|
||||
if ((part->size >> 11) == 0)
|
||||
rt_kprintf("%d%s", part->size >> 1, "KB\n"); /* KB */
|
||||
else
|
||||
{
|
||||
unsigned int part_size;
|
||||
part_size = part->size >> 11; /* MB */
|
||||
if ((part_size >> 10) == 0)
|
||||
rt_kprintf("%d.%d%s", part_size, (part->size >> 1) & 0x3FF, "MB\n");
|
||||
else
|
||||
rt_kprintf("%d.%d%s", part_size >> 10, part_size & 0x3FF, "GB\n");
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will mount a file system on a specified path.
|
||||
*
|
||||
* @param device_name the name of device which includes a file system.
|
||||
* @param path the path to mount a file system
|
||||
* @param filesystemtype the file system type
|
||||
* @param rwflag the read/write etc. flag.
|
||||
* @param data the private data(parameter) for this file system.
|
||||
*
|
||||
* @return 0 on successful or -1 on failed.
|
||||
*/
|
||||
int dfs_mount(const char *device_name,
|
||||
const char *path,
|
||||
const char *filesystemtype,
|
||||
unsigned long rwflag,
|
||||
const void *data)
|
||||
{
|
||||
const struct dfs_filesystem_ops **ops;
|
||||
struct dfs_filesystem *iter;
|
||||
struct dfs_filesystem *fs = NULL;
|
||||
char *fullpath = NULL;
|
||||
rt_device_t dev_id;
|
||||
|
||||
/* open specific device */
|
||||
if (device_name == NULL)
|
||||
{
|
||||
/* which is a non-device filesystem mount */
|
||||
dev_id = NULL;
|
||||
}
|
||||
else if ((dev_id = rt_device_find(device_name)) == NULL)
|
||||
{
|
||||
/* no this device */
|
||||
rt_set_errno(-ENODEV);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* find out the specific filesystem */
|
||||
dfs_lock();
|
||||
|
||||
for (ops = &filesystem_operation_table[0];
|
||||
ops < &filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX]; ops++)
|
||||
if ((*ops != NULL) && (strcmp((*ops)->name, filesystemtype) == 0))
|
||||
break;
|
||||
|
||||
dfs_unlock();
|
||||
|
||||
if (ops == &filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX])
|
||||
{
|
||||
/* can't find filesystem */
|
||||
rt_set_errno(-ENODEV);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check if there is mount implementation */
|
||||
if ((*ops == NULL) || ((*ops)->mount == NULL))
|
||||
{
|
||||
rt_set_errno(-ENOSYS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* make full path for special file */
|
||||
fullpath = dfs_normalize_path(NULL, path);
|
||||
if (fullpath == NULL) /* not an abstract path */
|
||||
{
|
||||
rt_set_errno(-ENOTDIR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if the path exists or not, raw APIs call, fixme */
|
||||
if ((strcmp(fullpath, "/") != 0) && (strcmp(fullpath, "/dev") != 0))
|
||||
{
|
||||
struct dfs_fd fd;
|
||||
|
||||
if (dfs_file_open(&fd, fullpath, O_RDONLY | O_DIRECTORY) < 0)
|
||||
{
|
||||
rt_free(fullpath);
|
||||
rt_set_errno(-ENOTDIR);
|
||||
|
||||
return -1;
|
||||
}
|
||||
dfs_file_close(&fd);
|
||||
}
|
||||
|
||||
/* check whether the file system mounted or not in the filesystem table
|
||||
* if it is unmounted yet, find out an empty entry */
|
||||
dfs_lock();
|
||||
|
||||
for (iter = &filesystem_table[0];
|
||||
iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
|
||||
{
|
||||
/* check if it is an empty filesystem table entry? if it is, save fs */
|
||||
if (iter->ops == NULL)
|
||||
(fs == NULL) ? (fs = iter) : 0;
|
||||
/* check if the PATH is mounted */
|
||||
else if (strcmp(iter->path, path) == 0)
|
||||
{
|
||||
rt_set_errno(-EINVAL);
|
||||
goto err1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((fs == NULL) && (iter == &filesystem_table[DFS_FILESYSTEMS_MAX]))
|
||||
{
|
||||
rt_set_errno(-ENOSPC);
|
||||
LOG_E("There is no space to mount this file system (%s).", filesystemtype);
|
||||
goto err1;
|
||||
}
|
||||
|
||||
/* register file system */
|
||||
fs->path = fullpath;
|
||||
fs->ops = *ops;
|
||||
fs->dev_id = dev_id;
|
||||
/* release filesystem_table lock */
|
||||
dfs_unlock();
|
||||
|
||||
/* open device, but do not check the status of device */
|
||||
if (dev_id != NULL)
|
||||
{
|
||||
if (rt_device_open(fs->dev_id,
|
||||
RT_DEVICE_OFLAG_RDWR) != RT_EOK)
|
||||
{
|
||||
/* The underlying device has error, clear the entry. */
|
||||
dfs_lock();
|
||||
rt_memset(fs, 0, sizeof(struct dfs_filesystem));
|
||||
|
||||
goto err1;
|
||||
}
|
||||
}
|
||||
|
||||
/* call mount of this filesystem */
|
||||
if ((*ops)->mount(fs, rwflag, data) < 0)
|
||||
{
|
||||
/* close device */
|
||||
if (dev_id != NULL)
|
||||
rt_device_close(fs->dev_id);
|
||||
|
||||
/* mount failed */
|
||||
dfs_lock();
|
||||
/* clear filesystem table entry */
|
||||
rt_memset(fs, 0, sizeof(struct dfs_filesystem));
|
||||
|
||||
goto err1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
dfs_unlock();
|
||||
rt_free(fullpath);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will unmount a file system on specified path.
|
||||
*
|
||||
* @param specialfile the specified path which mounted a file system.
|
||||
*
|
||||
* @return 0 on successful or -1 on failed.
|
||||
*/
|
||||
int dfs_unmount(const char *specialfile)
|
||||
{
|
||||
char *fullpath;
|
||||
struct dfs_filesystem *iter;
|
||||
struct dfs_filesystem *fs = NULL;
|
||||
|
||||
fullpath = dfs_normalize_path(NULL, specialfile);
|
||||
if (fullpath == NULL)
|
||||
{
|
||||
rt_set_errno(-ENOTDIR);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* lock filesystem */
|
||||
dfs_lock();
|
||||
|
||||
for (iter = &filesystem_table[0];
|
||||
iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
|
||||
{
|
||||
/* check if the PATH is mounted */
|
||||
if ((iter->path != NULL) && (strcmp(iter->path, fullpath) == 0))
|
||||
{
|
||||
fs = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fs == NULL ||
|
||||
fs->ops->unmount == NULL ||
|
||||
fs->ops->unmount(fs) < 0)
|
||||
{
|
||||
goto err1;
|
||||
}
|
||||
|
||||
/* close device, but do not check the status of device */
|
||||
if (fs->dev_id != NULL)
|
||||
rt_device_close(fs->dev_id);
|
||||
|
||||
if (fs->path != NULL)
|
||||
rt_free(fs->path);
|
||||
|
||||
/* clear this filesystem table entry */
|
||||
rt_memset(fs, 0, sizeof(struct dfs_filesystem));
|
||||
|
||||
dfs_unlock();
|
||||
rt_free(fullpath);
|
||||
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
dfs_unlock();
|
||||
rt_free(fullpath);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* make a file system on the special device
|
||||
*
|
||||
* @param fs_name the file system name
|
||||
* @param device_name the special device name
|
||||
*
|
||||
* @return 0 on successful, otherwise failed.
|
||||
*/
|
||||
int dfs_mkfs(const char *fs_name, const char *device_name)
|
||||
{
|
||||
int index;
|
||||
rt_device_t dev_id = NULL;
|
||||
|
||||
/* check device name, and it should not be NULL */
|
||||
if (device_name != NULL)
|
||||
dev_id = rt_device_find(device_name);
|
||||
|
||||
if (dev_id == NULL)
|
||||
{
|
||||
rt_set_errno(-ENODEV);
|
||||
LOG_E("Device (%s) was not found", device_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* lock file system */
|
||||
dfs_lock();
|
||||
/* find the file system operations */
|
||||
for (index = 0; index < DFS_FILESYSTEM_TYPES_MAX; index ++)
|
||||
{
|
||||
if (filesystem_operation_table[index] != NULL &&
|
||||
strcmp(filesystem_operation_table[index]->name, fs_name) == 0)
|
||||
break;
|
||||
}
|
||||
dfs_unlock();
|
||||
|
||||
if (index < DFS_FILESYSTEM_TYPES_MAX)
|
||||
{
|
||||
/* find file system operation */
|
||||
const struct dfs_filesystem_ops *ops = filesystem_operation_table[index];
|
||||
if (ops->mkfs == NULL)
|
||||
{
|
||||
LOG_E("The file system (%s) mkfs function was not implement", fs_name);
|
||||
rt_set_errno(-ENOSYS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ops->mkfs(dev_id);
|
||||
}
|
||||
|
||||
LOG_E("File system (%s) was not found.", fs_name);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function will return the information about a mounted file system.
|
||||
*
|
||||
* @param path the path which mounted file system.
|
||||
* @param buffer the buffer to save the returned information.
|
||||
*
|
||||
* @return 0 on successful, others on failed.
|
||||
*/
|
||||
int dfs_statfs(const char *path, struct statfs *buffer)
|
||||
{
|
||||
struct dfs_filesystem *fs;
|
||||
|
||||
fs = dfs_filesystem_lookup(path);
|
||||
if (fs != NULL)
|
||||
{
|
||||
if (fs->ops->statfs != NULL)
|
||||
return fs->ops->statfs(fs, buffer);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef RT_USING_DFS_MNTTABLE
|
||||
int dfs_mount_table(void)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (mount_table[index].path == NULL) break;
|
||||
|
||||
if (dfs_mount(mount_table[index].device_name,
|
||||
mount_table[index].path,
|
||||
mount_table[index].filesystemtype,
|
||||
mount_table[index].rwflag,
|
||||
mount_table[index].data) != 0)
|
||||
{
|
||||
LOG_E("mount fs[%s] on %s failed.\n", mount_table[index].filesystemtype,
|
||||
mount_table[index].path);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
index ++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(dfs_mount_table);
|
||||
|
||||
int dfs_mount_device(rt_device_t dev)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
if(dev == RT_NULL) {
|
||||
rt_kprintf("the device is NULL to be mounted.\n");
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (mount_table[index].path == NULL) break;
|
||||
|
||||
if(strcmp(mount_table[index].device_name, dev->parent.name) == 0) {
|
||||
if (dfs_mount(mount_table[index].device_name,
|
||||
mount_table[index].path,
|
||||
mount_table[index].filesystemtype,
|
||||
mount_table[index].rwflag,
|
||||
mount_table[index].data) != 0)
|
||||
{
|
||||
LOG_E("mount fs[%s] device[%s] to %s failed.\n", mount_table[index].filesystemtype, dev->parent.name,
|
||||
mount_table[index].path);
|
||||
return -RT_ERROR;
|
||||
} else {
|
||||
LOG_D("mount fs[%s] device[%s] to %s ok.\n", mount_table[index].filesystemtype, dev->parent.name,
|
||||
mount_table[index].path);
|
||||
return RT_EOK;
|
||||
}
|
||||
}
|
||||
|
||||
index ++;
|
||||
}
|
||||
|
||||
rt_kprintf("can't find device:%s to be mounted.\n", dev->parent.name);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
int dfs_unmount_device(rt_device_t dev)
|
||||
{
|
||||
struct dfs_filesystem *iter;
|
||||
struct dfs_filesystem *fs = NULL;
|
||||
|
||||
/* lock filesystem */
|
||||
dfs_lock();
|
||||
|
||||
for (iter = &filesystem_table[0];
|
||||
iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
|
||||
{
|
||||
/* check if the PATH is mounted */
|
||||
if (strcmp(iter->dev_id->parent.name, dev->parent.name) == 0)
|
||||
{
|
||||
fs = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fs == NULL ||
|
||||
fs->ops->unmount == NULL ||
|
||||
fs->ops->unmount(fs) < 0)
|
||||
{
|
||||
goto err1;
|
||||
}
|
||||
|
||||
/* close device, but do not check the status of device */
|
||||
if (fs->dev_id != NULL)
|
||||
rt_device_close(fs->dev_id);
|
||||
|
||||
if (fs->path != NULL)
|
||||
rt_free(fs->path);
|
||||
|
||||
/* clear this filesystem table entry */
|
||||
rt_memset(fs, 0, sizeof(struct dfs_filesystem));
|
||||
|
||||
dfs_unlock();
|
||||
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
dfs_unlock();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef RT_USING_FINSH
|
||||
#include <finsh.h>
|
||||
void mkfs(const char *fs_name, const char *device_name)
|
||||
{
|
||||
dfs_mkfs(fs_name, device_name);
|
||||
}
|
||||
FINSH_FUNCTION_EXPORT(mkfs, make a file system);
|
||||
|
||||
int df(const char *path)
|
||||
{
|
||||
int result;
|
||||
int minor = 0;
|
||||
long long cap;
|
||||
struct statfs buffer;
|
||||
|
||||
int unit_index = 0;
|
||||
char *unit_str[] = {"KB", "MB", "GB"};
|
||||
|
||||
result = dfs_statfs(path ? path : NULL, &buffer);
|
||||
if (result != 0)
|
||||
{
|
||||
rt_kprintf("dfs_statfs failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cap = ((long long)buffer.f_bsize) * ((long long)buffer.f_bfree) / 1024LL;
|
||||
for (unit_index = 0; unit_index < 2; unit_index ++)
|
||||
{
|
||||
if (cap < 1024) break;
|
||||
|
||||
minor = (cap % 1024) * 10 / 1024; /* only one decimal point */
|
||||
cap = cap / 1024;
|
||||
}
|
||||
|
||||
rt_kprintf("disk free: %d.%d %s [ %d block, %d bytes per block ]\n",
|
||||
(unsigned long)cap, minor, unit_str[unit_index], buffer.f_bfree, buffer.f_bsize);
|
||||
return 0;
|
||||
}
|
||||
FINSH_FUNCTION_EXPORT(df, get disk free);
|
||||
#endif
|
||||
|
||||
/* @} */
|
||||
@@ -0,0 +1,953 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2009-05-27 Yi.qiu The first version
|
||||
* 2018-02-07 Bernard Change the 3rd parameter of open/fcntl/ioctl to '...'
|
||||
* 2022-01-19 Meco Man add creat()
|
||||
*/
|
||||
|
||||
#include <dfs_file.h>
|
||||
#include <dfs_private.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will open a file and
|
||||
* return a file descriptor according specified flags.
|
||||
*
|
||||
* @param file the path name of file.
|
||||
* @param flags the file open flags.
|
||||
*
|
||||
* @return the non-negative integer on successful open, others for failed.
|
||||
*/
|
||||
int open(const char *file, int flags, ...)
|
||||
{
|
||||
int fd, result;
|
||||
struct dfs_fd *d;
|
||||
|
||||
/* allocate a fd */
|
||||
fd = fd_new();
|
||||
if (fd < 0)
|
||||
{
|
||||
rt_set_errno(-ENOMEM);
|
||||
|
||||
return -1;
|
||||
}
|
||||
d = fd_get(fd);
|
||||
|
||||
result = dfs_file_open(d, file, flags);
|
||||
if (result < 0)
|
||||
{
|
||||
/* release the ref-count of fd */
|
||||
fd_put(d);
|
||||
fd_put(d);
|
||||
|
||||
rt_set_errno(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* release the ref-count of fd */
|
||||
fd_put(d);
|
||||
|
||||
return fd;
|
||||
}
|
||||
RTM_EXPORT(open);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version,
|
||||
* which will create a new file or rewrite an existing one
|
||||
*
|
||||
* @param path the path name of file.
|
||||
* @param mode the file permission bits to be used in creating the file (not used, can be 0)
|
||||
*
|
||||
* @return the non-negative integer on successful open, others for failed.
|
||||
*/
|
||||
int creat(const char *path, mode_t mode)
|
||||
{
|
||||
return open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
|
||||
}
|
||||
RTM_EXPORT(creat);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will close the open
|
||||
* file descriptor.
|
||||
*
|
||||
* @param fd the file descriptor.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int close(int fd)
|
||||
{
|
||||
int result;
|
||||
struct dfs_fd *d;
|
||||
|
||||
d = fd_get(fd);
|
||||
if (d == NULL)
|
||||
{
|
||||
rt_set_errno(-EBADF);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = dfs_file_close(d);
|
||||
fd_put(d);
|
||||
|
||||
if (result < 0)
|
||||
{
|
||||
rt_set_errno(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd_put(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
RTM_EXPORT(close);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will read specified data
|
||||
* buffer length for an open file descriptor.
|
||||
*
|
||||
* @param fd the file descriptor.
|
||||
* @param buf the buffer to save the read data.
|
||||
* @param len the maximal length of data buffer
|
||||
*
|
||||
* @return the actual read data buffer length. If the returned value is 0, it
|
||||
* may be reach the end of file, please check errno.
|
||||
*/
|
||||
#ifdef _READ_WRITE_RETURN_TYPE
|
||||
_READ_WRITE_RETURN_TYPE read(int fd, void *buf, size_t len) /* some gcc tool chains will use different data structure */
|
||||
#else
|
||||
ssize_t read(int fd, void *buf, size_t len)
|
||||
#endif
|
||||
{
|
||||
int result;
|
||||
struct dfs_fd *d;
|
||||
|
||||
/* get the fd */
|
||||
d = fd_get(fd);
|
||||
if (d == NULL)
|
||||
{
|
||||
rt_set_errno(-EBADF);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = dfs_file_read(d, buf, len);
|
||||
if (result < 0)
|
||||
{
|
||||
fd_put(d);
|
||||
rt_set_errno(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* release the ref-count of fd */
|
||||
fd_put(d);
|
||||
|
||||
return result;
|
||||
}
|
||||
RTM_EXPORT(read);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will write specified data
|
||||
* buffer length for an open file descriptor.
|
||||
*
|
||||
* @param fd the file descriptor
|
||||
* @param buf the data buffer to be written.
|
||||
* @param len the data buffer length.
|
||||
*
|
||||
* @return the actual written data buffer length.
|
||||
*/
|
||||
#ifdef _READ_WRITE_RETURN_TYPE
|
||||
_READ_WRITE_RETURN_TYPE write(int fd, const void *buf, size_t len) /* some gcc tool chains will use different data structure */
|
||||
#else
|
||||
ssize_t write(int fd, const void *buf, size_t len)
|
||||
#endif
|
||||
{
|
||||
int result;
|
||||
struct dfs_fd *d;
|
||||
|
||||
/* get the fd */
|
||||
d = fd_get(fd);
|
||||
if (d == NULL)
|
||||
{
|
||||
rt_set_errno(-EBADF);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = dfs_file_write(d, buf, len);
|
||||
if (result < 0)
|
||||
{
|
||||
fd_put(d);
|
||||
rt_set_errno(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* release the ref-count of fd */
|
||||
fd_put(d);
|
||||
|
||||
return result;
|
||||
}
|
||||
RTM_EXPORT(write);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will seek the offset for
|
||||
* an open file descriptor.
|
||||
*
|
||||
* @param fd the file descriptor.
|
||||
* @param offset the offset to be seeked.
|
||||
* @param whence the directory of seek.
|
||||
*
|
||||
* @return the current read/write position in the file, or -1 on failed.
|
||||
*/
|
||||
off_t lseek(int fd, off_t offset, int whence)
|
||||
{
|
||||
int result;
|
||||
struct dfs_fd *d;
|
||||
|
||||
d = fd_get(fd);
|
||||
if (d == NULL)
|
||||
{
|
||||
rt_set_errno(-EBADF);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_SET:
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
offset += d->pos;
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
offset += d->size;
|
||||
break;
|
||||
|
||||
default:
|
||||
fd_put(d);
|
||||
rt_set_errno(-EINVAL);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (offset < 0)
|
||||
{
|
||||
fd_put(d);
|
||||
rt_set_errno(-EINVAL);
|
||||
|
||||
return -1;
|
||||
}
|
||||
result = dfs_file_lseek(d, offset);
|
||||
if (result < 0)
|
||||
{
|
||||
fd_put(d);
|
||||
rt_set_errno(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* release the ref-count of fd */
|
||||
fd_put(d);
|
||||
|
||||
return offset;
|
||||
}
|
||||
RTM_EXPORT(lseek);
|
||||
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will rename old file name
|
||||
* to new file name.
|
||||
*
|
||||
* @param old the old file name.
|
||||
* @param new the new file name.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*
|
||||
* note: the old and new file name must be belong to a same file system.
|
||||
*/
|
||||
int rename(const char *old_file, const char *new_file)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = dfs_file_rename(old_file, new_file);
|
||||
if (result < 0)
|
||||
{
|
||||
rt_set_errno(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
RTM_EXPORT(rename);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will unlink (remove) a
|
||||
* specified path file from file system.
|
||||
*
|
||||
* @param pathname the specified path name to be unlinked.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int unlink(const char *pathname)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = dfs_file_unlink(pathname);
|
||||
if (result < 0)
|
||||
{
|
||||
rt_set_errno(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
RTM_EXPORT(unlink);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will get file information.
|
||||
*
|
||||
* @param file the file name
|
||||
* @param buf the data buffer to save stat description.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int stat(const char *file, struct stat *buf)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = dfs_file_stat(file, buf);
|
||||
if (result < 0)
|
||||
{
|
||||
rt_set_errno(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
RTM_EXPORT(stat);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will get file status.
|
||||
*
|
||||
* @param fildes the file description
|
||||
* @param buf the data buffer to save stat description.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int fstat(int fildes, struct stat *buf)
|
||||
{
|
||||
struct dfs_fd *d;
|
||||
|
||||
/* get the fd */
|
||||
d = fd_get(fildes);
|
||||
if (d == NULL)
|
||||
{
|
||||
rt_set_errno(-EBADF);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* it's the root directory */
|
||||
buf->st_dev = 0;
|
||||
|
||||
buf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
|
||||
S_IWUSR | S_IWGRP | S_IWOTH;
|
||||
if (d->type == FT_DIRECTORY)
|
||||
{
|
||||
buf->st_mode &= ~S_IFREG;
|
||||
buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
|
||||
}
|
||||
|
||||
buf->st_size = d->size;
|
||||
buf->st_mtime = 0;
|
||||
|
||||
fd_put(d);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
RTM_EXPORT(fstat);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which shall request that all data
|
||||
* for the open file descriptor named by fildes is to be transferred to the storage
|
||||
* device associated with the file described by fildes.
|
||||
*
|
||||
* @param fildes the file description
|
||||
*
|
||||
* @return 0 on successful completion. Otherwise, -1 shall be returned and errno
|
||||
* set to indicate the error.
|
||||
*/
|
||||
int fsync(int fildes)
|
||||
{
|
||||
int ret;
|
||||
struct dfs_fd *d;
|
||||
|
||||
/* get the fd */
|
||||
d = fd_get(fildes);
|
||||
if (d == NULL)
|
||||
{
|
||||
rt_set_errno(-EBADF);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = dfs_file_flush(d);
|
||||
|
||||
fd_put(d);
|
||||
return ret;
|
||||
}
|
||||
RTM_EXPORT(fsync);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which shall perform a variety of
|
||||
* control functions on devices.
|
||||
*
|
||||
* @param fildes the file description
|
||||
* @param cmd the specified command
|
||||
* @param data represents the additional information that is needed by this
|
||||
* specific device to perform the requested function.
|
||||
*
|
||||
* @return 0 on successful completion. Otherwise, -1 shall be returned and errno
|
||||
* set to indicate the error.
|
||||
*/
|
||||
int fcntl(int fildes, int cmd, ...)
|
||||
{
|
||||
int ret = -1;
|
||||
struct dfs_fd *d;
|
||||
|
||||
/* get the fd */
|
||||
d = fd_get(fildes);
|
||||
if (d)
|
||||
{
|
||||
void *arg;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, cmd);
|
||||
arg = va_arg(ap, void *);
|
||||
va_end(ap);
|
||||
|
||||
ret = dfs_file_ioctl(d, cmd, arg);
|
||||
fd_put(d);
|
||||
}
|
||||
else ret = -EBADF;
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
rt_set_errno(ret);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
RTM_EXPORT(fcntl);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which shall perform a variety of
|
||||
* control functions on devices.
|
||||
*
|
||||
* @param fildes the file description
|
||||
* @param cmd the specified command
|
||||
* @param data represents the additional information that is needed by this
|
||||
* specific device to perform the requested function.
|
||||
*
|
||||
* @return 0 on successful completion. Otherwise, -1 shall be returned and errno
|
||||
* set to indicate the error.
|
||||
*/
|
||||
int ioctl(int fildes, int cmd, ...)
|
||||
{
|
||||
void *arg;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, cmd);
|
||||
arg = va_arg(ap, void *);
|
||||
va_end(ap);
|
||||
|
||||
/* we use fcntl for this API. */
|
||||
return fcntl(fildes, cmd, arg);
|
||||
}
|
||||
RTM_EXPORT(ioctl);
|
||||
|
||||
/**
|
||||
*
|
||||
* this function is a POSIX compliant version, which cause the regular file
|
||||
* referenced by fd to be truncated to a size of precisely length bytes.
|
||||
* @param fd the file descriptor.
|
||||
* @param length the length to be truncated.
|
||||
*
|
||||
* @return Upon successful completion, ftruncate() shall return 0;
|
||||
* otherwise, -1 shall be returned and errno set to indicate the error.
|
||||
*/
|
||||
int ftruncate(int fd, off_t length)
|
||||
{
|
||||
int result;
|
||||
struct dfs_fd *d;
|
||||
|
||||
d = fd_get(fd);
|
||||
if (d == NULL)
|
||||
{
|
||||
rt_set_errno(-EBADF);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (length < 0)
|
||||
{
|
||||
fd_put(d);
|
||||
rt_set_errno(-EINVAL);
|
||||
|
||||
return -1;
|
||||
}
|
||||
result = dfs_file_ftruncate(d, length);
|
||||
if (result < 0)
|
||||
{
|
||||
fd_put(d);
|
||||
rt_set_errno(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* release the ref-count of fd */
|
||||
fd_put(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
RTM_EXPORT(ftruncate);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will return the
|
||||
* information about a mounted file system.
|
||||
*
|
||||
* @param path the path which mounted file system.
|
||||
* @param buf the buffer to save the returned information.
|
||||
*
|
||||
* @return 0 on successful, others on failed.
|
||||
*/
|
||||
int statfs(const char *path, struct statfs *buf)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = dfs_statfs(path, buf);
|
||||
if (result < 0)
|
||||
{
|
||||
rt_set_errno(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
RTM_EXPORT(statfs);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will make a directory
|
||||
*
|
||||
* @param path the directory path to be made.
|
||||
* @param mode
|
||||
*
|
||||
* @return 0 on successful, others on failed.
|
||||
*/
|
||||
int mkdir(const char *path, mode_t mode)
|
||||
{
|
||||
int fd;
|
||||
struct dfs_fd *d;
|
||||
int result;
|
||||
|
||||
fd = fd_new();
|
||||
if (fd == -1)
|
||||
{
|
||||
rt_set_errno(-ENOMEM);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
d = fd_get(fd);
|
||||
|
||||
result = dfs_file_open(d, path, O_DIRECTORY | O_CREAT);
|
||||
|
||||
if (result < 0)
|
||||
{
|
||||
fd_put(d);
|
||||
fd_put(d);
|
||||
rt_set_errno(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
dfs_file_close(d);
|
||||
fd_put(d);
|
||||
fd_put(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
RTM_EXPORT(mkdir);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will remove a directory.
|
||||
*
|
||||
* @param pathname the path name to be removed.
|
||||
*
|
||||
* @return 0 on successful, others on failed.
|
||||
*/
|
||||
int rmdir(const char *pathname)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = dfs_file_unlink(pathname);
|
||||
if (result < 0)
|
||||
{
|
||||
rt_set_errno(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
RTM_EXPORT(rmdir);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will open a directory.
|
||||
*
|
||||
* @param name the path name to be open.
|
||||
*
|
||||
* @return the DIR pointer of directory, NULL on open directory failed.
|
||||
*/
|
||||
DIR *opendir(const char *name)
|
||||
{
|
||||
struct dfs_fd *d;
|
||||
int fd, result;
|
||||
DIR *t;
|
||||
|
||||
t = NULL;
|
||||
|
||||
/* allocate a fd */
|
||||
fd = fd_new();
|
||||
if (fd == -1)
|
||||
{
|
||||
rt_set_errno(-ENOMEM);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
d = fd_get(fd);
|
||||
|
||||
result = dfs_file_open(d, name, O_RDONLY | O_DIRECTORY);
|
||||
if (result >= 0)
|
||||
{
|
||||
/* open successfully */
|
||||
t = (DIR *) rt_malloc(sizeof(DIR));
|
||||
if (t == NULL)
|
||||
{
|
||||
dfs_file_close(d);
|
||||
fd_put(d);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_memset(t, 0, sizeof(DIR));
|
||||
|
||||
t->fd = fd;
|
||||
}
|
||||
fd_put(d);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* open failed */
|
||||
fd_put(d);
|
||||
fd_put(d);
|
||||
rt_set_errno(result);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
RTM_EXPORT(opendir);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will return a pointer
|
||||
* to a dirent structure representing the next directory entry in the
|
||||
* directory stream.
|
||||
*
|
||||
* @param d the directory stream pointer.
|
||||
*
|
||||
* @return the next directory entry, NULL on the end of directory or failed.
|
||||
*/
|
||||
struct dirent *readdir(DIR *d)
|
||||
{
|
||||
int result;
|
||||
struct dfs_fd *fd;
|
||||
|
||||
fd = fd_get(d->fd);
|
||||
if (fd == NULL)
|
||||
{
|
||||
rt_set_errno(-EBADF);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (d->num)
|
||||
{
|
||||
struct dirent *dirent_ptr;
|
||||
dirent_ptr = (struct dirent *)&d->buf[d->cur];
|
||||
d->cur += dirent_ptr->d_reclen;
|
||||
}
|
||||
|
||||
if (!d->num || d->cur >= d->num)
|
||||
{
|
||||
/* get a new entry */
|
||||
result = dfs_file_getdents(fd,
|
||||
(struct dirent *)d->buf,
|
||||
sizeof(d->buf) - 1);
|
||||
if (result <= 0)
|
||||
{
|
||||
fd_put(fd);
|
||||
rt_set_errno(result);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
d->num = result;
|
||||
d->cur = 0; /* current entry index */
|
||||
}
|
||||
|
||||
fd_put(fd);
|
||||
|
||||
return (struct dirent *)(d->buf + d->cur);
|
||||
}
|
||||
RTM_EXPORT(readdir);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will return current
|
||||
* location in directory stream.
|
||||
*
|
||||
* @param d the directory stream pointer.
|
||||
*
|
||||
* @return the current location in directory stream.
|
||||
*/
|
||||
long telldir(DIR *d)
|
||||
{
|
||||
struct dfs_fd *fd;
|
||||
long result;
|
||||
|
||||
fd = fd_get(d->fd);
|
||||
if (fd == NULL)
|
||||
{
|
||||
rt_set_errno(-EBADF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = fd->pos - d->num + d->cur;
|
||||
fd_put(fd);
|
||||
|
||||
return result;
|
||||
}
|
||||
RTM_EXPORT(telldir);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will set position of
|
||||
* next directory structure in the directory stream.
|
||||
*
|
||||
* @param d the directory stream.
|
||||
* @param offset the offset in directory stream.
|
||||
*/
|
||||
void seekdir(DIR *d, off_t offset)
|
||||
{
|
||||
struct dfs_fd *fd;
|
||||
|
||||
fd = fd_get(d->fd);
|
||||
if (fd == NULL)
|
||||
{
|
||||
rt_set_errno(-EBADF);
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
/* seek to the offset position of directory */
|
||||
if (dfs_file_lseek(fd, offset) >= 0)
|
||||
d->num = d->cur = 0;
|
||||
fd_put(fd);
|
||||
}
|
||||
RTM_EXPORT(seekdir);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will reset directory
|
||||
* stream.
|
||||
*
|
||||
* @param d the directory stream.
|
||||
*/
|
||||
void rewinddir(DIR *d)
|
||||
{
|
||||
struct dfs_fd *fd;
|
||||
|
||||
fd = fd_get(d->fd);
|
||||
if (fd == NULL)
|
||||
{
|
||||
rt_set_errno(-EBADF);
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
/* seek to the beginning of directory */
|
||||
if (dfs_file_lseek(fd, 0) >= 0)
|
||||
d->num = d->cur = 0;
|
||||
fd_put(fd);
|
||||
}
|
||||
RTM_EXPORT(rewinddir);
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will close a directory
|
||||
* stream.
|
||||
*
|
||||
* @param d the directory stream.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int closedir(DIR *d)
|
||||
{
|
||||
int result;
|
||||
struct dfs_fd *fd;
|
||||
|
||||
fd = fd_get(d->fd);
|
||||
if (fd == NULL)
|
||||
{
|
||||
rt_set_errno(-EBADF);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = dfs_file_close(fd);
|
||||
fd_put(fd);
|
||||
|
||||
fd_put(fd);
|
||||
rt_free(d);
|
||||
|
||||
if (result < 0)
|
||||
{
|
||||
rt_set_errno(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
RTM_EXPORT(closedir);
|
||||
|
||||
#ifdef DFS_USING_WORKDIR
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will change working
|
||||
* directory.
|
||||
*
|
||||
* @param path the path name to be changed to.
|
||||
*
|
||||
* @return 0 on successful, -1 on failed.
|
||||
*/
|
||||
int chdir(const char *path)
|
||||
{
|
||||
char *fullpath;
|
||||
DIR *d;
|
||||
|
||||
if (path == NULL)
|
||||
{
|
||||
dfs_lock();
|
||||
rt_kprintf("%s\n", working_directory);
|
||||
dfs_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strlen(path) > DFS_PATH_MAX)
|
||||
{
|
||||
rt_set_errno(-ENOTDIR);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
fullpath = dfs_normalize_path(NULL, path);
|
||||
if (fullpath == NULL)
|
||||
{
|
||||
rt_set_errno(-ENOTDIR);
|
||||
|
||||
return -1; /* build path failed */
|
||||
}
|
||||
|
||||
dfs_lock();
|
||||
d = opendir(fullpath);
|
||||
if (d == NULL)
|
||||
{
|
||||
rt_free(fullpath);
|
||||
/* this is a not exist directory */
|
||||
dfs_unlock();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* close directory stream */
|
||||
closedir(d);
|
||||
|
||||
/* copy full path to working directory */
|
||||
strncpy(working_directory, fullpath, DFS_PATH_MAX);
|
||||
/* release normalize directory path name */
|
||||
rt_free(fullpath);
|
||||
|
||||
dfs_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
RTM_EXPORT(chdir);
|
||||
|
||||
#ifdef RT_USING_FINSH
|
||||
FINSH_FUNCTION_EXPORT_ALIAS(chdir, cd, change current working directory);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which shall check the file named
|
||||
* by the pathname pointed to by the path argument for accessibility according
|
||||
* to the bit pattern contained in amode.
|
||||
*
|
||||
* @param path the specified file/dir path.
|
||||
* @param amode the value is either the bitwise-inclusive OR of the access
|
||||
* permissions to be checked (R_OK, W_OK, X_OK) or the existence test (F_OK).
|
||||
*/
|
||||
int access(const char *path, int amode)
|
||||
{
|
||||
struct stat sb;
|
||||
if (stat(path, &sb) < 0)
|
||||
return -1; /* already sets errno */
|
||||
|
||||
/* ignore R_OK,W_OK,X_OK condition */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function is a POSIX compliant version, which will return current
|
||||
* working directory.
|
||||
*
|
||||
* @param buf the returned current directory.
|
||||
* @param size the buffer size.
|
||||
*
|
||||
* @return the returned current directory.
|
||||
*/
|
||||
char *getcwd(char *buf, size_t size)
|
||||
{
|
||||
#ifdef DFS_USING_WORKDIR
|
||||
dfs_lock();
|
||||
strncpy(buf, working_directory, size);
|
||||
dfs_unlock();
|
||||
#else
|
||||
rt_kprintf(NO_WORKING_DIR);
|
||||
#endif
|
||||
|
||||
return buf;
|
||||
}
|
||||
RTM_EXPORT(getcwd);
|
||||
Reference in New Issue
Block a user