blob: e036c9584020910f2f91963e5c162413207f6e4b [file] [log] [blame]
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
Kevin Ferrare011a3252007-07-20 17:06:55 +00008 * $Id: dir.c 13741 2007-06-30 02:08:27Z jethead71 $
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +00009 *
Nicolas Pennequin357ffb32008-05-05 10:32:46 +000010 * Copyright (C) 2002 by Björn Stenberg
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +000011 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * 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.
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <stdio.h>
Björn Stenberg3d25f782002-05-27 09:13:24 +000022#include <errno.h>
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +000023#include <string.h>
Björn Stenberg23820442002-05-13 12:29:34 +000024#include <stdbool.h>
Bertrik Sikken28434692008-04-28 16:18:04 +000025#include <stdlib.h>
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +000026#include "fat.h"
27#include "dir.h"
28#include "debug.h"
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +000029
Björn Stenberg3d25f782002-05-27 09:13:24 +000030#define MAX_OPEN_DIRS 8
31
Kevin Ferrare011a3252007-07-20 17:06:55 +000032static DIR_UNCACHED opendirs[MAX_OPEN_DIRS];
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +000033
Jörg Hohensohnda848572004-12-28 22:16:07 +000034#ifdef HAVE_MULTIVOLUME
35
Jörg Hohensohnda848572004-12-28 22:16:07 +000036/* returns on which volume this is, and copies the reduced name
37 (sortof a preprocessor for volume-decorated pathnames) */
Miika Pekkarinen52d827a2008-03-11 19:39:26 +000038int strip_volume(const char* name, char* namecopy)
Jörg Hohensohnda848572004-12-28 22:16:07 +000039{
40 int volume = 0;
Jens Arnoldf9b20912006-06-30 05:35:11 +000041 const char *temp = name;
42
Jens Arnold7bbf53e2006-06-30 05:43:08 +000043 while (*temp == '/') /* skip all leading slashes */
Jens Arnoldf9b20912006-06-30 05:35:11 +000044 ++temp;
45
Miika Pekkarinen52d827a2008-03-11 19:39:26 +000046 if (*temp && !strncmp(temp, VOL_NAMES, VOL_ENUM_POS))
Jörg Hohensohnda848572004-12-28 22:16:07 +000047 {
Jens Arnoldf9b20912006-06-30 05:35:11 +000048 temp += VOL_ENUM_POS; /* behind special name */
49 volume = atoi(temp); /* number is following */
Jörg Hohensohnda848572004-12-28 22:16:07 +000050 temp = strchr(temp, '/'); /* search for slash behind */
51 if (temp != NULL)
Jens Arnoldf9b20912006-06-30 05:35:11 +000052 name = temp; /* use the part behind the volume */
Jörg Hohensohnda848572004-12-28 22:16:07 +000053 else
Jens Arnoldf9b20912006-06-30 05:35:11 +000054 name = "/"; /* else this must be the root dir */
Jörg Hohensohnda848572004-12-28 22:16:07 +000055 }
56
57 strncpy(namecopy, name, MAX_PATH);
58 namecopy[MAX_PATH-1] = '\0';
59
60 return volume;
61}
62#endif /* #ifdef HAVE_MULTIVOLUME */
63
64
Jörg Hohensohndc7534b2005-01-28 21:32:16 +000065#ifdef HAVE_HOTSWAP
66// release all dir handles on a given volume "by force", to avoid leaks
67int release_dirs(int volume)
68{
Kevin Ferrare011a3252007-07-20 17:06:55 +000069 DIR_UNCACHED* pdir = opendirs;
Jörg Hohensohndc7534b2005-01-28 21:32:16 +000070 int dd;
71 int closed = 0;
72 for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
73 {
74 if (pdir->fatdir.file.volume == volume)
75 {
76 pdir->busy = false; /* mark as available, no further action */
77 closed++;
78 }
79 }
80 return closed; /* return how many we did */
81}
82#endif /* #ifdef HAVE_HOTSWAP */
83
Kevin Ferrare011a3252007-07-20 17:06:55 +000084DIR_UNCACHED* opendir_uncached(const char* name)
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +000085{
Linus Nielsen Feltzing377725f2002-07-15 22:58:28 +000086 char namecopy[MAX_PATH];
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +000087 char* part;
Björn Stenberga7b8b002002-05-24 09:57:33 +000088 char* end;
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +000089 struct fat_direntry entry;
Björn Stenberg3d25f782002-05-27 09:13:24 +000090 int dd;
Kevin Ferrare011a3252007-07-20 17:06:55 +000091 DIR_UNCACHED* pdir = opendirs;
Jörg Hohensohnda848572004-12-28 22:16:07 +000092#ifdef HAVE_MULTIVOLUME
93 int volume;
94#endif
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +000095
Jörg Hohensohndcdffe82005-01-03 20:23:42 +000096 if ( name[0] != '/' ) {
97 DEBUGF("Only absolute paths supported right now\n");
98 return NULL;
99 }
100
Björn Stenberg3d25f782002-05-27 09:13:24 +0000101 /* find a free dir descriptor */
Jörg Hohensohndcdffe82005-01-03 20:23:42 +0000102 for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
103 if ( !pdir->busy )
Björn Stenberg3d25f782002-05-27 09:13:24 +0000104 break;
105
106 if ( dd == MAX_OPEN_DIRS ) {
107 DEBUGF("Too many dirs open\n");
108 errno = EMFILE;
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000109 return NULL;
110 }
111
Jörg Hohensohndcdffe82005-01-03 20:23:42 +0000112 pdir->busy = true;
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000113
Jörg Hohensohnda848572004-12-28 22:16:07 +0000114#ifdef HAVE_MULTIVOLUME
115 /* try to extract a heading volume name, if present */
116 volume = strip_volume(name, namecopy);
Jörg Hohensohndcdffe82005-01-03 20:23:42 +0000117 pdir->volumecounter = 0;
Jörg Hohensohnda848572004-12-28 22:16:07 +0000118#else
119 strncpy(namecopy,name,sizeof(namecopy)); /* just copy */
120 namecopy[sizeof(namecopy)-1] = '\0';
121#endif
122
Jörg Hohensohndcdffe82005-01-03 20:23:42 +0000123 if ( fat_opendir(IF_MV2(volume,) &pdir->fatdir, 0, NULL) < 0 ) {
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000124 DEBUGF("Failed opening root dir\n");
Jörg Hohensohndcdffe82005-01-03 20:23:42 +0000125 pdir->busy = false;
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000126 return NULL;
127 }
128
Björn Stenberga7b8b002002-05-24 09:57:33 +0000129 for ( part = strtok_r(namecopy, "/", &end); part;
130 part = strtok_r(NULL, "/", &end)) {
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000131 /* scan dir for name */
132 while (1) {
Jörg Hohensohndcdffe82005-01-03 20:23:42 +0000133 if ((fat_getnext(&pdir->fatdir,&entry) < 0) ||
Björn Stenberg3d25f782002-05-27 09:13:24 +0000134 (!entry.name[0])) {
Jörg Hohensohndcdffe82005-01-03 20:23:42 +0000135 pdir->busy = false;
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000136 return NULL;
Björn Stenberg3d25f782002-05-27 09:13:24 +0000137 }
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000138 if ( (entry.attr & FAT_ATTR_DIRECTORY) &&
Linus Nielsen Feltzingfae5b822002-08-15 11:26:12 +0000139 (!strcasecmp(part, entry.name)) ) {
Jörg Hohensohndcdffe82005-01-03 20:23:42 +0000140 pdir->parent_dir = pdir->fatdir;
Jörg Hohensohnda848572004-12-28 22:16:07 +0000141 if ( fat_opendir(IF_MV2(volume,)
Jörg Hohensohndcdffe82005-01-03 20:23:42 +0000142 &pdir->fatdir,
Linus Nielsen Feltzingbc9397d2004-04-16 08:58:29 +0000143 entry.firstcluster,
Jörg Hohensohndcdffe82005-01-03 20:23:42 +0000144 &pdir->parent_dir) < 0 ) {
Jean-Philippe Bernardycea551d2005-01-23 23:29:35 +0000145 DEBUGF("Failed opening dir '%s' (%ld)\n",
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000146 part, entry.firstcluster);
Jörg Hohensohndcdffe82005-01-03 20:23:42 +0000147 pdir->busy = false;
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000148 return NULL;
149 }
Jörg Hohensohnda848572004-12-28 22:16:07 +0000150#ifdef HAVE_MULTIVOLUME
Jörg Hohensohndcdffe82005-01-03 20:23:42 +0000151 pdir->volumecounter = -1; /* n.a. to subdirs */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000152#endif
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000153 break;
154 }
155 }
156 }
157
Jörg Hohensohndcdffe82005-01-03 20:23:42 +0000158 return pdir;
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000159}
160
Kevin Ferrare011a3252007-07-20 17:06:55 +0000161int closedir_uncached(DIR_UNCACHED* dir)
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000162{
Björn Stenberg3d25f782002-05-27 09:13:24 +0000163 dir->busy=false;
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000164 return 0;
165}
166
Kevin Ferrare011a3252007-07-20 17:06:55 +0000167struct dirent_uncached* readdir_uncached(DIR_UNCACHED* dir)
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000168{
169 struct fat_direntry entry;
Kevin Ferrare011a3252007-07-20 17:06:55 +0000170 struct dirent_uncached* theent = &(dir->theent);
Jörg Hohensohndc7534b2005-01-28 21:32:16 +0000171
172 if (!dir->busy)
173 return NULL;
174
Jörg Hohensohnda848572004-12-28 22:16:07 +0000175#ifdef HAVE_MULTIVOLUME
176 /* Volumes (secondary file systems) get inserted into the root directory
177 of the first volume, since we have no separate top level. */
178 if (dir->volumecounter >= 0 /* on a root dir */
179 && dir->volumecounter < NUM_VOLUMES /* in range */
180 && dir->fatdir.file.volume == 0) /* at volume 0 */
181 { /* fake special directories, which don't really exist, but
Kevin Ferrare011a3252007-07-20 17:06:55 +0000182 will get redirected upon opendir_uncached() */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000183 while (++dir->volumecounter < NUM_VOLUMES)
184 {
185 if (fat_ismounted(dir->volumecounter))
186 {
187 memset(theent, 0, sizeof(*theent));
188 theent->attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
189 snprintf(theent->d_name, sizeof(theent->d_name),
Miika Pekkarinen52d827a2008-03-11 19:39:26 +0000190 VOL_NAMES, dir->volumecounter);
Jörg Hohensohnda848572004-12-28 22:16:07 +0000191 return theent;
192 }
193 }
194 }
195#endif
196 /* normal directory entry fetching follows here */
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000197 if (fat_getnext(&(dir->fatdir),&entry) < 0)
198 return NULL;
199
200 if ( !entry.name[0] )
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000201 return NULL;
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000202
Björn Stenberg3d25f782002-05-27 09:13:24 +0000203 strncpy(theent->d_name, entry.name, sizeof( theent->d_name ) );
204 theent->attribute = entry.attr;
205 theent->size = entry.filesize;
206 theent->startcluster = entry.firstcluster;
Jörg Hohensohn74eb6422004-06-20 16:34:29 +0000207 theent->wrtdate = entry.wrtdate;
208 theent->wrttime = entry.wrttime;
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000209
Björn Stenberg3d25f782002-05-27 09:13:24 +0000210 return theent;
Björn Stenberg4d4ec3a2002-05-03 15:36:52 +0000211}
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +0000212
Kevin Ferrare011a3252007-07-20 17:06:55 +0000213int mkdir_uncached(const char *name)
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +0000214{
Kevin Ferrare011a3252007-07-20 17:06:55 +0000215 DIR_UNCACHED *dir;
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +0000216 char namecopy[MAX_PATH];
217 char* end;
218 char *basename;
219 char *parent;
Kevin Ferrare011a3252007-07-20 17:06:55 +0000220 struct dirent_uncached *entry;
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +0000221 struct fat_dir newdir;
222 int rc;
Linus Nielsen Feltzingef7293f2004-01-21 14:58:40 +0000223
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +0000224 if ( name[0] != '/' ) {
Linus Nielsen Feltzing05f1fc42004-02-11 14:37:16 +0000225 DEBUGF("mkdir: Only absolute paths supported right now\n");
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +0000226 return -1;
227 }
228
229 strncpy(namecopy,name,sizeof(namecopy));
230 namecopy[sizeof(namecopy)-1] = 0;
231
232 /* Split the base name and the path */
233 end = strrchr(namecopy, '/');
234 *end = 0;
235 basename = end+1;
236
237 if(namecopy == end) /* Root dir? */
238 parent = "/";
239 else
240 parent = namecopy;
241
242 DEBUGF("mkdir: parent: %s, name: %s\n", parent, basename);
243
Kevin Ferrare011a3252007-07-20 17:06:55 +0000244 dir = opendir_uncached(parent);
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +0000245
246 if(!dir) {
247 DEBUGF("mkdir: can't open parent dir\n");
248 return -2;
249 }
250
Linus Nielsen Feltzing05f1fc42004-02-11 14:37:16 +0000251 if(basename[0] == 0) {
252 DEBUGF("mkdir: Empty dir name\n");
253 errno = EINVAL;
254 return -3;
255 }
256
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +0000257 /* Now check if the name already exists */
Kevin Ferrare011a3252007-07-20 17:06:55 +0000258 while ((entry = readdir_uncached(dir))) {
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +0000259 if ( !strcasecmp(basename, entry->d_name) ) {
260 DEBUGF("mkdir error: file exists\n");
261 errno = EEXIST;
Kevin Ferrare011a3252007-07-20 17:06:55 +0000262 closedir_uncached(dir);
Linus Nielsen Feltzing05f1fc42004-02-11 14:37:16 +0000263 return - 4;
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +0000264 }
265 }
Linus Nielsen Feltzingce1c93d2004-01-15 14:34:54 +0000266
Linus Nielsen Feltzing54353e02004-01-15 15:56:19 +0000267 memset(&newdir, sizeof(struct fat_dir), 0);
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +0000268
269 rc = fat_create_dir(basename, &newdir, &(dir->fatdir));
Kevin Ferrare011a3252007-07-20 17:06:55 +0000270 closedir_uncached(dir);
Linus Nielsen Feltzing54353e02004-01-15 15:56:19 +0000271
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +0000272 return rc;
273}
Linus Nielsen Feltzingbc9397d2004-04-16 08:58:29 +0000274
Kevin Ferrare011a3252007-07-20 17:06:55 +0000275int rmdir_uncached(const char* name)
Linus Nielsen Feltzingbc9397d2004-04-16 08:58:29 +0000276{
277 int rc;
Kevin Ferrare011a3252007-07-20 17:06:55 +0000278 DIR_UNCACHED* dir;
279 struct dirent_uncached* entry;
Linus Nielsen Feltzingbc9397d2004-04-16 08:58:29 +0000280
Kevin Ferrare011a3252007-07-20 17:06:55 +0000281 dir = opendir_uncached(name);
Linus Nielsen Feltzingbc9397d2004-04-16 08:58:29 +0000282 if (!dir)
283 {
284 errno = ENOENT; /* open error */
285 return -1;
286 }
287
288 /* check if the directory is empty */
Kevin Ferrare011a3252007-07-20 17:06:55 +0000289 while ((entry = readdir_uncached(dir)))
Linus Nielsen Feltzingbc9397d2004-04-16 08:58:29 +0000290 {
291 if (strcmp(entry->d_name, ".") &&
292 strcmp(entry->d_name, ".."))
293 {
294 DEBUGF("rmdir error: not empty\n");
295 errno = ENOTEMPTY;
Kevin Ferrare011a3252007-07-20 17:06:55 +0000296 closedir_uncached(dir);
Linus Nielsen Feltzingbc9397d2004-04-16 08:58:29 +0000297 return -2;
298 }
299 }
300
301 rc = fat_remove(&(dir->fatdir.file));
302 if ( rc < 0 ) {
303 DEBUGF("Failed removing dir: %d\n", rc);
304 errno = EIO;
305 rc = rc * 10 - 3;
306 }
307
Kevin Ferrare011a3252007-07-20 17:06:55 +0000308 closedir_uncached(dir);
Linus Nielsen Feltzingbc9397d2004-04-16 08:58:29 +0000309 return rc;
310}