Fix red: broken tools/addtargetdir.pl
[kugel-rb.git] / firmware / common / dir_uncached.c
blobc6af1459301d7e4a21b14dea2024e415aad2fc09
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 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include <stdio.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include "fat.h"
27 #include "dir.h"
28 #include "debug.h"
30 #if ((defined(MEMORYSIZE) && (MEMORYSIZE > 8)) || MEM > 8)
31 #define MAX_OPEN_DIRS 12
32 #else
33 #define MAX_OPEN_DIRS 8
34 #endif
36 static DIR_UNCACHED opendirs[MAX_OPEN_DIRS];
38 #ifdef HAVE_MULTIVOLUME
40 /* returns on which volume this is, and copies the reduced name
41 (sortof a preprocessor for volume-decorated pathnames) */
42 int strip_volume(const char* name, char* namecopy)
44 int volume = 0;
45 const char *temp = name;
47 while (*temp == '/') /* skip all leading slashes */
48 ++temp;
50 if (*temp && !strncmp(temp, VOL_NAMES, VOL_ENUM_POS))
52 temp += VOL_ENUM_POS; /* behind special name */
53 volume = atoi(temp); /* number is following */
54 temp = strchr(temp, '/'); /* search for slash behind */
55 if (temp != NULL)
56 name = temp; /* use the part behind the volume */
57 else
58 name = "/"; /* else this must be the root dir */
61 strncpy(namecopy, name, MAX_PATH);
62 namecopy[MAX_PATH-1] = '\0';
64 return volume;
66 #endif /* #ifdef HAVE_MULTIVOLUME */
69 #ifdef HAVE_HOTSWAP
70 // release all dir handles on a given volume "by force", to avoid leaks
71 int release_dirs(int volume)
73 DIR_UNCACHED* pdir = opendirs;
74 int dd;
75 int closed = 0;
76 for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
78 if (pdir->fatdir.file.volume == volume)
80 pdir->busy = false; /* mark as available, no further action */
81 closed++;
84 return closed; /* return how many we did */
86 #endif /* #ifdef HAVE_HOTSWAP */
88 DIR_UNCACHED* opendir_uncached(const char* name)
90 char namecopy[MAX_PATH];
91 char* part;
92 char* end;
93 struct fat_direntry entry;
94 int dd;
95 DIR_UNCACHED* pdir = opendirs;
96 #ifdef HAVE_MULTIVOLUME
97 int volume;
98 #endif
100 if ( name[0] != '/' ) {
101 DEBUGF("Only absolute paths supported right now\n");
102 return NULL;
105 /* find a free dir descriptor */
106 for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
107 if ( !pdir->busy )
108 break;
110 if ( dd == MAX_OPEN_DIRS ) {
111 DEBUGF("Too many dirs open\n");
112 errno = EMFILE;
113 return NULL;
116 pdir->busy = true;
118 #ifdef HAVE_MULTIVOLUME
119 /* try to extract a heading volume name, if present */
120 volume = strip_volume(name, namecopy);
121 pdir->volumecounter = 0;
122 #else
123 strncpy(namecopy,name,sizeof(namecopy)); /* just copy */
124 namecopy[sizeof(namecopy)-1] = '\0';
125 #endif
127 if ( fat_opendir(IF_MV2(volume,) &pdir->fatdir, 0, NULL) < 0 ) {
128 DEBUGF("Failed opening root dir\n");
129 pdir->busy = false;
130 return NULL;
133 for ( part = strtok_r(namecopy, "/", &end); part;
134 part = strtok_r(NULL, "/", &end)) {
135 /* scan dir for name */
136 while (1) {
137 if ((fat_getnext(&pdir->fatdir,&entry) < 0) ||
138 (!entry.name[0])) {
139 pdir->busy = false;
140 return NULL;
142 if ( (entry.attr & FAT_ATTR_DIRECTORY) &&
143 (!strcasecmp(part, entry.name)) ) {
144 pdir->parent_dir = pdir->fatdir;
145 if ( fat_opendir(IF_MV2(volume,)
146 &pdir->fatdir,
147 entry.firstcluster,
148 &pdir->parent_dir) < 0 ) {
149 DEBUGF("Failed opening dir '%s' (%ld)\n",
150 part, entry.firstcluster);
151 pdir->busy = false;
152 return NULL;
154 #ifdef HAVE_MULTIVOLUME
155 pdir->volumecounter = -1; /* n.a. to subdirs */
156 #endif
157 break;
162 return pdir;
165 int closedir_uncached(DIR_UNCACHED* dir)
167 dir->busy=false;
168 return 0;
171 struct dirent_uncached* readdir_uncached(DIR_UNCACHED* dir)
173 struct fat_direntry entry;
174 struct dirent_uncached* theent = &(dir->theent);
176 if (!dir->busy)
177 return NULL;
179 #ifdef HAVE_MULTIVOLUME
180 /* Volumes (secondary file systems) get inserted into the root directory
181 of the first volume, since we have no separate top level. */
182 if (dir->volumecounter >= 0 /* on a root dir */
183 && dir->volumecounter < NUM_VOLUMES /* in range */
184 && dir->fatdir.file.volume == 0) /* at volume 0 */
185 { /* fake special directories, which don't really exist, but
186 will get redirected upon opendir_uncached() */
187 while (++dir->volumecounter < NUM_VOLUMES)
189 if (fat_ismounted(dir->volumecounter))
191 memset(theent, 0, sizeof(*theent));
192 theent->attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
193 snprintf(theent->d_name, sizeof(theent->d_name),
194 VOL_NAMES, dir->volumecounter);
195 return theent;
199 #endif
200 /* normal directory entry fetching follows here */
201 if (fat_getnext(&(dir->fatdir),&entry) < 0)
202 return NULL;
204 if ( !entry.name[0] )
205 return NULL;
207 strncpy(theent->d_name, entry.name, sizeof( theent->d_name ) );
208 theent->attribute = entry.attr;
209 theent->size = entry.filesize;
210 theent->startcluster = entry.firstcluster;
211 theent->wrtdate = entry.wrtdate;
212 theent->wrttime = entry.wrttime;
214 return theent;
217 int mkdir_uncached(const char *name)
219 DIR_UNCACHED *dir;
220 char namecopy[MAX_PATH];
221 char* end;
222 char *basename;
223 char *parent;
224 struct dirent_uncached *entry;
225 struct fat_dir newdir;
226 int rc;
228 if ( name[0] != '/' ) {
229 DEBUGF("mkdir: Only absolute paths supported right now\n");
230 return -1;
233 strncpy(namecopy,name,sizeof(namecopy));
234 namecopy[sizeof(namecopy)-1] = 0;
236 /* Split the base name and the path */
237 end = strrchr(namecopy, '/');
238 *end = 0;
239 basename = end+1;
241 if(namecopy == end) /* Root dir? */
242 parent = "/";
243 else
244 parent = namecopy;
246 DEBUGF("mkdir: parent: %s, name: %s\n", parent, basename);
248 dir = opendir_uncached(parent);
250 if(!dir) {
251 DEBUGF("mkdir: can't open parent dir\n");
252 return -2;
255 if(basename[0] == 0) {
256 DEBUGF("mkdir: Empty dir name\n");
257 errno = EINVAL;
258 return -3;
261 /* Now check if the name already exists */
262 while ((entry = readdir_uncached(dir))) {
263 if ( !strcasecmp(basename, entry->d_name) ) {
264 DEBUGF("mkdir error: file exists\n");
265 errno = EEXIST;
266 closedir_uncached(dir);
267 return - 4;
271 memset(&newdir, 0, sizeof(struct fat_dir));
273 rc = fat_create_dir(basename, &newdir, &(dir->fatdir));
274 closedir_uncached(dir);
276 return rc;
279 int rmdir_uncached(const char* name)
281 int rc;
282 DIR_UNCACHED* dir;
283 struct dirent_uncached* entry;
285 dir = opendir_uncached(name);
286 if (!dir)
288 errno = ENOENT; /* open error */
289 return -1;
292 /* check if the directory is empty */
293 while ((entry = readdir_uncached(dir)))
295 if (strcmp(entry->d_name, ".") &&
296 strcmp(entry->d_name, ".."))
298 DEBUGF("rmdir error: not empty\n");
299 errno = ENOTEMPTY;
300 closedir_uncached(dir);
301 return -2;
305 rc = fat_remove(&(dir->fatdir.file));
306 if ( rc < 0 ) {
307 DEBUGF("Failed removing dir: %d\n", rc);
308 errno = EIO;
309 rc = rc * 10 - 3;
312 closedir_uncached(dir);
313 return rc;