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