]> git.ozlabs.org Git - yaboot.git/blob - second/fs_ext2.c
Commit yaboot 1.3.4-pre3
[yaboot.git] / second / fs_ext2.c
1 /* ext2 filesystem
2    
3    Copyright (C) 1999 Benjamin Herrenschmidt
4
5    Adapted from quik/silo
6
7    Copyright (C) 1996 Maurizio Plaza
8                  1996 Jakub Jelinek
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24    Note: This version is way too slow due to the way we use bmap. This
25          should be replaced by an iterator.
26 */
27
28 #include "ctype.h"
29 #include "types.h"
30 #include "stddef.h"
31 #include "file.h"
32 #include "prom.h"
33 #include "string.h"
34 #include "partition.h"
35 #include "fs.h"
36 #include "errors.h"
37 #include "debug.h"
38
39 #define FAST_VERSION
40 #define MAX_READ_RANGE  256
41 #undef VERBOSE_DEBUG
42
43 typedef int FILE;
44 #include "linux/ext2_fs.h"
45 #include "ext2fs/ext2fs.h"
46
47 static int ext2_open(   struct boot_file_t*     file,
48                         const char*             dev_name,
49                         struct partition_t*     part,
50                         const char*             file_name);
51 static int ext2_read(   struct boot_file_t*     file,
52                         unsigned int            size,
53                         void*                   buffer);
54 static int ext2_seek(   struct boot_file_t*     file,
55                         unsigned int            newpos);
56 static int ext2_close(  struct boot_file_t*     file);
57
58 struct fs_t ext2_filesystem =
59 {
60     "ext2",
61     ext2_open,
62     ext2_read,
63     ext2_seek,
64     ext2_close
65 };
66
67 /* IO manager structure for the ext2 library */
68  
69 static errcode_t linux_open (const char *name, int flags, io_channel * channel);
70 static errcode_t linux_close (io_channel channel);
71 static errcode_t linux_set_blksize (io_channel channel, int blksize);
72 static errcode_t linux_read_blk (io_channel channel, unsigned long block, int count, void *data);
73 static errcode_t linux_write_blk (io_channel channel, unsigned long block, int count, const void *data);
74 static errcode_t linux_flush (io_channel channel);
75
76 static struct struct_io_manager struct_linux_manager =
77 {
78     EXT2_ET_MAGIC_IO_MANAGER,
79     "linux I/O Manager",
80     linux_open,
81     linux_close,
82     linux_set_blksize,
83     linux_read_blk,
84     linux_write_blk,
85     linux_flush
86 };
87
88 static io_manager linux_io_manager = &struct_linux_manager;
89
90 /* Currently, we have a mess between what is in the file structure
91  * and what is stored globally here. I'll clean this up later
92  */
93 static int opened = 0;          /* We can't open twice ! */
94 static unsigned int bs;         /* Blocksize */
95 static unsigned long long doff; /* Byte offset where partition starts */
96 static ino_t root,cwd;
97 static ext2_filsys fs = 0;
98 static struct boot_file_t* cur_file;
99 static char *block_buffer = NULL;
100
101 #ifdef FAST_VERSION
102 static unsigned long read_range_start;
103 static unsigned long read_range_count;
104 static unsigned long read_last_logical;
105 static unsigned long read_total;
106 static unsigned long read_max;
107 static struct boot_file_t* read_cur_file;
108 static errcode_t read_result;
109 static char* read_buffer;
110
111 static int read_dump_range(void);
112 static int read_iterator(ext2_filsys fs, blk_t *blocknr, int lg_block, void *private);
113 #else /* FAST_VERSION */
114 static struct ext2_inode cur_inode;
115 #endif /* FAST_VERSION */
116
117 void com_err (const char *a, long i, const char *fmt,...)
118 {
119     prom_printf ((char *) fmt);
120 }
121
122 static int
123 ext2_open(      struct boot_file_t*     file,
124                 const char*             dev_name,
125                 struct partition_t*     part,
126                 const char*             file_name)
127 {
128         int result = 0;
129         int error = FILE_ERR_NOTFOUND;
130         static char buffer[1024];
131         int ofopened = 0;
132         
133         DEBUG_ENTER;
134         DEBUG_OPEN;
135
136         if (opened) {
137                 prom_printf("ext2_open() : fs busy\n");
138                 DEBUG_LEAVE(FILE_ERR_FSBUSY);
139                 return FILE_ERR_FSBUSY;
140         }
141         if (file->device_kind != FILE_DEVICE_BLOCK) {
142                 prom_printf("Can't open ext2 filesystem on non-block device\n");
143                 DEBUG_LEAVE(FILE_ERR_BADDEV);
144                 return FILE_ERR_BADDEV;
145         }
146
147         fs = NULL;
148         
149         /* We don't care too much about the device block size since we run
150          * thru the deblocker. We may have to change that is we plan to be
151          * compatible with older versions of OF
152          */
153         bs = 1024;
154         doff = 0;
155         if (part)
156             doff = (unsigned long long)(part->part_start) * part->blocksize;
157         cur_file = file;
158
159
160         DEBUG_F("partition offset: %Lu\n", doff);
161
162         /* Open the OF device for the entire disk */
163         strncpy(buffer, dev_name, 1020);
164         strcat(buffer, ":0");
165
166         DEBUG_F("<%s>\n", buffer);
167
168         file->of_device = prom_open(buffer);
169
170         DEBUG_F("file->of_device = %p\n", file->of_device);
171
172         if (file->of_device == PROM_INVALID_HANDLE) {
173
174                 DEBUG_F("Can't open device %p\n", file->of_device);
175                 DEBUG_LEAVE(FILE_IOERR);
176                 return FILE_IOERR;
177         }
178         ofopened = 1;
179         
180         /* Open the ext2 filesystem */
181         result = ext2fs_open (buffer, EXT2_FLAG_RW, 0, 0, linux_io_manager, &fs);
182         if (result) {
183
184             if(result == EXT2_ET_BAD_MAGIC)
185             {
186                 DEBUG_F( "ext2fs_open returned bad magic loading file %p\n",
187                          file );
188             }
189             else
190             {
191                 DEBUG_F( "ext2fs_open error #%d while loading file %s\n",
192                          result, file_name);
193             }
194             error = FILE_ERR_BAD_FSYS;
195             goto bail;
196         }
197
198         /* Allocate the block buffer */
199         block_buffer = malloc(fs->blocksize * 2);
200         if (!block_buffer) {
201
202             DEBUG_F("ext2fs: can't alloc block buffer (%d bytes)\n", fs->blocksize * 2);
203             error = FILE_IOERR;
204             goto bail;
205         }
206         
207         /* Lookup file by pathname */
208         root = cwd = EXT2_ROOT_INO;
209         result = ext2fs_namei_follow(fs, root, cwd, file_name, &file->inode);
210         if (result) {
211
212             DEBUG_F("ext2fs_namei error #%d while loading file %s\n", result, file_name);
213             if (result == EXT2_ET_SYMLINK_LOOP)
214                  error = FILE_ERR_SYMLINK_LOOP;
215             else if (result == EXT2_ET_FILE_NOT_FOUND)
216                  error = FILE_ERR_NOTFOUND;
217             else
218                  error = FILE_IOERR;
219             goto bail;
220         }
221
222 #if 0
223         result = ext2fs_follow_link(fs, root, cwd,  file->inode, &file->inode);
224         if (result) {
225
226             DEBUG_F("ext2fs_follow_link error #%d while loading file %s\n", result, file_name);
227             error = FILE_ERR_NOTFOUND;
228             goto bail;
229         }
230 #endif  
231
232 #ifndef FAST_VERSION
233         result = ext2fs_read_inode(fs, file->inode, &cur_inode);
234         if (result) {
235
236             DEBUG_F("ext2fs_read_inode error #%d while loading file %s\n", result, file_name);
237             if (result == EXT2_ET_FILE_TOO_BIG)
238                  error = FILE_ERR_LENGTH;
239             else if (result == EXT2_ET_LLSEEK_FAILED)
240                  error = FILE_CANT_SEEK;
241             else if (result ==  EXT2_ET_FILE_NOT_FOUND)
242                  error = FILE_ERR_NOTFOUND;
243             else
244                  error = FILE_IOERR;
245             goto bail;
246         }
247 #endif /* FAST_VERSION */
248         file->pos = 0;
249         
250         opened = 1;
251 bail:
252         if (!opened) {
253             if (fs)
254                 ext2fs_close(fs);
255             fs = NULL;
256             if (ofopened)
257                 prom_close(file->of_device);
258             if (block_buffer)
259                 free(block_buffer);
260             block_buffer = NULL;
261             cur_file = NULL;
262             
263             DEBUG_LEAVE_F(error);
264             return error;
265         }
266
267         DEBUG_LEAVE(FILE_ERR_OK);
268         return FILE_ERR_OK;
269 }
270
271 #ifdef FAST_VERSION
272
273 static int
274 read_dump_range(void)
275 {
276         int count = read_range_count;
277         int size;
278
279 #ifdef VERBOSE_DEBUG
280         DEBUG_F("   dumping range: start: 0x%x count: 0x%x\n",
281                 read_range_count, read_range_start);
282 #endif
283         /* Check if we need to handle a special case for the last block */
284         if ((count * bs) > read_max)
285                 count--;
286         if (count) {
287                 size = count * bs;      
288                 read_result = io_channel_read_blk(fs->io, read_range_start, count, read_buffer);
289                 if (read_result)
290                         return BLOCK_ABORT;
291                 read_buffer += size;
292                 read_max -= size;
293                 read_total += size;
294                 read_cur_file->pos += size;
295                 read_range_count -= count;
296                 read_range_start += count;
297                 read_last_logical += count;
298         }       
299         /* Handle remaining block */
300         if (read_max && read_range_count) {
301                 read_result = io_channel_read_blk(fs->io, read_range_start, 1, block_buffer);
302                 if (read_result)
303                         return BLOCK_ABORT;
304                 memcpy(read_buffer, block_buffer, read_max);
305                 read_cur_file->pos += read_max;
306                 read_total += read_max;
307                 read_max = 0;
308         }
309         read_range_count = read_range_start = 0;
310
311         return (read_max == 0) ? BLOCK_ABORT : 0;
312 }
313
314 static int
315 read_iterator(ext2_filsys fs, blk_t *blocknr, int lg_block, void *private)
316 {
317 #ifdef VERBOSE_DEBUG
318     DEBUG_F("read_it: p_bloc: 0x%x, l_bloc: 0x%x, f_pos: 0x%x, rng_pos: 0x%x   ",
319         *blocknr, lg_block, read_cur_file->pos, read_last_logical);
320 #endif
321     if (lg_block < 0) {
322 #ifdef VERBOSE_DEBUG
323         DEBUG_F(" <skip lg>\n");
324 #endif
325         return 0;
326     }
327
328     /* If we have not reached the start block yet, we skip */
329     if (lg_block < read_cur_file->pos / bs) {
330 #ifdef VERBOSE_DEBUG
331         DEBUG_F(" <skip pos>\n");
332 #endif
333         return 0;
334     }
335
336     /* If block is contiguous to current range, just extend range,
337      * exit if we pass the remaining bytes count to read
338      */
339     if (read_range_start && read_range_count < MAX_READ_RANGE
340         && (*blocknr == read_range_start + read_range_count)
341         && (lg_block == read_last_logical + read_range_count)) {
342 #ifdef VERBOSE_DEBUG
343         DEBUG_F(" block in range\n");
344 #endif
345         ++read_range_count;
346         return ((read_range_count * bs) >= read_max) ? BLOCK_ABORT : 0;
347     }
348     
349     /* Range doesn't match. Dump existing range */
350     if (read_range_start) {
351 #ifdef VERBOSE_DEBUG
352         DEBUG_F(" calling dump range \n");
353 #endif
354         if (read_dump_range())
355                 return BLOCK_ABORT;
356     }
357
358     /* Here we handle holes in the file */
359     if (lg_block && lg_block != read_last_logical) {
360         unsigned long nzero;
361 #ifdef VERBOSE_DEBUG
362         DEBUG_F(" hole from lg_bloc 0x%x\n", read_last_logical);
363 #endif
364         if (read_cur_file->pos % bs) {
365                 int offset = read_cur_file->pos % bs;
366                 int size = bs - offset;
367                 if (size > read_max)
368                         size = read_max;
369                 memset(read_buffer, 0, size);
370                 read_max -= size;
371                 read_total += size;
372                 read_buffer += size;
373                 read_cur_file->pos += size;
374                 ++read_last_logical;
375                 if (read_max == 0)
376                         return BLOCK_ABORT;
377         }
378         nzero = (lg_block - read_last_logical) * bs;
379         if (nzero) {
380                 if (nzero > read_max)
381                         nzero = read_max;
382                 memset(read_buffer, 0, nzero);
383                 read_max -= nzero;
384                 read_total += nzero;
385                 read_buffer += nzero;
386                 read_cur_file->pos += nzero;
387                 if (read_max == 0)
388                         return BLOCK_ABORT;
389         }
390         read_last_logical = lg_block;
391     }
392
393      /* If we are not aligned, handle that case */
394     if (read_cur_file->pos % bs) {
395         int offset = read_cur_file->pos % bs;
396         int size = bs - offset;
397 #ifdef VERBOSE_DEBUG
398         DEBUG_F(" handle unaligned start\n");
399 #endif
400         read_result = io_channel_read_blk(fs->io, *blocknr, 1, block_buffer);
401         if (read_result)
402             return BLOCK_ABORT;
403         if (size > read_max)
404                 size = read_max;
405         memcpy(read_buffer, block_buffer + offset, size);
406         read_cur_file->pos += size;
407         read_max -= size;
408         read_total += size;
409         read_buffer += size;
410         read_last_logical = lg_block + 1;
411         return (read_max == 0) ? BLOCK_ABORT : 0;
412     }
413
414    /* If there is still a physical block to add, then create a new range */
415     if (*blocknr) {
416 #ifdef VERBOSE_DEBUG
417         DEBUG_F(" new range\n");
418 #endif
419          read_range_start = *blocknr;
420          read_range_count = 1;
421          return (bs >= read_max) ? BLOCK_ABORT : 0;
422     }
423     
424 #ifdef VERBOSE_DEBUG
425         DEBUG_F("\n");
426 #endif
427     return 0;
428 }
429
430 #endif /* FAST_VERSION */
431
432 static int
433 ext2_read(      struct boot_file_t*     file,
434                 unsigned int            size,
435                 void*                   buffer)
436 {
437         errcode_t retval;
438
439 #ifdef FAST_VERSION
440         if (!opened)
441             return FILE_IOERR;
442
443
444         DEBUG_F("ext_read() from pos 0x%Lx, size: 0x%ux\n", file->pos, size);
445
446
447         read_cur_file = file;
448         read_range_start = 0;
449         read_range_count = 0;
450         read_last_logical = file->pos / bs;
451         read_total = 0;
452         read_max = size;
453         read_buffer = (unsigned char*)buffer;
454         read_result = 0;
455     
456         retval = ext2fs_block_iterate(fs, file->inode, 0, 0, read_iterator, 0);
457         if (retval == BLOCK_ABORT)
458                 retval = read_result;
459         if (!retval && read_range_start) {
460 #ifdef VERBOSE_DEBUG
461                 DEBUG_F("on exit: range_start is 0x%x, calling dump...\n",
462                         read_range_start);
463 #endif
464                 read_dump_range();
465                 retval = read_result;
466         }
467         if (retval)
468                 prom_printf ("ext2: i/o error %ld in read\n", (long) retval);
469                 
470         return read_total;
471
472 #else /* FAST_VERSION */
473         int status;
474         unsigned int read = 0;
475         
476         if (!opened)
477             return FILE_IOERR;
478
479
480         DEBUG_F("ext_read() from pos 0x%x, size: 0x%x\n", file->pos, size);
481
482
483         while(size) {   
484             blk_t fblock = file->pos / bs;
485             blk_t pblock;
486             unsigned int blkorig, s, b;
487         
488             pblock = 0;
489             status = ext2fs_bmap(fs, file->inode, &cur_inode,
490                         block_buffer, 0, fblock, &pblock);
491             if (status) {
492
493                 DEBUG_F("ext2fs_bmap(fblock:%d) return: %d\n", fblock, status);
494                 return read;
495             }
496             blkorig = fblock * bs;
497             b = file->pos - blkorig;
498             s = ((bs - b) > size) ? size : (bs - b);
499             if (pblock) {
500                 unsigned long long pos =
501                   ((unsigned long long)pblock) * (unsigned long long)bs;
502                 pos += doff;
503                 prom_lseek(file->of_device, pos);
504                 status = prom_read(file->of_device, block_buffer, bs);
505                 if (status != bs) {
506                     prom_printf("ext2: io error in read, ex: %d, got: %d\n",
507                         bs, status);
508                     return read;
509                 }
510             } else
511                 memset(block_buffer, 0, bs);
512
513            memcpy(buffer, block_buffer + b, s);
514            read += s;
515            size -= s;
516            buffer += s;
517            file->pos += s;
518         }
519         return read;
520 #endif /* FAST_VERSION */       
521 }
522
523 static int
524 ext2_seek(      struct boot_file_t*     file,
525                 unsigned int            newpos)
526 {
527         if (!opened)
528                 return FILE_CANT_SEEK;
529
530         file->pos = newpos;
531         return FILE_ERR_OK;
532 }
533
534 static int
535 ext2_close(     struct boot_file_t*     file)
536 {
537         if (!opened)
538                 return FILE_IOERR;
539
540         if (block_buffer)
541                 free(block_buffer);
542         block_buffer = NULL;
543
544         if (fs)
545                 ext2fs_close(fs);
546         fs = NULL;
547         
548         prom_close(file->of_device);
549         DEBUG_F("ext2_close called\n");
550
551         opened = 0;
552             
553         return 0;
554 }
555
556 static errcode_t linux_open (const char *name, int flags, io_channel * channel)
557 {
558     io_channel io;
559
560
561     if (!name)
562         return EXT2_ET_BAD_DEVICE_NAME;
563     io = (io_channel) malloc (sizeof (struct struct_io_channel));
564     if (!io)
565         return EXT2_ET_BAD_DEVICE_NAME;
566     memset (io, 0, sizeof (struct struct_io_channel));
567     io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
568     io->manager = linux_io_manager;
569     io->name = (char *) malloc (strlen (name) + 1);
570     strcpy (io->name, name);
571     io->block_size = bs;
572     io->read_error = 0;
573     io->write_error = 0;
574     *channel = io;
575
576     return 0;
577 }
578
579 static errcode_t linux_close (io_channel channel)
580 {
581     free(channel);
582     return 0;
583 }
584
585 static errcode_t linux_set_blksize (io_channel channel, int blksize)
586 {
587     channel->block_size = bs = blksize;
588     if (block_buffer) {
589         free(block_buffer);
590         block_buffer = malloc(bs * 2);
591     }   
592     return 0;
593 }
594
595 static errcode_t linux_read_blk (io_channel channel, unsigned long block, int count, void *data)
596 {
597     int size;
598     unsigned long long tempb;
599
600     if (count == 0)
601         return 0;
602     
603     tempb = (((unsigned long long) block) *
604         ((unsigned long long)bs)) + (unsigned long long)doff;
605     size = (count < 0) ? -count : count * bs;
606     prom_lseek(cur_file->of_device, tempb);
607     if (prom_read(cur_file->of_device, data, size) != size) {
608         DEBUG_F("\nRead error on block %ld\n", block);
609         return EXT2_ET_SHORT_READ;
610     }
611     return 0;
612 }
613
614 static errcode_t linux_write_blk (io_channel channel, unsigned long block, int count, const void *data)
615 {
616     return 0;
617 }
618
619 static errcode_t linux_flush (io_channel channel)
620 {
621     return 0;
622 }