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