411 lines
8.9 KiB
C
411 lines
8.9 KiB
C
/*
|
|
* File : dfs.c
|
|
* This file is part of Device File System in RT-Thread RTOS
|
|
* COPYRIGHT (C) 2004-2012, RT-Thread Development Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2005-02-22 Bernard The first version.
|
|
*/
|
|
|
|
#include <dfs.h>
|
|
#include <dfs_fs.h>
|
|
#include <dfs_file.h>
|
|
|
|
/* Global variables */
|
|
const struct dfs_filesystem_operation *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
|
|
|
|
#ifdef DFS_USING_STDIO
|
|
struct dfs_fd fd_table[3 + DFS_FD_MAX];
|
|
#else
|
|
struct dfs_fd fd_table[DFS_FD_MAX];
|
|
#endif
|
|
|
|
/**
|
|
* @addtogroup DFS
|
|
*/
|
|
|
|
/*@{*/
|
|
|
|
/**
|
|
* this function will initialize device file system.
|
|
*/
|
|
int dfs_init(void)
|
|
{
|
|
/* 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(fd_table, 0, sizeof(fd_table));
|
|
|
|
/* create device filesystem lock */
|
|
rt_mutex_init(&fslock, "fslock", RT_IPC_FLAG_FIFO);
|
|
|
|
#ifdef DFS_USING_WORKDIR
|
|
/* set current working directory */
|
|
rt_memset(working_directory, 0, sizeof(working_directory));
|
|
working_directory[0] = '/';
|
|
#endif
|
|
return 0;
|
|
}
|
|
INIT_COMPONENT_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;
|
|
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
|
|
/* lock filesystem */
|
|
dfs_lock();
|
|
|
|
/* find an empty fd entry */
|
|
#ifdef DFS_USING_STDIO
|
|
for (idx = 3; idx < DFS_FD_MAX + 3 && fd_table[idx].ref_count > 0; idx++);
|
|
#else
|
|
for (idx = 0; idx < DFS_FD_MAX && fd_table[idx].ref_count > 0; idx++);
|
|
#endif
|
|
|
|
/* can't find an empty fd entry */
|
|
#ifdef DFS_USING_STDIO
|
|
if (idx == DFS_FD_MAX + 3)
|
|
#else
|
|
if (idx == DFS_FD_MAX)
|
|
#endif
|
|
{
|
|
idx = -1;
|
|
goto __result;
|
|
}
|
|
|
|
d = &(fd_table[idx]);
|
|
d->ref_count = 1;
|
|
d->magic = DFS_FD_MAGIC;
|
|
|
|
__result:
|
|
dfs_unlock();
|
|
return idx;
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
|
|
#ifdef DFS_USING_STDIO
|
|
if (fd < 3 || fd >= DFS_FD_MAX + 3)
|
|
return RT_NULL;
|
|
#else
|
|
if (fd < 0 || fd >= DFS_FD_MAX)
|
|
return RT_NULL;
|
|
#endif
|
|
|
|
dfs_lock();
|
|
d = &fd_table[fd];
|
|
|
|
/* check dfs_fd valid or not */
|
|
if (d->magic != DFS_FD_MAGIC)
|
|
{
|
|
dfs_unlock();
|
|
return RT_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 != RT_NULL);
|
|
|
|
dfs_lock();
|
|
fd->ref_count --;
|
|
|
|
/* clear this fd entry */
|
|
if (fd->ref_count == 0)
|
|
{
|
|
rt_memset(fd, 0, sizeof(struct dfs_fd));
|
|
}
|
|
dfs_unlock();
|
|
};
|
|
|
|
/**
|
|
* @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;
|
|
|
|
fullpath = dfs_normalize_path(RT_NULL, pathname);
|
|
if (fullpath != RT_NULL)
|
|
{
|
|
char *mountpath;
|
|
fs = dfs_filesystem_lookup(fullpath);
|
|
if (fs == RT_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 < DFS_FD_MAX; index++)
|
|
{
|
|
fd = &(fd_table[index]);
|
|
if (fd->fs == RT_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 RT_NULL;
|
|
|
|
dir = filename + strlen(directory);
|
|
if ((*dir != '/') && (dir != filename))
|
|
{
|
|
dir --;
|
|
}
|
|
|
|
return dir;
|
|
}
|
|
|
|
/**
|
|
* 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 != RT_NULL);
|
|
|
|
#ifdef DFS_USING_WORKDIR
|
|
if (directory == RT_NULL) /* shall use working directory */
|
|
directory = &working_directory[0];
|
|
#else
|
|
if ((directory == RT_NULL) && (filename[0] != '/'))
|
|
{
|
|
rt_kprintf(NO_WORKING_DIR);
|
|
|
|
return RT_NULL;
|
|
}
|
|
#endif
|
|
|
|
if (filename[0] != '/') /* it's a absolute path, use it directly */
|
|
{
|
|
fullpath = rt_malloc(strlen(directory) + strlen(filename) + 2);
|
|
|
|
if (fullpath == RT_NULL)
|
|
return RT_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 == RT_NULL)
|
|
return RT_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 RT_NULL;
|
|
}
|
|
while (dst0 < dst && dst[-1] != '/')
|
|
dst --;
|
|
}
|
|
|
|
*dst = '\0';
|
|
|
|
/* remove '/' in the end of path if exist */
|
|
dst --;
|
|
if ((dst != fullpath) && (*dst == '/'))
|
|
*dst = '\0';
|
|
|
|
return fullpath;
|
|
}
|
|
/*@}*/
|
|
|