diff options
author | Michael Brandt <michael.brandt@stericsson.com> | 2010-07-20 15:08:22 +0200 |
---|---|---|
committer | Michael BRANDT <michael.brandt@stericsson.com> | 2010-08-30 20:03:48 +0200 |
commit | 20503b61a7f73fad8ee97219f7a1e74de3a8a2ac (patch) | |
tree | ec58c1f830639a45462fe07219304a06a16b7b54 /fs/fat/rockbox_dir.c | |
parent | 3a5b27b58258ff61dfae3167bc46fed0146ddffd (diff) |
Read/write VFAT support from Rockbox-3.3 FAT stack
Add read/write VFAT support from Rockbox-3.3 FAT stack.
It should be also applicable to the unmodified 2009.11 U-Boot release.
Note that is was taken as is from Rockbox and from a older U-Boot
Rockbox patch. "checkpatch" shows very many coding style errors and
warnings, but it is tedious work to clean this up.
To make this patch work an additional mmc_block_write() board support
routine and the errno variable are needed.
Furthermore following defines in the board config header file:
#define CONFIG_ROCKBOX_FAT 1
#define CONFIG_U_BOOT 1
#define CONFIG_SUPPORT_VFAT 1
#define CONFIG_CMD_TREE_FAT
This will be added in a follow-up patch.
This patch is based on the patch from Etienne Carriere
<etienne.carriere@stericsson.com> for the U671x U-Boot:
This commit adds FAT write support to u-boot native read-only FAT code.
Commit initially applied on u-boot-v2009.01
(SHA1: 72d15e705bc3983884105cb7755c7ba80e74a0a5)
Based on FAT stack dumped from Rockbox package v3.1 (www.rockbox.org).
Based on initial Rockbox FAT stack integration in u-boot by Keith Outwater
(outwater@comcast.net).
Current porting is aligned with Rockbox v3.3 FAT stack.
Enable upon config switches:
CONFIG_CMD_FAT
CONFIG_ROCKBOX_FAT
CONFIG_CMD_TREE_FAT (recommended)
CONFIG_SUPPORT_VFAT (recommended)
C code APIs (from U-boot native FAT support):
int fat_register_device(block_dev_desc_t *dev_desc, int part_no);
long file_fat_read(const char *path, void *buf, unsigned long maxsize);
int file_fat_ls(const char *dirname);
int file_fat_detectfs(void);
C code APIs (added by Rockbox FAT support):
long file_fat_write(const char *path, void *buf, unsigned long maxsize);
int file_fat_rm(const char *path);
int file_fat_rmdir(const char *path);
int file_fat_mkdir(const char *path);
int file_fat_cd(const char *path);
int file_fat_pwd(void);
int file_fat_mv(const char *oldpath, const char *newpath);
unsigned int rockbox_fat_free(unsigned long size_kbyte);
unsigned int rockbox_fat_size(void);
Use "help fat" from u-boot console to see available commands.
ST-Ericsson ID: WP264488
Change-Id: I9afc29ecb80f9152bd8534bbf11e47e54cfad796
Signed-off-by: Michael Brandt <michael.brandt@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/3009
Diffstat (limited to 'fs/fat/rockbox_dir.c')
-rw-r--r-- | fs/fat/rockbox_dir.c | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/fs/fat/rockbox_dir.c b/fs/fat/rockbox_dir.c new file mode 100644 index 000000000..24c0ac2cc --- /dev/null +++ b/fs/fat/rockbox_dir.c @@ -0,0 +1,329 @@ +/*************************************************************************** + * Copyright (C) 2002 by Björn Stenberg + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + **************************************************************************** + * Source file dumped from rockbox-3.1 distribution. + * $Id: dir.c 13741 2007-06-30 02:08:27Z jethead71 $ + * Copyright (C) 2002 by Björn Stenberg + **************************************************************************** + * See file CREDITS for list of people who contributed to the U-boot + * project. + * + * 01/17/2006 Keith Outwater (outwater4@comcast.net) - port to U-Boot using + * CVS version 1.29 of 'firmware/common/dir.c' from rockbox CVS server. + * 01/07/2009 etienne.carriere@stnwireless.com - update u-boot port from rockbox-3.1 + **************************************************************************** + */ +#include <common.h> +#include <config.h> + +#include <asm/errno.h> +#include <linux/string.h> +#include <fat.h> +#include "rockbox_debug.h" + +extern int errno; /* see board/<board>/<board>.c */ +extern int strcasecmp(const char *s1, const char *s2); /* from rockbox_wrapper.c */ + +#ifndef __HAVE_ARCH_STRNICMP +extern int strnicmp(const char *s1, const char *s2, size_t len); +#endif + +#if !defined(CONFIG_ROCKBOX_FAT_MAX_OPEN_DIRS) +#define CONFIG_ROCKBOX_FAT_MAX_OPEN_DIRS 8 +#endif + +static DIR_UNCACHED opendirs[CONFIG_ROCKBOX_FAT_MAX_OPEN_DIRS]; + +#define DEBUGF fat_dprintf /* map debug on old fat_dprintf from rockbox_debug.h */ + +#ifdef HAVE_MULTIVOLUME +/* @brief returns on which volume this is, and copies the reduced name + (sortof a preprocessor for volume-decorated pathnames) +*/ +int strip_volume(const char* name, char* namecopy) +{ + int volume = 0; + const char *temp = name; + + while (*temp == '/') /* skip all leading slashes */ + ++temp; + + if (*temp && !strncmp(temp, VOL_NAMES, VOL_ENUM_POS)) + { + temp += VOL_ENUM_POS; /* behind special name */ + volume = atoi(temp); /* number is following */ + temp = strchr(temp, '/'); /* search for slash behind */ + if (temp != NULL) + name = temp; /* use the part behind the volume */ + else + name = "/"; /* else this must be the root dir */ + } + + strncpy(namecopy, name, MAX_PATH); + namecopy[MAX_PATH-1] = '\0'; + + return volume; +} +#endif /* #ifdef HAVE_MULTIVOLUME */ + + +#ifdef HAVE_HOTSWAP +// release all dir handles on a given volume "by force", to avoid leaks +int release_dirs(int volume) +{ + DIR_UNCACHED* pdir = opendirs; + int dd; + int closed = 0; + for ( dd=0; dd<CONFIG_ROCKBOX_FAT_MAX_OPEN_DIRS; dd++, pdir++) + { + if (pdir->fatdir.file.volume == volume) + { + pdir->busy = false; /* mark as available, no further action */ + closed++; + } + } + return closed; /* return how many we did */ +} +#endif /* #ifdef HAVE_HOTSWAP */ + +DIR_UNCACHED* opendir_uncached(const char* name) +{ + char namecopy[MAX_PATH]; + char *part; + char* end; + struct fat_direntry entry; + int dd; + DIR_UNCACHED* pdir = opendirs; +#ifdef HAVE_MULTIVOLUME + int volume; +#endif + + if (name[0] != '/') { + DEBUGF("Only absolute paths supported right now\n"); + return NULL; + } + + /* find a free dir descriptor */ + for ( dd=0; dd<CONFIG_ROCKBOX_FAT_MAX_OPEN_DIRS; dd++, pdir++) + if (!pdir->busy) + break; + + if (dd == CONFIG_ROCKBOX_FAT_MAX_OPEN_DIRS) { + DEBUGF("Too many dirs open\n"); + errno = EMFILE; + return NULL; + } + + pdir->busy = true; + +#ifdef HAVE_MULTIVOLUME + /* try to extract a heading volume name, if present */ + volume = strip_volume(name, namecopy); + pdir->volumecounter = 0; +#else + strncpy(namecopy, name, sizeof (namecopy)); /* just copy */ + namecopy[sizeof (namecopy) - 1] = '\0'; +#endif + + if ( fat_opendir(IF_MV2(volume,) &pdir->fatdir, 0, NULL) < 0 ) { + DEBUGF("Failed opening root dir\n"); + pdir->busy = false; + return NULL; + } + + for ( part = strtok_r(namecopy, "/", &end); part; + part = strtok_r(NULL, "/", &end)) { + /* scan dir for name */ + while (1) { + if ((fat_getnext(&pdir->fatdir, &entry) < 0) || + (!entry.name[0])) { + pdir->busy = false; + return NULL; + } + if ((entry.attr & FAT_ATTR_DIRECTORY) && + (!strcasecmp(part, (char*)entry.name)) ) { + pdir->parent_dir = pdir->fatdir; + if ( fat_opendir(IF_MV2(volume,) + &pdir->fatdir, + entry.firstcluster, + &pdir->parent_dir) < 0) { + DEBUGF("Failed opening dir '%s' (%ld)\n", + part, entry.firstcluster); + pdir->busy = false; + return NULL; + } +#ifdef HAVE_MULTIVOLUME + pdir->volumecounter = -1; /* n.a. to subdirs */ +#endif + break; + } + } + } + + return pdir; +} + +int closedir_uncached(DIR_UNCACHED* dir) +{ + dir->busy = false; + return 0; +} + +struct dirent_uncached* readdir_uncached(DIR_UNCACHED* dir) +{ + struct fat_direntry entry; + struct dirent_uncached* theent = &(dir->theent); + + if (!dir->busy) + return NULL; + +#ifdef HAVE_MULTIVOLUME + /* Volumes (secondary file systems) get inserted into the root directory + of the first volume, since we have no separate top level. */ + if (dir->volumecounter >= 0 /* on a root dir */ + && dir->volumecounter < NUM_VOLUMES /* in range */ + && dir->fatdir.file.volume == 0) /* at volume 0 */ + { /* fake special directories, which don't really exist, but + will get redirected upon opendir_uncached() */ + while (++dir->volumecounter < NUM_VOLUMES) + { + if (fat_ismounted(dir->volumecounter)) + { + memset(theent, 0, sizeof(*theent)); + theent->attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME; + snprintf(theent->d_name, sizeof(theent->d_name), + VOL_NAMES, dir->volumecounter); + return theent; + } + } + } +#endif + /* normal directory entry fetching follows here */ + if (fat_getnext(&(dir->fatdir), &entry) < 0) + return NULL; + + if (!entry.name[0]) + return NULL; + + strncpy((char*)theent->d_name, (char*)entry.name, sizeof(theent->d_name)); + theent->attribute = entry.attr; + theent->size = entry.filesize; + theent->startcluster = entry.firstcluster; + theent->wrtdate = entry.wrtdate; + theent->wrttime = entry.wrttime; + + return theent; +} + +int mkdir_uncached(const char *name) +{ + DIR_UNCACHED *dir; + char namecopy[MAX_PATH]; + char *end; + char *basename; + char *parent; + struct dirent_uncached *entry; + struct fat_dir newdir; + int rc; + + if (name[0] != '/') { + DEBUGF("mkdir: Only absolute paths supported right now\n"); + return -1; + } + + strncpy(namecopy, name, sizeof (namecopy)); + namecopy[sizeof (namecopy) - 1] = 0; + + /* Split the base name and the path */ + end = strrchr(namecopy, '/'); + *end = 0; + basename = end + 1; + + if (namecopy == end) /* Root dir? */ + parent = "/"; + else + parent = namecopy; + + DEBUGF("mkdir: parent: %s, name: %s\n", parent, basename); + + dir = opendir_uncached(parent); + + if (!dir) { + DEBUGF("mkdir: can't open parent dir\n"); + return -2; + } + + if (basename[0] == 0) { + DEBUGF("mkdir: Empty dir name\n"); + errno = EINVAL; + return -3; + } + + /* Now check if the name already exists */ + while ((entry = readdir_uncached(dir))) { + if ( !strcasecmp(basename, (char*)entry->d_name) ) { + DEBUGF("mkdir error: file exists\n"); + errno = EEXIST; + closedir_uncached(dir); + return -4; + } + } + + memset(&newdir, 0, sizeof (struct fat_dir)); + + rc = fat_create_dir(basename, &newdir, &(dir->fatdir)); + closedir_uncached(dir); + + return rc; +} + +int rmdir_uncached(const char* name) +{ + int rc; + DIR_UNCACHED* dir; + struct dirent_uncached* entry; + + dir = opendir_uncached(name); + if (!dir) + { + errno = ENOENT; /* open error */ + return -1; + } + + /* check if the directory is empty */ + while ((entry = readdir_uncached(dir))) + { + if (strcmp((char*)entry->d_name, ".") && + strcmp((char*)entry->d_name, "..")) + { + DEBUGF("rmdir error: not empty\n"); + errno = ENOTEMPTY; + closedir_uncached(dir); + return -2; + } + } + + rc = fat_remove(&(dir->fatdir.file)); + if (rc < 0) { + DEBUGF("Failed removing dir: %d\n", rc); + errno = EIO; + rc = rc * 10 - 3; + } + + closedir_uncached(dir); + return rc; +} |