From 011a325e32c05f6e4817dcdc555615e6b7b6c102 Mon Sep 17 00:00:00 2001 From: Kevin Ferrare Date: Fri, 20 Jul 2007 17:06:55 +0000 Subject: Makes apps and plugins interract with directories using a posix-like api instead of calling dircache / simulator functions (no additionnal layer added, only a cosmetic change) git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13943 a1c6a512-1295-4272-9138-f99709370657 --- firmware/common/dir_uncached.c | 322 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 firmware/common/dir_uncached.c (limited to 'firmware/common/dir_uncached.c') diff --git a/firmware/common/dir_uncached.c b/firmware/common/dir_uncached.c new file mode 100644 index 0000000000..e6b59c30ec --- /dev/null +++ b/firmware/common/dir_uncached.c @@ -0,0 +1,322 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: dir.c 13741 2007-06-30 02:08:27Z jethead71 $ + * + * Copyright (C) 2002 by Björn Stenberg + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include +#include +#include "fat.h" +#include "dir.h" +#include "debug.h" +#include "atoi.h" +//#include "dircache.h" + +#define MAX_OPEN_DIRS 8 + +static DIR_UNCACHED opendirs[MAX_OPEN_DIRS]; + +#ifdef HAVE_MULTIVOLUME + +/* how to name volumes, first char must be outside of legal file names, + a number gets appended to enumerate, if applicable */ +#ifdef HAVE_MMC +static const char* vol_names = ""; +#define VOL_ENUM_POS 4 /* position of %d, to avoid runtime calculation */ +#elif defined(HAVE_HOTSWAP) +static const char* vol_names = ""; +#define VOL_ENUM_POS 8 /* position of %d, to avoid runtime calculation */ +#else +static const char* vol_names = ""; +#define VOL_ENUM_POS 3 +#endif + +/* returns on which volume this is, and copies the reduced name + (sortof a preprocessor for volume-decorated pathnames) */ +static 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; ddfatdir.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; ddbusy ) + break; + + if ( dd == 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, 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(theent->d_name, 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, entry->d_name) ) { + DEBUGF("mkdir error: file exists\n"); + errno = EEXIST; + closedir_uncached(dir); + return - 4; + } + } + + memset(&newdir, sizeof(struct fat_dir), 0); + + 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(entry->d_name, ".") && + strcmp(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; +} -- cgit v1.2.3