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