diff options
Diffstat (limited to 'firmware/common/dir_uncached.c')
-rw-r--r-- | firmware/common/dir_uncached.c | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/firmware/common/dir_uncached.c b/firmware/common/dir_uncached.c new file mode 100644 index 0000000000..e6b59c30ec --- /dev/null +++ b/firmware/common/dir_uncached.c | |||
@@ -0,0 +1,322 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id: dir.c 13741 2007-06-30 02:08:27Z jethead71 $ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Björn Stenberg | ||
11 | * | ||
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. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
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" | ||
28 | |||
29 | #define MAX_OPEN_DIRS 8 | ||
30 | |||
31 | static DIR_UNCACHED opendirs[MAX_OPEN_DIRS]; | ||
32 | |||
33 | #ifdef HAVE_MULTIVOLUME | ||
34 | |||
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 | ||
47 | |||
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) | ||
51 | { | ||
52 | int volume = 0; | ||
53 | const char *temp = name; | ||
54 | |||
55 | while (*temp == '/') /* skip all leading slashes */ | ||
56 | ++temp; | ||
57 | |||
58 | if (*temp && !strncmp(temp, vol_names, VOL_ENUM_POS)) | ||
59 | { | ||
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 */ | ||
67 | } | ||
68 | |||
69 | strncpy(namecopy, name, MAX_PATH); | ||
70 | namecopy[MAX_PATH-1] = '\0'; | ||
71 | |||
72 | return volume; | ||
73 | } | ||
74 | #endif /* #ifdef HAVE_MULTIVOLUME */ | ||
75 | |||
76 | |||
77 | #ifdef HAVE_HOTSWAP | ||
78 | // release all dir handles on a given volume "by force", to avoid leaks | ||
79 | int release_dirs(int volume) | ||
80 | { | ||
81 | DIR_UNCACHED* pdir = opendirs; | ||
82 | int dd; | ||
83 | int closed = 0; | ||
84 | for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++) | ||
85 | { | ||
86 | if (pdir->fatdir.file.volume == volume) | ||
87 | { | ||
88 | pdir->busy = false; /* mark as available, no further action */ | ||
89 | closed++; | ||
90 | } | ||
91 | } | ||
92 | return closed; /* return how many we did */ | ||
93 | } | ||
94 | #endif /* #ifdef HAVE_HOTSWAP */ | ||
95 | |||
96 | DIR_UNCACHED* opendir_uncached(const char* name) | ||
97 | { | ||
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 | ||
107 | |||
108 | if ( name[0] != '/' ) { | ||
109 | DEBUGF("Only absolute paths supported right now\n"); | ||
110 | return NULL; | ||
111 | } | ||
112 | |||
113 | /* find a free dir descriptor */ | ||
114 | for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++) | ||
115 | if ( !pdir->busy ) | ||
116 | break; | ||
117 | |||
118 | if ( dd == MAX_OPEN_DIRS ) { | ||
119 | DEBUGF("Too many dirs open\n"); | ||
120 | errno = EMFILE; | ||
121 | return NULL; | ||
122 | } | ||
123 | |||
124 | pdir->busy = true; | ||
125 | |||
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 | ||
134 | |||
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; | ||
139 | } | ||
140 | |||
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; | ||
149 | } | ||
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; | ||
161 | } | ||
162 | #ifdef HAVE_MULTIVOLUME | ||
163 | pdir->volumecounter = -1; /* n.a. to subdirs */ | ||
164 | #endif | ||
165 | break; | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | |||
170 | return pdir; | ||
171 | } | ||
172 | |||
173 | int closedir_uncached(DIR_UNCACHED* dir) | ||
174 | { | ||
175 | dir->busy=false; | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | struct dirent_uncached* readdir_uncached(DIR_UNCACHED* dir) | ||
180 | { | ||
181 | struct fat_direntry entry; | ||
182 | struct dirent_uncached* theent = &(dir->theent); | ||
183 | |||
184 | if (!dir->busy) | ||
185 | return NULL; | ||
186 | |||
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) | ||
196 | { | ||
197 | if (fat_ismounted(dir->volumecounter)) | ||
198 | { | ||
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; | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | #endif | ||
208 | /* normal directory entry fetching follows here */ | ||
209 | if (fat_getnext(&(dir->fatdir),&entry) < 0) | ||
210 | return NULL; | ||
211 | |||
212 | if ( !entry.name[0] ) | ||
213 | return NULL; | ||
214 | |||
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; | ||
221 | |||
222 | return theent; | ||
223 | } | ||
224 | |||
225 | int mkdir_uncached(const char *name) | ||
226 | { | ||
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; | ||
235 | |||
236 | if ( name[0] != '/' ) { | ||
237 | DEBUGF("mkdir: Only absolute paths supported right now\n"); | ||
238 | return -1; | ||
239 | } | ||
240 | |||
241 | strncpy(namecopy,name,sizeof(namecopy)); | ||
242 | namecopy[sizeof(namecopy)-1] = 0; | ||
243 | |||
244 | /* Split the base name and the path */ | ||
245 | end = strrchr(namecopy, '/'); | ||
246 | *end = 0; | ||
247 | basename = end+1; | ||
248 | |||
249 | if(namecopy == end) /* Root dir? */ | ||
250 | parent = "/"; | ||
251 | else | ||
252 | parent = namecopy; | ||
253 | |||
254 | DEBUGF("mkdir: parent: %s, name: %s\n", parent, basename); | ||
255 | |||
256 | dir = opendir_uncached(parent); | ||
257 | |||
258 | if(!dir) { | ||
259 | DEBUGF("mkdir: can't open parent dir\n"); | ||
260 | return -2; | ||
261 | } | ||
262 | |||
263 | if(basename[0] == 0) { | ||
264 | DEBUGF("mkdir: Empty dir name\n"); | ||
265 | errno = EINVAL; | ||
266 | return -3; | ||
267 | } | ||
268 | |||
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; | ||
276 | } | ||
277 | } | ||
278 | |||
279 | memset(&newdir, sizeof(struct fat_dir), 0); | ||
280 | |||
281 | rc = fat_create_dir(basename, &newdir, &(dir->fatdir)); | ||
282 | closedir_uncached(dir); | ||
283 | |||
284 | return rc; | ||
285 | } | ||
286 | |||
287 | int rmdir_uncached(const char* name) | ||
288 | { | ||
289 | int rc; | ||
290 | DIR_UNCACHED* dir; | ||
291 | struct dirent_uncached* entry; | ||
292 | |||
293 | dir = opendir_uncached(name); | ||
294 | if (!dir) | ||
295 | { | ||
296 | errno = ENOENT; /* open error */ | ||
297 | return -1; | ||
298 | } | ||
299 | |||
300 | /* check if the directory is empty */ | ||
301 | while ((entry = readdir_uncached(dir))) | ||
302 | { | ||
303 | if (strcmp(entry->d_name, ".") && | ||
304 | strcmp(entry->d_name, "..")) | ||
305 | { | ||
306 | DEBUGF("rmdir error: not empty\n"); | ||
307 | errno = ENOTEMPTY; | ||
308 | closedir_uncached(dir); | ||
309 | return -2; | ||
310 | } | ||
311 | } | ||
312 | |||
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; | ||
318 | } | ||
319 | |||
320 | closedir_uncached(dir); | ||
321 | return rc; | ||
322 | } | ||