Get rid of the parent_dir field in dir_uncached.c by using the same FAT trick as...
[kugel-rb.git] / firmware / common / dir_uncached.c
blobc6f3a6f2e1eb27a517529f4bfd2ec00ebb22de72
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"
29 #include "filefuncs.h"
31 #if ((defined(MEMORYSIZE) && (MEMORYSIZE > 8)) || MEM > 8)
32 #define MAX_OPEN_DIRS 12
33 #else
34 #define MAX_OPEN_DIRS 8
35 #endif
37 static DIR_UNCACHED opendirs[MAX_OPEN_DIRS];
39 #ifdef HAVE_HOTSWAP
40 // release all dir handles on a given volume "by force", to avoid leaks
41 int release_dirs(int volume)
43 DIR_UNCACHED* pdir = opendirs;
44 int dd;
45 int closed = 0;
46 for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
48 #ifdef HAVE_MULTIVOLUME
49 if (pdir->fatdir.file.volume == volume)
50 #else
51 (void)volume;
52 #endif
54 pdir->busy = false; /* mark as available, no further action */
55 closed++;
58 return closed; /* return how many we did */
60 #endif /* #ifdef HAVE_HOTSWAP */
62 DIR_UNCACHED* opendir_uncached(const char* name)
64 char namecopy[MAX_PATH];
65 char* part;
66 char* end;
67 struct fat_direntry entry;
68 int dd;
69 DIR_UNCACHED* pdir = opendirs;
70 #ifdef HAVE_MULTIVOLUME
71 int volume;
72 #endif
74 if ( name[0] != '/' ) {
75 DEBUGF("Only absolute paths supported right now\n");
76 return NULL;
79 /* find a free dir descriptor */
80 for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
81 if ( !pdir->busy )
82 break;
84 if ( dd == MAX_OPEN_DIRS ) {
85 DEBUGF("Too many dirs open\n");
86 errno = EMFILE;
87 return NULL;
90 pdir->busy = true;
92 #ifdef HAVE_MULTIVOLUME
93 /* try to extract a heading volume name, if present */
94 volume = strip_volume(name, namecopy);
95 pdir->volumecounter = 0;
96 #else
97 strlcpy(namecopy, name, sizeof(namecopy)); /* just copy */
98 #endif
100 if ( fat_opendir(IF_MV2(volume,) &pdir->fatdir, 0, NULL) < 0 ) {
101 DEBUGF("Failed opening root dir\n");
102 pdir->busy = false;
103 return NULL;
106 for ( part = strtok_r(namecopy, "/", &end); part;
107 part = strtok_r(NULL, "/", &end)) {
108 /* scan dir for name */
109 while (1) {
110 if ((fat_getnext(&pdir->fatdir,&entry) < 0) ||
111 (!entry.name[0])) {
112 pdir->busy = false;
113 return NULL;
115 if ( (entry.attr & FAT_ATTR_DIRECTORY) &&
116 (!strcasecmp(part, entry.name)) ) {
117 /* in reality, the parent_dir parameter of fat_opendir is
118 * useless because it's sole purpose it to have a way to
119 * update the file metadata, but here we are only reading
120 * a directory so there's no need for that kind of stuff.
121 * Consequently, we can safely pass NULL of it because
122 * fat_opendir and fat_open are NULL-protected. */
123 if ( fat_opendir(IF_MV2(volume,)
124 &pdir->fatdir,
125 entry.firstcluster,
126 NULL) < 0 ) {
127 DEBUGF("Failed opening dir '%s' (%ld)\n",
128 part, entry.firstcluster);
129 pdir->busy = false;
130 return NULL;
132 #ifdef HAVE_MULTIVOLUME
133 pdir->volumecounter = -1; /* n.a. to subdirs */
134 #endif
135 break;
140 return pdir;
143 int closedir_uncached(DIR_UNCACHED* dir)
145 dir->busy=false;
146 return 0;
149 struct dirent_uncached* readdir_uncached(DIR_UNCACHED* dir)
151 struct fat_direntry entry;
152 struct dirent_uncached* theent = &(dir->theent);
154 if (!dir->busy)
155 return NULL;
157 #ifdef HAVE_MULTIVOLUME
158 /* Volumes (secondary file systems) get inserted into the root directory
159 of the first volume, since we have no separate top level. */
160 if (dir->volumecounter >= 0 /* on a root dir */
161 && dir->volumecounter < NUM_VOLUMES /* in range */
162 && dir->fatdir.file.volume == 0) /* at volume 0 */
163 { /* fake special directories, which don't really exist, but
164 will get redirected upon opendir_uncached() */
165 while (++dir->volumecounter < NUM_VOLUMES)
167 if (fat_ismounted(dir->volumecounter))
169 memset(theent, 0, sizeof(*theent));
170 theent->attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
171 snprintf(theent->d_name, sizeof(theent->d_name),
172 VOL_NAMES, dir->volumecounter);
173 return theent;
177 #endif
178 /* normal directory entry fetching follows here */
179 if (fat_getnext(&(dir->fatdir),&entry) < 0)
180 return NULL;
182 if ( !entry.name[0] )
183 return NULL;
185 strlcpy(theent->d_name, entry.name, sizeof(theent->d_name));
186 theent->attribute = entry.attr;
187 theent->size = entry.filesize;
188 theent->startcluster = entry.firstcluster;
189 theent->wrtdate = entry.wrtdate;
190 theent->wrttime = entry.wrttime;
192 return theent;
195 int mkdir_uncached(const char *name)
197 DIR_UNCACHED *dir;
198 char namecopy[MAX_PATH];
199 char* end;
200 char *basename;
201 char *parent;
202 struct dirent_uncached *entry;
203 struct fat_dir newdir;
204 int rc;
206 if ( name[0] != '/' ) {
207 DEBUGF("mkdir: Only absolute paths supported right now\n");
208 return -1;
211 strlcpy(namecopy, name, sizeof(namecopy));
213 /* Split the base name and the path */
214 end = strrchr(namecopy, '/');
215 *end = 0;
216 basename = end+1;
218 if(namecopy == end) /* Root dir? */
219 parent = "/";
220 else
221 parent = namecopy;
223 DEBUGF("mkdir: parent: %s, name: %s\n", parent, basename);
225 dir = opendir_uncached(parent);
227 if(!dir) {
228 DEBUGF("mkdir: can't open parent dir\n");
229 return -2;
232 if(basename[0] == 0) {
233 DEBUGF("mkdir: Empty dir name\n");
234 errno = EINVAL;
235 return -3;
238 /* Now check if the name already exists */
239 while ((entry = readdir_uncached(dir))) {
240 if ( !strcasecmp(basename, entry->d_name) ) {
241 DEBUGF("mkdir error: file exists\n");
242 errno = EEXIST;
243 closedir_uncached(dir);
244 return - 4;
248 memset(&newdir, 0, sizeof(struct fat_dir));
250 rc = fat_create_dir(basename, &newdir, &(dir->fatdir));
251 closedir_uncached(dir);
253 return rc;
256 int rmdir_uncached(const char* name)
258 int rc;
259 DIR_UNCACHED* dir;
260 struct dirent_uncached* entry;
262 dir = opendir_uncached(name);
263 if (!dir)
265 errno = ENOENT; /* open error */
266 return -1;
269 /* check if the directory is empty */
270 while ((entry = readdir_uncached(dir)))
272 if (strcmp(entry->d_name, ".") &&
273 strcmp(entry->d_name, ".."))
275 DEBUGF("rmdir error: not empty\n");
276 errno = ENOTEMPTY;
277 closedir_uncached(dir);
278 return -2;
282 rc = fat_remove(&(dir->fatdir.file));
283 if ( rc < 0 ) {
284 DEBUGF("Failed removing dir: %d\n", rc);
285 errno = EIO;
286 rc = rc * 10 - 3;
289 closedir_uncached(dir);
290 return rc;