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