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