/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id: dir.c 13741 2007-06-30 02:08:27Z jethead71 $ * * 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. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include #include #include #include #include #include "fat.h" #include "dir.h" #include "debug.h" #include "filefuncs.h" #if (MEMORYSIZE > 8) #define MAX_OPEN_DIRS 12 #else #define MAX_OPEN_DIRS 8 #endif static DIR_UNCACHED opendirs[MAX_OPEN_DIRS]; // 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) #else (void)volume; #endif { pdir->busy = false; /* mark as available, no further action */ closed++; } } return closed; /* return how many we did */ } 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 strlcpy(namecopy, name, sizeof(namecopy)); /* just copy */ #endif if ( fat_opendir(IF_MV(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)) ) { /* In reality, the parent_dir parameter of fat_opendir seems * useless because it's sole purpose it to have a way to * update the file metadata, but here we are only reading * a directory so there's no need for that kind of stuff. * However, the rmdir_uncached function uses a ugly hack to * avoid opening a directory twice when deleting it and thus * needs those information. That's why we pass pdir->fatdir both * as the parent directory and the resulting one (this is safe, * in doubt, check fat_open(dir) code) which will allow this kind of * (ugly) things */ if ( fat_opendir(IF_MV(volume,) &pdir->fatdir, entry.firstcluster, &pdir->fatdir) < 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->info.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; strlcpy(theent->d_name, entry.name, sizeof(theent->d_name)); theent->info.attribute = entry.attr; theent->info.wrtdate = entry.wrtdate; theent->info.wrttime = entry.wrttime; theent->info.size = entry.filesize; theent->startcluster = entry.firstcluster; 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; int dd; DIR_UNCACHED* pdir = opendirs; struct fat_dir *newdir; int rc; if ( name[0] != '/' ) { DEBUGF("mkdir: Only absolute paths supported right now\n"); return -1; } /* find a free dir descriptor */ for ( dd=0; ddbusy ) break; if ( dd == MAX_OPEN_DIRS ) { DEBUGF("Too many dirs open\n"); errno = EMFILE; return -5; } pdir->busy = true; newdir = &pdir->fatdir; strlcpy(namecopy, name, sizeof(namecopy)); /* 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"); pdir->busy = false; return -2; } if(basename[0] == 0) { DEBUGF("mkdir: Empty dir name\n"); pdir->busy = false; 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); pdir->busy = false; return - 4; } } memset(newdir, 0, sizeof(struct fat_dir)); rc = fat_create_dir(basename, newdir, &(dir->fatdir)); closedir_uncached(dir); pdir->busy = false; 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; }