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