Oops, missed that when changing the default proxy to system ...
[Rockbox.git] / firmware / common / dir_uncached.c
blobe6b59c30ece47a188636e8b712fbc6d2580097ac
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: dir.c 13741 2007-06-30 02:08:27Z jethead71 $
10 * Copyright (C) 2002 by Björn Stenberg
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include <stdio.h>
20 #include <errno.h>
21 #include <string.h>
22 #include <stdbool.h>
23 #include "fat.h"
24 #include "dir.h"
25 #include "debug.h"
26 #include "atoi.h"
27 //#include "dircache.h"
29 #define MAX_OPEN_DIRS 8
31 static DIR_UNCACHED opendirs[MAX_OPEN_DIRS];
33 #ifdef HAVE_MULTIVOLUME
35 /* how to name volumes, first char must be outside of legal file names,
36 a number gets appended to enumerate, if applicable */
37 #ifdef HAVE_MMC
38 static const char* vol_names = "<MMC%d>";
39 #define VOL_ENUM_POS 4 /* position of %d, to avoid runtime calculation */
40 #elif defined(HAVE_HOTSWAP)
41 static const char* vol_names = "<microSD%d>";
42 #define VOL_ENUM_POS 8 /* position of %d, to avoid runtime calculation */
43 #else
44 static const char* vol_names = "<HD%d>";
45 #define VOL_ENUM_POS 3
46 #endif
48 /* returns on which volume this is, and copies the reduced name
49 (sortof a preprocessor for volume-decorated pathnames) */
50 static int strip_volume(const char* name, char* namecopy)
52 int volume = 0;
53 const char *temp = name;
55 while (*temp == '/') /* skip all leading slashes */
56 ++temp;
58 if (*temp && !strncmp(temp, vol_names, VOL_ENUM_POS))
60 temp += VOL_ENUM_POS; /* behind special name */
61 volume = atoi(temp); /* number is following */
62 temp = strchr(temp, '/'); /* search for slash behind */
63 if (temp != NULL)
64 name = temp; /* use the part behind the volume */
65 else
66 name = "/"; /* else this must be the root dir */
69 strncpy(namecopy, name, MAX_PATH);
70 namecopy[MAX_PATH-1] = '\0';
72 return volume;
74 #endif /* #ifdef HAVE_MULTIVOLUME */
77 #ifdef HAVE_HOTSWAP
78 // release all dir handles on a given volume "by force", to avoid leaks
79 int release_dirs(int volume)
81 DIR_UNCACHED* pdir = opendirs;
82 int dd;
83 int closed = 0;
84 for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
86 if (pdir->fatdir.file.volume == volume)
88 pdir->busy = false; /* mark as available, no further action */
89 closed++;
92 return closed; /* return how many we did */
94 #endif /* #ifdef HAVE_HOTSWAP */
96 DIR_UNCACHED* opendir_uncached(const char* name)
98 char namecopy[MAX_PATH];
99 char* part;
100 char* end;
101 struct fat_direntry entry;
102 int dd;
103 DIR_UNCACHED* pdir = opendirs;
104 #ifdef HAVE_MULTIVOLUME
105 int volume;
106 #endif
108 if ( name[0] != '/' ) {
109 DEBUGF("Only absolute paths supported right now\n");
110 return NULL;
113 /* find a free dir descriptor */
114 for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
115 if ( !pdir->busy )
116 break;
118 if ( dd == MAX_OPEN_DIRS ) {
119 DEBUGF("Too many dirs open\n");
120 errno = EMFILE;
121 return NULL;
124 pdir->busy = true;
126 #ifdef HAVE_MULTIVOLUME
127 /* try to extract a heading volume name, if present */
128 volume = strip_volume(name, namecopy);
129 pdir->volumecounter = 0;
130 #else
131 strncpy(namecopy,name,sizeof(namecopy)); /* just copy */
132 namecopy[sizeof(namecopy)-1] = '\0';
133 #endif
135 if ( fat_opendir(IF_MV2(volume,) &pdir->fatdir, 0, NULL) < 0 ) {
136 DEBUGF("Failed opening root dir\n");
137 pdir->busy = false;
138 return NULL;
141 for ( part = strtok_r(namecopy, "/", &end); part;
142 part = strtok_r(NULL, "/", &end)) {
143 /* scan dir for name */
144 while (1) {
145 if ((fat_getnext(&pdir->fatdir,&entry) < 0) ||
146 (!entry.name[0])) {
147 pdir->busy = false;
148 return NULL;
150 if ( (entry.attr & FAT_ATTR_DIRECTORY) &&
151 (!strcasecmp(part, entry.name)) ) {
152 pdir->parent_dir = pdir->fatdir;
153 if ( fat_opendir(IF_MV2(volume,)
154 &pdir->fatdir,
155 entry.firstcluster,
156 &pdir->parent_dir) < 0 ) {
157 DEBUGF("Failed opening dir '%s' (%ld)\n",
158 part, entry.firstcluster);
159 pdir->busy = false;
160 return NULL;
162 #ifdef HAVE_MULTIVOLUME
163 pdir->volumecounter = -1; /* n.a. to subdirs */
164 #endif
165 break;
170 return pdir;
173 int closedir_uncached(DIR_UNCACHED* dir)
175 dir->busy=false;
176 return 0;
179 struct dirent_uncached* readdir_uncached(DIR_UNCACHED* dir)
181 struct fat_direntry entry;
182 struct dirent_uncached* theent = &(dir->theent);
184 if (!dir->busy)
185 return NULL;
187 #ifdef HAVE_MULTIVOLUME
188 /* Volumes (secondary file systems) get inserted into the root directory
189 of the first volume, since we have no separate top level. */
190 if (dir->volumecounter >= 0 /* on a root dir */
191 && dir->volumecounter < NUM_VOLUMES /* in range */
192 && dir->fatdir.file.volume == 0) /* at volume 0 */
193 { /* fake special directories, which don't really exist, but
194 will get redirected upon opendir_uncached() */
195 while (++dir->volumecounter < NUM_VOLUMES)
197 if (fat_ismounted(dir->volumecounter))
199 memset(theent, 0, sizeof(*theent));
200 theent->attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
201 snprintf(theent->d_name, sizeof(theent->d_name),
202 vol_names, dir->volumecounter);
203 return theent;
207 #endif
208 /* normal directory entry fetching follows here */
209 if (fat_getnext(&(dir->fatdir),&entry) < 0)
210 return NULL;
212 if ( !entry.name[0] )
213 return NULL;
215 strncpy(theent->d_name, entry.name, sizeof( theent->d_name ) );
216 theent->attribute = entry.attr;
217 theent->size = entry.filesize;
218 theent->startcluster = entry.firstcluster;
219 theent->wrtdate = entry.wrtdate;
220 theent->wrttime = entry.wrttime;
222 return theent;
225 int mkdir_uncached(const char *name)
227 DIR_UNCACHED *dir;
228 char namecopy[MAX_PATH];
229 char* end;
230 char *basename;
231 char *parent;
232 struct dirent_uncached *entry;
233 struct fat_dir newdir;
234 int rc;
236 if ( name[0] != '/' ) {
237 DEBUGF("mkdir: Only absolute paths supported right now\n");
238 return -1;
241 strncpy(namecopy,name,sizeof(namecopy));
242 namecopy[sizeof(namecopy)-1] = 0;
244 /* Split the base name and the path */
245 end = strrchr(namecopy, '/');
246 *end = 0;
247 basename = end+1;
249 if(namecopy == end) /* Root dir? */
250 parent = "/";
251 else
252 parent = namecopy;
254 DEBUGF("mkdir: parent: %s, name: %s\n", parent, basename);
256 dir = opendir_uncached(parent);
258 if(!dir) {
259 DEBUGF("mkdir: can't open parent dir\n");
260 return -2;
263 if(basename[0] == 0) {
264 DEBUGF("mkdir: Empty dir name\n");
265 errno = EINVAL;
266 return -3;
269 /* Now check if the name already exists */
270 while ((entry = readdir_uncached(dir))) {
271 if ( !strcasecmp(basename, entry->d_name) ) {
272 DEBUGF("mkdir error: file exists\n");
273 errno = EEXIST;
274 closedir_uncached(dir);
275 return - 4;
279 memset(&newdir, sizeof(struct fat_dir), 0);
281 rc = fat_create_dir(basename, &newdir, &(dir->fatdir));
282 closedir_uncached(dir);
284 return rc;
287 int rmdir_uncached(const char* name)
289 int rc;
290 DIR_UNCACHED* dir;
291 struct dirent_uncached* entry;
293 dir = opendir_uncached(name);
294 if (!dir)
296 errno = ENOENT; /* open error */
297 return -1;
300 /* check if the directory is empty */
301 while ((entry = readdir_uncached(dir)))
303 if (strcmp(entry->d_name, ".") &&
304 strcmp(entry->d_name, ".."))
306 DEBUGF("rmdir error: not empty\n");
307 errno = ENOTEMPTY;
308 closedir_uncached(dir);
309 return -2;
313 rc = fat_remove(&(dir->fatdir.file));
314 if ( rc < 0 ) {
315 DEBUGF("Failed removing dir: %d\n", rc);
316 errno = EIO;
317 rc = rc * 10 - 3;
320 closedir_uncached(dir);
321 return rc;