summaryrefslogtreecommitdiff
path: root/fs/fat/rockbox_dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fat/rockbox_dir.c')
-rw-r--r--fs/fat/rockbox_dir.c329
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;
+}