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