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