Subversion Repositories freemyipod

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
46 theseven 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
 * 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.
16
 *
17
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18
 * KIND, either express or implied.
19
 *
20
 ****************************************************************************/
53 theseven 21
#include "global.h"
46 theseven 22
#include <stdio.h>
23
#include <errno.h>
24
#include <string.h>
25
#include <stdlib.h>
26
#include "fat.h"
27
#include "dir.h"
28
#include "debug.h"
29
 
30
#if ((defined(MEMORYSIZE) && (MEMORYSIZE > 8)) || MEM > 8)
31
#define MAX_OPEN_DIRS 12
32
#else
33
#define MAX_OPEN_DIRS 8
34
#endif
35
 
36
static DIR opendirs[MAX_OPEN_DIRS];
37
 
38
#ifdef HAVE_HOTSWAP
39
// release all dir handles on a given volume "by force", to avoid leaks
40
int release_dirs(int volume)
41
{
42
    DIR* pdir = opendirs;
43
    int dd;
44
    int closed = 0;
45
    for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
46
    {
47
#ifdef HAVE_MULTIVOLUME
48
        if (pdir->fatdir.file.volume == volume)
49
#else
50
        (void)volume;
51
#endif
52
        {
53
            pdir->busy = false; /* mark as available, no further action */
54
            closed++;
55
        }
56
    }
57
    return closed; /* return how many we did */
58
}
59
#endif /* #ifdef HAVE_HOTSWAP */
60
 
61
DIR* opendir(const char* name)
62
{
63
    char namecopy[MAX_PATH];
64
    char* part;
65
    char* end;
66
    struct fat_direntry entry;
67
    int dd;
68
    DIR* pdir = opendirs;
69
#ifdef HAVE_MULTIVOLUME
70
    int volume;
71
#endif
72
 
73
    if ( name[0] != '/' ) {
50 theseven 74
        DEBUGF("Only absolute paths supported right now");
46 theseven 75
        return NULL;
76
    }
77
 
78
    /* find a free dir descriptor */
79
    for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
80
        if ( !pdir->busy )
81
            break;
82
 
83
    if ( dd == MAX_OPEN_DIRS ) {
50 theseven 84
        DEBUGF("Too many dirs open");
46 theseven 85
        errno = EMFILE;
86
        return NULL;
87
    }
88
 
89
    pdir->busy = true;
90
 
91
#ifdef HAVE_MULTIVOLUME
92
    /* try to extract a heading volume name, if present */
93
    volume = strip_volume(name, namecopy);
94
    pdir->volumecounter = 0;
95
#else
96
    strlcpy(namecopy, name, sizeof(namecopy)); /* just copy */
97
#endif
98
 
99
    if ( fat_opendir(IF_MV2(volume,) &pdir->fatdir, 0, NULL) < 0 ) {
50 theseven 100
        DEBUGF("Failed opening root dir");
46 theseven 101
        pdir->busy = false;
102
        return NULL;
103
    }
104
 
105
    for ( part = strtok_r(namecopy, "/", &end); part;
106
          part = strtok_r(NULL, "/", &end)) {
107
        /* scan dir for name */
108
        while (1) {
109
            if ((fat_getnext(&pdir->fatdir,&entry) < 0) ||
110
                (!entry.name[0])) {
111
                pdir->busy = false;
112
                return NULL;
113
            }
114
            if ( (entry.attr & FAT_ATTR_DIRECTORY) &&
115
                 (!strcasecmp(part, entry.name)) ) {
116
                /* In reality, the parent_dir parameter of fat_opendir seems
117
                 * useless because it's sole purpose it to have a way to
118
                 * update the file metadata, but here we are only reading
119
                 * a directory so there's no need for that kind of stuff.
120
                 * However, the rmdir function uses a ugly hack to
121
                 * avoid opening a directory twice when deleting it and thus
122
                 * needs those information. That's why we pass pdir->fatdir both
123
                 * as the parent directory and the resulting one (this is safe,
124
                 * in doubt, check fat_open(dir) code) which will allow this kind of
125
                 * (ugly) things */
126
                if ( fat_opendir(IF_MV2(volume,)
127
                                 &pdir->fatdir,
128
                                 entry.firstcluster,
129
                                 &pdir->fatdir) < 0 ) {
50 theseven 130
                    DEBUGF("Failed opening dir '%s' (%ld)",
46 theseven 131
                           part, entry.firstcluster);
132
                    pdir->busy = false;
133
                    return NULL;
134
                }
135
#ifdef HAVE_MULTIVOLUME
136
                pdir->volumecounter = -1; /* n.a. to subdirs */
137
#endif
138
                break;
139
            }
140
        }
141
    }
142
 
143
    return pdir;
144
}
145
 
146
int closedir(DIR* dir)
147
{
148
    dir->busy=false;
149
    return 0;
150
}
151
 
