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: file.c 26191 2010-05-20 12:59:12Z funman $
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
 ****************************************************************************/
52 theseven 21
#include "global.h"
46 theseven 22
#include <string.h>
23
#include <errno.h>
24
#include "file.h"
25
#include "fat.h"
26
#include "dir.h"
27
#include "debug.h"
28
 
29
/*
30
  These functions provide a roughly POSIX-compatible file IO API.
31
 
32
  Since the fat32 driver only manages sectors, we maintain a one-sector
33
  cache for each open file. This way we can provide byte access without
47 farthen 34
  having to re-read the sector each time.
46 theseven 35
  The penalty is the RAM used for the cache and slightly more complex code.
36
*/
37
 
38
struct filedesc {
39
    unsigned char cache[SECTOR_SIZE];
40
    int cacheoffset; /* invariant: 0 <= cacheoffset <= SECTOR_SIZE */
41
    long fileoffset;
42
    long size;
43
    int attr;
44
    struct fat_file fatfile;
45
    bool busy;
46
    bool write;
47
    bool dirty;
48
    bool trunc;
49
};
50
 
51
static struct filedesc openfiles[MAX_OPEN_FILES];
52
 
53
static int flush_cache(int fd);
54
 
55
int file_creat(const char *pathname)
56
{
57
    return open(pathname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
58
}
59
 
60
static int open_internal(const char* pathname, int flags, bool use_cache)
61
{
62
    DIR* dir;
63
    struct dirent* entry;
64
    int fd;
65
    char pathnamecopy[MAX_PATH];
66
    char* name;
67
    struct filedesc* file = NULL;
68
    int rc;
69
#ifndef HAVE_DIRCACHE
70
    (void)use_cache;
71
#endif
72
 
50 theseven 73
    DEBUGF("open(\"%s\",%d)",pathname,flags);
46 theseven 74
 
75
    if ( pathname[0] != '/' ) {
50 theseven 76
        DEBUGF("'%s' is not an absolute path.",pathname);
77
        DEBUGF("Only absolute pathnames supported at the moment");
46 theseven 78
        errno = EINVAL;
79
        return -1;
80
    }
81
 
82
    /* find a free file descriptor */
83
    for ( fd=0; fd<MAX_OPEN_FILES; fd++ )
84
        if ( !openfiles[fd].busy )
85
            break;
86
 
87
    if ( fd == MAX_OPEN_FILES ) {
50 theseven 88
        DEBUGF("Too many files open");
46 theseven 89
        errno = EMFILE;
90
        return -2;
91
    }
92
 
93
    file = &openfiles[fd];
94
    memset(file, 0, sizeof(struct filedesc));
95
 
96
    if (flags & (O_RDWR | O_WRONLY)) {
97
        file->write = true;
98
 
99
        if (flags & O_TRUNC)
100
            file->trunc = true;
101
    }
102
    file->busy = true;
103
 
104
#ifdef HAVE_DIRCACHE
105
    if (dircache_is_enabled() && !file->write && use_cache)
106
    {
107
        const struct dircache_entry *ce;
108
# ifdef HAVE_MULTIVOLUME
109
        int volume = strip_volume(pathname, pathnamecopy);
110
# endif
111
 
112
        ce = dircache_get_entry_ptr(pathname);
113
        if (!ce)
114
        {
115
            errno = ENOENT;
116
            file->busy = false;
117
            return -7;
118
        }
119
 
120
        fat_open(IF_MV2(volume,)
121
                 ce->startcluster,
122
                 &(file->fatfile),
123
                 NULL);
124
        file->size = ce->size;
125
        file->attr = ce->attribute;
126
        file->cacheoffset = -1;
127
        file->fileoffset = 0;
128
 
129
        return fd;
130
    }
131
#endif
132
 
133
    strlcpy(pathnamecopy, pathname, sizeof(pathnamecopy));
134
 
135
    /* locate filename */
136
    name=strrchr(pathnamecopy+1,'/');
137
    if ( name ) {
138
        *name = 0; 
139
        dir = opendir(pathnamecopy);
140
        *name = '/';
141
        name++;
142
    }
143
    else {
144
        dir = opendir("/");
145
        name = pathnamecopy+1;
146
    }
147
    if (!dir) {
50 theseven 148
        DEBUGF("Failed opening dir");
46 theseven 149
        errno = EIO;
150
        file->busy = false;
151
        return -4;
152
    }
153
 
154
    if(name[0] == 0) {
50 theseven 155
        DEBUGF("Empty file name");
46 theseven 156
        errno = EINVAL;
157
        file->busy = false;
158
        closedir(dir);
159
        return -5;
160
    }
161
 
162
    /* scan dir for name */
163
    while ((entry = readdir(dir))) {
164
        if ( !strcasecmp(name, entry->d_name) ) {
165
            fat_open(IF_MV2(dir->fatdir.file.volume,)
166
                     entry->startcluster,
167
                     &(file->fatfile),
168
                     &(dir->fatdir));
169
            file->size = file->trunc ? 0 : entry->size;
170
            file->attr = entry->attribute;
171
            break;
172
        }
173
    }
174
 
175
    if ( !entry ) {
50 theseven 176
        DEBUGF("Didn't find file %s",name);
46 theseven 177
        if ( file->write && (flags & O_CREAT) ) {
178
            rc = fat_create_file(name,
179
                                 &(file->fatfile),
180
                                 &(dir->fatdir));
181
            if (rc < 0) {
50 theseven 182
                DEBUGF("Couldn't create %s in %s",name,pathnamecopy);
46 theseven 183
                errno = EIO;
184
                file->busy = false;
185
                closedir(dir);
186
                return rc * 10 - 6;
187
            }
188
#ifdef HAVE_DIRCACHE
189
            dircache_add_file(pathname, file->fatfile.firstcluster);
190
#endif
191
            file->size = 0;
192
            file->attr = 0;
193
        }
194
        else {
50 theseven 195
            DEBUGF("Couldn't find %s in %s",name,pathnamecopy);
46 theseven 196
            errno = ENOENT;
197
            file->busy = false;
198
            closedir(dir);
199
            return -7;
200
        }
201
    } else {
202
        if(file->write && (file->attr & FAT_ATTR_DIRECTORY)) {
203
            errno = EISDIR;
204
            file->busy = false;
205
            closedir(dir);
206
            return -8;
207
        }
208
    }
209
    closedir(dir);
210
 
211
    file->cacheoffset = -1;
212
    file->fileoffset = 0;
213
 
214
    if (file->write && (flags & O_APPEND)) {
215
        rc = lseek(fd,0,SEEK_END);
216
        if (rc < 0 )
217
            return rc * 10 - 9;
218
    }
219
 
220
#ifdef HAVE_DIRCACHE
221
    if (file->write)
222
        dircache_bind(fd, pathname);
223
#endif
224
 
225
    return fd;
226
}
227
 
228
int file_open(const char* pathname, int flags)
229
{
230
    /* By default, use the dircache if available. */
231
    return open_internal(pathname, flags, true);
232
}
233
 
234
int close(int fd)
235
{
236
    struct filedesc* file = &openfiles[fd];
237
    int rc = 0;
238
 
50 theseven 239
    DEBUGF("close(%d)", fd);
46 theseven 240
 
241
    if (fd < 0 || fd > MAX_OPEN_FILES-1) {
242
        errno = EINVAL;
243
        return -1;
244
    }
245
    if (!file->busy) {
246
        errno = EBADF;
247
        return -2;
248
    }
249
    if (file->write) {
250
        rc = fsync(fd);
251
        if (rc < 0)
252
            return rc * 10 - 3;
253
#ifdef HAVE_DIRCACHE
254
        dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
255
        dircache_update_filetime(fd);
256
#endif
257
    }
258
 
259
    file->busy = false;
260
    return 0;
261
}
262
 
263
int fsync(int fd)
264
{
265
    struct filedesc* file = &openfiles[fd];
266
    int rc = 0;
267
 
50 theseven 268
    DEBUGF("fsync(%d)", fd);
46 theseven 269
 
270
    if (fd < 0 || fd > MAX_OPEN_FILES-1) {
271
        errno = EINVAL;
272
        return -1;
273
    }
274
    if (!file->busy) {
275
        errno = EBADF;
276
        return -2;
277
    }
278
    if (file->write) {
279
        /* flush sector cache */
280
        if ( file->dirty ) {
281
            rc = flush_cache(fd);
282
            if (rc < 0)
283
            {
284
                /* when failing, try to close the file anyway */
285
                fat_closewrite(&(file->fatfile), file->size, file->attr);
286
                return rc * 10 - 3;
287
            }
288
        }
289
 
290
        /* truncate? */
291
        if (file->trunc) {
292
            rc = ftruncate(fd, file->size);
293
            if (rc < 0)
294
            {
295
                /* when failing, try to close the file anyway */
296
                fat_closewrite(&(file->fatfile), file->size, file->attr);
297
                return rc * 10 - 4;
298
            }
299
        }
300
 
301
        /* tie up all loose ends */
302
        rc = fat_closewrite(&(file->fatfile), file->size, file->attr);
303
        if (rc < 0)
304
            return rc * 10 - 5;
305
    }
306
    return 0;
307
}
308
 
309
int remove(const char* name)
310
{
311
    int rc;
312
    struct filedesc* file;
313
    /* Can't use dircache now, because we need to access the fat structures. */
314
    int fd = open_internal(name, O_WRONLY, false);
315
    if ( fd < 0 )
316
        return fd * 10 - 1;
317
 
318
    file = &openfiles[fd];
319
#ifdef HAVE_DIRCACHE
320
    dircache_remove(name);
321
#endif
322
    rc = fat_remove(&(file->fatfile));
323
    if ( rc < 0 ) {
50 theseven 324
        DEBUGF("Failed removing file: %d", rc);
46 theseven 325
        errno = EIO;
326
        return rc * 10 - 3;
327
    }
328
 
329
    file->size = 0;
330
 
331
    rc = close(fd);
332
    if (rc<0)
333
        return rc * 10 - 4;
334
 
335
    return 0;
336
}
337
 
338
int rename(const char* path, const char* newpath)
339
{
340
    int rc, fd;
341
    DIR* dir;
342
    char* nameptr;
343
    char* dirptr;
344
    struct filedesc* file;
345
    char newpath2[MAX_PATH];
346
 
347
    /* verify new path does not already exist */
348
    /* If it is a directory, errno == EISDIR if the name exists */
349
    fd = open(newpath, O_RDONLY);
350
    if ( fd >= 0 || errno == EISDIR) {
351
        close(fd);
352
        errno = EBUSY;
353
        return -1;
354
    }
355
    close(fd);
356
 
357
    fd = open_internal(path, O_RDONLY, false);
358
    if ( fd < 0 ) {
359
        errno = EIO;
360
        return fd * 10 - 2;
361
    }
362
 
363
    /* extract new file name */
364
    nameptr = strrchr(newpath,'/');
365
    if (nameptr)
366
        nameptr++;
367
    else {
368
        close(fd);
369
        return - 3;
370
    }
371
 
372
    /* Extract new path */
373
    strcpy(newpath2, newpath);
374
 
375
    dirptr = strrchr(newpath2,'/');
376
    if(dirptr)
377
        *dirptr = 0;
378
    else {
379
        close(fd);
380
        return - 4;
381
    }
382
 
383
    dirptr = newpath2;
384
 
385
    if(strlen(dirptr) == 0) {
386
        dirptr = "/";
387
    }
388
 
389
    dir = opendir(dirptr);
390
    if(!dir) {
391
        close(fd);
392
        return - 5;
393
    }
394
 
395
    file = &openfiles[fd];
396
 
397
    rc = fat_rename(&file->fatfile, &dir->fatdir, nameptr,
398
                    file->size, file->attr);
399
#ifdef HAVE_MULTIVOLUME
400
    if ( rc == -1) {
401
        close(fd);
402
        closedir(dir);
50 theseven 403
        DEBUGF("Failed renaming file across volumnes: %d", rc);
46 theseven 404
        errno = EXDEV;
405
        return -6;
406
    }
407
#endif
408
    if ( rc < 0 ) {
409
        close(fd);
410
        closedir(dir);
50 theseven 411
        DEBUGF("Failed renaming file: %d", rc);
46 theseven 412
        errno = EIO;
413
        return rc * 10 - 7;
414
    }
415
 
416
#ifdef HAVE_DIRCACHE
417
    dircache_rename(path, newpath);
418
#endif
419
 
420
    rc = close(fd);
421
    if (rc<0) {
422
        closedir(dir);
423
        errno = EIO;
424
        return rc * 10 - 8;
425
    }
426
 
427
    rc = closedir(dir);
428
    if (rc<0) {
429
        errno = EIO;
430
        return rc * 10 - 9;
431
    }
432
 
433
    return 0;
434
}
435
 
436
int ftruncate(int fd, off_t size)
437
{
438
    int rc, sector;
439
    struct filedesc* file = &openfiles[fd];
440
 
441
    sector = size / SECTOR_SIZE;
442
    if (size % SECTOR_SIZE)
443
        sector++;
444
 
445
    rc = fat_seek(&(file->fatfile), sector);
446
    if (rc < 0) {
447
        errno = EIO;
448
        return rc * 10 - 1;
449
    }
450
 
451
    rc = fat_truncate(&(file->fatfile));
452
    if (rc < 0) {
453
        errno = EIO;
454
        return rc * 10 - 2;
455
    }
456
 
457
    file->size = size;
458
#ifdef HAVE_DIRCACHE
459
    dircache_update_filesize(fd, size, file->fatfile.firstcluster);
460
#endif
461
 
462
    return 0;
463
}
464
 
465
static int flush_cache(int fd)
466
{
467
    int rc;
468
    struct filedesc* file = &openfiles[fd];
469
    long sector = file->fileoffset / SECTOR_SIZE;
470
 
50 theseven 471
    DEBUGF("Flushing dirty sector cache");
46 theseven 472
 
473
    /* make sure we are on correct sector */
474
    rc = fat_seek(&(file->fatfile), sector);
475
    if ( rc < 0 )
476
        return rc * 10 - 3;
477
 
478
    rc = fat_readwrite(&(file->fatfile), 1, file->cache, true );
479
 
480
    if ( rc < 0 ) {
481
        if(file->fatfile.eof)
482
            errno = ENOSPC;
483
 
484
        return rc * 10 - 2;
485
    }
486
 
487
    file->dirty = false;
488
 
489
    return 0;
490
}
491
 
492
static int readwrite(int fd, void* buf, long count, bool write)
493
{
494
    long sectors;
495
    long nread=0;
496
    struct filedesc* file;
497
    int rc;
498
 
499
    if (fd < 0 || fd > MAX_OPEN_FILES-1) {
500
        errno = EINVAL;
501
        return -1;
502
    }
503
 
504
    file = &openfiles[fd];
505
 
506
    if ( !file->busy ) {
507
        errno = EBADF;
508
        return -1;
509
    }
510
 
511
    if(file->attr & FAT_ATTR_DIRECTORY) {
512
        errno = EISDIR;
513
        return -1;
514
    }
515
 
50 theseven 516
    DEBUGF( "readwrite(%d,%lx,%ld,%s)",
46 theseven 517
             fd,(long)buf,count,write?"write":"read");
518
 
519
    /* attempt to read past EOF? */
520
    if (!write && count > file->size - file->fileoffset)
521
        count = file->size - file->fileoffset;
522
 
523
    /* any head bytes? */
524
    if ( file->cacheoffset != -1 ) {
525
        int offs = file->cacheoffset;
526
        int headbytes = MIN(count, SECTOR_SIZE - offs);
527
 
528
        if (write) {
529
            memcpy( file->cache + offs, buf, headbytes );
530
            file->dirty = true;
531
        }
532
        else {
533
            memcpy( buf, file->cache + offs, headbytes );
534
        }
535
 
536
        if (offs + headbytes == SECTOR_SIZE) {
537
            if (file->dirty) {
538
                rc = flush_cache(fd);
539
                if ( rc < 0 ) {
540
                    errno = EIO;
541
                    return rc * 10 - 2;
542
                }
543
            }
544
            file->cacheoffset = -1;
545
        }
546
        else {
547
            file->cacheoffset += headbytes;
548
        }
549
 
550
        nread = headbytes;
551
        count -= headbytes;
552
    }
553
 
554
    /* If the buffer has been modified, either it has been flushed already
555
     * (if (offs+headbytes == SECTOR_SIZE)...) or does not need to be (no
556
     * more data to follow in this call). Do NOT flush here. */
557
 
558
    /* read/write whole sectors right into/from the supplied buffer */
559
    sectors = count / SECTOR_SIZE;
560
    if ( sectors ) {
561
        rc = fat_readwrite(&(file->fatfile), sectors,
562
            (unsigned char*)buf+nread, write );
563
        if ( rc < 0 ) {
50 theseven 564
            DEBUGF("Failed read/writing %ld sectors",sectors);
46 theseven 565
            errno = EIO;
566
            if(write && file->fatfile.eof) {
50 theseven 567
                DEBUGF("No space left on device");
46 theseven 568
                errno = ENOSPC;
569
            } else {
570
                file->fileoffset += nread;
571
            }
572
            file->cacheoffset = -1;
573
            /* adjust file size to length written */
574
            if ( write && file->fileoffset > file->size )
575
            {
576
                file->size = file->fileoffset;
577
#ifdef HAVE_DIRCACHE
578
                dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
579
#endif
580
            }
581
            return nread ? nread : rc * 10 - 4;
582
        }
583
        else {
584
            if ( rc > 0 ) {
585
                nread += rc * SECTOR_SIZE;
586
                count -= sectors * SECTOR_SIZE;
587
 
588
                /* if eof, skip tail bytes */
589
                if ( rc < sectors )
590
                    count = 0;
591
            }
592
            else {
593
                /* eof */
594
                count=0;
595
            }
596
 
597
            file->cacheoffset = -1;
598
        }
599
    }
600
 
601
    /* any tail bytes? */
602
    if ( count ) {
603
        if (write) {
604
            if ( file->fileoffset + nread < file->size ) {
605
                /* sector is only partially filled. copy-back from disk */
50 theseven 606
                DEBUGF("Copy-back tail cache");
46 theseven 607
                rc = fat_readwrite(&(file->fatfile), 1, file->cache, false );
608
                if ( rc < 0 ) {
50 theseven 609
                    DEBUGF("Failed writing");
46 theseven 610
                    errno = EIO;
611
                    file->fileoffset += nread;
612
                    file->cacheoffset = -1;
613
                    /* adjust file size to length written */
614
                    if ( file->fileoffset > file->size )
615
                    {
616
                        file->size = file->fileoffset;
617
#ifdef HAVE_DIRCACHE
618
                        dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
619
#endif
620
                    }
621
                    return nread ? nread : rc * 10 - 5;
622
                }
623
                /* seek back one sector to put file position right */
624
                rc = fat_seek(&(file->fatfile), 
625
                              (file->fileoffset + nread) /
626
                              SECTOR_SIZE);
627
                if ( rc < 0 ) {
50 theseven 628
                    DEBUGF("fat_seek() failed");
46 theseven 629
                    errno = EIO;
630
                    file->fileoffset += nread;
631
                    file->cacheoffset = -1;
632
                    /* adjust file size to length written */
633
                    if ( file->fileoffset > file->size )
634
                    {
635
                        file->size = file->fileoffset;
636
#ifdef HAVE_DIRCACHE
637
                        dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
638
#endif
639
                    }
640
                    return nread ? nread : rc * 10 - 6;
641
                }
642
            }
643
            memcpy( file->cache, (unsigned char*)buf + nread, count );
644
            file->dirty = true;
645
        }
646
        else {
647
            rc = fat_readwrite(&(file->fatfile), 1, file->cache,false);
648
            if (rc < 1 ) {
50 theseven 649
                DEBUGF("Failed caching sector");
46 theseven 650
                errno = EIO;
651
                file->fileoffset += nread;
652
                file->cacheoffset = -1;
653
                return nread ? nread : rc * 10 - 7;
654
            }
655
            memcpy( (unsigned char*)buf + nread, file->cache, count );
656
        }
657
 
658
        nread += count;
659
        file->cacheoffset = count;
660
    }
661
 
662
    file->fileoffset += nread;
50 theseven 663
    DEBUGF("fileoffset: %ld", file->fileoffset);
46 theseven 664
 
665
    /* adjust file size to length written */
666
    if ( write && file->fileoffset > file->size )
667
    {
668
        file->size = file->fileoffset;
669
#ifdef HAVE_DIRCACHE
670
        dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
671
#endif
672
    }
673
 
674
    return nread;
675
}
676
 
677
ssize_t write(int fd, const void* buf, size_t count)
678
{
679
    if (!openfiles[fd].write) {
680
        errno = EACCES;
681
        return -1;
682
    }
683
    return readwrite(fd, (void *)buf, count, true);
684
}
685
 
686
ssize_t read(int fd, void* buf, size_t count)
687
{
688
    return readwrite(fd, buf, count, false);
689
}
690
 
691
 
692
off_t lseek(int fd, off_t offset, int whence)
693
{
694
    off_t pos;
695
    long newsector;
696
    long oldsector;
697
    int sectoroffset;
698
    int rc;
699
    struct filedesc* file = &openfiles[fd];
700
 
50 theseven 701
    DEBUGF("lseek(%d,%ld,%d)",fd,offset,whence);
46 theseven 702
 
703
    if (fd < 0 || fd > MAX_OPEN_FILES-1) {
704
        errno = EINVAL;
705
        return -1;
706
    }
707
    if ( !file->busy ) {
708
        errno = EBADF;
709
        return -1;
710
    }
711
 
712
    switch ( whence ) {
713
        case SEEK_SET:
714
            pos = offset;
715
            break;
716
 
717
        case SEEK_CUR:
718
            pos = file->fileoffset + offset;
719
            break;
720
 
721
        case SEEK_END:
722
            pos = file->size + offset;
723
            break;
724
 
725
        default:
726
            errno = EINVAL;
727
            return -2;
728
    }
729
    if ((pos < 0) || (pos > file->size)) {
730
        errno = EINVAL;
731
        return -3;
732
    }
733
 
734
    /* new sector? */
735
    newsector = pos / SECTOR_SIZE;
736
    oldsector = file->fileoffset / SECTOR_SIZE;
737
    sectoroffset = pos % SECTOR_SIZE;
738
 
739
    if ( (newsector != oldsector) ||
740
         ((file->cacheoffset==-1) && sectoroffset) ) {
741
 
742
        if ( newsector != oldsector ) {
743
            if (file->dirty) {
744
                rc = flush_cache(fd);
745
                if (rc < 0)
746
                    return rc * 10 - 5;
747
            }
748
 
749
            rc = fat_seek(&(file->fatfile), newsector);
750
            if ( rc < 0 ) {
751
                errno = EIO;
752
                return rc * 10 - 4;
753
            }
754
        }
755
        if ( sectoroffset ) {
756
            rc = fat_readwrite(&(file->fatfile), 1, file->cache ,false);
757
            if ( rc < 0 ) {
758
                errno = EIO;
759
                return rc * 10 - 6;
760
            }
761
            file->cacheoffset = sectoroffset;
762
        }
763
        else
764
            file->cacheoffset = -1;
765
    }
766
    else
767
        if ( file->cacheoffset != -1 )
768
            file->cacheoffset = sectoroffset;
769
 
770
    file->fileoffset = pos;
771
 
772
    return pos;
773
}
774
 
775
off_t filesize(int fd)
776
{
777
    struct filedesc* file = &openfiles[fd];
778
 
779
    if (fd < 0 || fd > MAX_OPEN_FILES-1) {
780
        errno = EINVAL;
781
        return -1;
782
    }
783
    if ( !file->busy ) {
784
        errno = EBADF;
785
        return -1;
786
    }
787
 
788
    return file->size;
789
}
790
 
791
 
792
#ifdef HAVE_HOTSWAP
793
/* release all file handles on a given volume "by force", to avoid leaks */
794
int release_files(int volume)
795
{
796
    struct filedesc* pfile = openfiles;
797
    int fd;
798
    int closed = 0;
799
    for ( fd=0; fd<MAX_OPEN_FILES; fd++, pfile++)
800
    {
801
#ifdef HAVE_MULTIVOLUME
802
        if (pfile->fatfile.volume == volume)
803
#else
804
        (void)volume;
805
#endif
806
        {
807
            pfile->busy = false; /* mark as available, no further action */
808
            closed++;
809
        }
810
    }
811
    return closed; /* return how many we did */
812
}
813
#endif /* #ifdef HAVE_HOTSWAP */