Subversion Repositories freemyipod

Rev

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

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