152
struct dirent* readdir(DIR* dir)
153
{
154
    struct fat_direntry entry;
155
    struct dirent* theent = &(dir->theent);
156
 
157
    if (!dir->busy)
158
        return NULL;
159
 
160
#ifdef HAVE_MULTIVOLUME
161
    /* Volumes (secondary file systems) get inserted into the root directory
162
        of the first volume, since we have no separate top level. */
163
    if (dir->volumecounter >= 0 /* on a root dir */
164
     && dir->volumecounter < NUM_VOLUMES /* in range */
165
     && dir->fatdir.file.volume == 0) /* at volume 0 */
166
    {   /* fake special directories, which don't really exist, but
167
           will get redirected upon opendir() */
168
        while (++dir->volumecounter < NUM_VOLUMES)
169
        {
170
            if (fat_ismounted(dir->volumecounter))
171
            {
172
                memset(theent, 0, sizeof(*theent));
173
                theent->attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
174
                snprintf(theent->d_name, sizeof(theent->d_name), 
175
                         VOL_NAMES, dir->volumecounter);
176
                return theent;
177
            }
178
        }
179
    }
180
#endif
181
    /* normal directory entry fetching follows here */
182
    if (fat_getnext(&(dir->fatdir),&entry) < 0)
183
        return NULL;
184
 
185
    if ( !entry.name[0] )
186
        return NULL;
187
 
188
    strlcpy(theent->d_name, entry.name, sizeof(theent->d_name));
189
    theent->attribute = entry.attr;
190
    theent->size = entry.filesize;
191
    theent->startcluster = entry.firstcluster;
192
    theent->wrtdate = entry.wrtdate;
193
    theent->wrttime = entry.wrttime;
194
 
195
    return theent;
196
}
197
 
198
int mkdir(const char *name)
199
{
200
    DIR *dir;
201
    char namecopy[MAX_PATH];
202
    char* end;
203
    char *basename;
204
    char *parent;
205
    struct dirent *entry;
206
    struct fat_dir newdir;
207
    int rc;
208
 
209
    if ( name[0] != '/' ) {
50 theseven 210
        DEBUGF("mkdir: Only absolute paths supported right now");
46 theseven 211
        return -1;
212
    }
213
 
214
    strlcpy(namecopy, name, sizeof(namecopy));
215
 
216
    /* Split the base name and the path */
217
    end = strrchr(namecopy, '/');
218
    *end = 0;
219
    basename = end+1;
220
 
221
    if(namecopy == end) /* Root dir? */
222
        parent = "/";
223
    else
224
        parent = namecopy;
225
 
50 theseven 226
    DEBUGF("mkdir: parent: %s, name: %s", parent, basename);
46 theseven 227
 
228
    dir = opendir(parent);
229
 
230
    if(!dir) {
50 theseven 231
        DEBUGF("mkdir: can't open parent dir");
46 theseven 232
        return -2;
233
    }    
234
 
235
    if(basename[0] == 0) {
50 theseven 236
        DEBUGF("mkdir: Empty dir name");
46 theseven 237
        errno = EINVAL;
238
        return -3;
239
    }
240
 
241
    /* Now check if the name already exists */
242
    while ((entry = readdir(dir))) {
243
        if ( !strcasecmp(basename, entry->d_name) ) {
50 theseven 244
            DEBUGF("mkdir error: file exists");
46 theseven 245
            errno = EEXIST;
246
            closedir(dir);
247
            return - 4;
248
        }
249
    }
250
 
251
    memset(&newdir, 0, sizeof(struct fat_dir));
252
 
253
    rc = fat_create_dir(basename, &newdir, &(dir->fatdir));
254
    closedir(dir);
255
 
256
    return rc;
257
}
258
 
259
int rmdir(const char* name)
260
{
261
    int rc;
262
    DIR* dir;
263
    struct dirent* entry;
264
 
265
    dir = opendir(name);
266
    if (!dir)
267
    {
268
        errno = ENOENT; /* open error */
269
        return -1;
270
    }
271
 
272
    /* check if the directory is empty */
273
    while ((entry = readdir(dir)))
274
    {
275
        if (strcmp(entry->d_name, ".") &&
276
            strcmp(entry->d_name, ".."))
277
        {
50 theseven 278
            DEBUGF("rmdir error: not empty");
46 theseven 279
            errno = ENOTEMPTY;
280
            closedir(dir);
281
            return -2;
282
        }
283
    }
284
 
285
    rc = fat_remove(&(dir->fatdir.file));
286
    if ( rc < 0 ) {
50 theseven 287
        DEBUGF("Failed removing dir: %d", rc);
46 theseven 288
        errno = EIO;
289
        rc = rc * 10 - 3;
290
    }
291
 
292
    closedir(dir);
293
    return rc;
294
}
295
 
296
#ifdef HAVE_MULTIVOLUME
297
/* returns on which volume this is, and copies the reduced name
298
   (sortof a preprocessor for volume-decorated pathnames) */
299
int strip_volume(const char* name, char* namecopy)
300
{
301
    int volume = 0;
302
    const char *temp = name;
303
 
304
    while (*temp == '/')          /* skip all leading slashes */
305
        ++temp;
306
 
307
    if (*temp && !strncmp(temp, VOL_NAMES, VOL_ENUM_POS))
308
    {
309
        temp += VOL_ENUM_POS;     /* behind special name */
310
        volume = atoi(temp);      /* number is following */
311
        temp = strchr(temp, '/'); /* search for slash behind */
312
        if (temp != NULL)
313
            name = temp;          /* use the part behind the volume */
314
        else
315
            name = "/";           /* else this must be the root dir */
316
    }
317
 
318
    strlcpy(namecopy, name, MAX_PATH);
319
 
320
    return volume;
321
}
322
#endif /* #ifdef HAVE_MULTIVOLUME */