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