/* * 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 #include #include #include "dfs_private.h" /* 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 struct dfs_fd fd_table[DFS_FD_MAX]; /** * @addtogroup DFS */ /*@{*/ /** * this function will initialize device file system. */ int dfs_init(void) { /* clear filesystem operations table */ memset((void *)filesystem_operation_table, 0, sizeof(filesystem_operation_table)); /* clear filesystem table */ memset(filesystem_table, 0, sizeof(filesystem_table)); /* clean fd table */ 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 */ 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 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; 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 */ for (idx = 0; idx < DFS_FD_MAX && fd_table[idx].ref_count > 0; idx++); /* can't find an empty fd entry */ if (idx == DFS_FD_MAX) { idx = -(1 + DFS_FD_OFFSET); goto __result; } d = &(fd_table[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; fd = fd - DFS_FD_OFFSET; if (fd < 0 || fd >= DFS_FD_MAX) return NULL; dfs_lock(); d = &fd_table[fd]; /* check dfs_fd valid or not */ if (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) { 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(NULL, pathname); if (fullpath != NULL) { char *mountpath; fs = dfs_filesystem_lookup(fullpath); if (fs == NULL) { /* can't find mounted file system */ 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->fops == NULL) continue; if (fd->fops == fs->ops->fops && 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 = 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'; return fullpath; } RTM_EXPORT(dfs_normalize_path); #ifdef RT_USING_FINSH #include int list_fd(void) { int index; rt_enter_critical(); for (index = 0; index < DFS_FD_MAX; index ++) { struct dfs_fd *fd = &(fd_table[index]); if (fd->fops) { rt_kprintf("--fd: %d--", index); if (fd->type == FT_DIRECTORY) rt_kprintf("[dir]\n"); if (fd->type == FT_REGULAR) rt_kprintf("[file]\n"); if (fd->type == FT_SOCKET) rt_kprintf("[socket]\n"); if (fd->type == FT_USER) rt_kprintf("[user]\n"); rt_kprintf("refcount=%d\n", fd->ref_count); rt_kprintf("magic=0x%04x\n", fd->magic); if (fd->path) { rt_kprintf("path: %s\n", fd->path); } } } rt_exit_critical(); return 0; } MSH_CMD_EXPORT(list_fd, list file descriptor); #endif /*@}*/