Whamcloud - gitweb
Implement the ability for the test_io mechanism to abort after n reads
[tools/e2fsprogs.git] / lib / ext2fs / inode.c
1 /*
2  * inode.c --- utility routines to read and write inodes
3  * 
4  * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
5  *
6  * %Begin-Header%
7  * This file may be redistributed under the terms of the GNU Public
8  * License.
9  * %End-Header%
10  */
11
12 #include <stdio.h>
13 #include <string.h>
14 #if HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #if HAVE_SYS_STAT_H
18 #include <sys/stat.h>
19 #endif
20 #if HAVE_SYS_TYPES_H
21 #include <sys/types.h>
22 #endif
23
24 #include "ext2_fs.h"
25 #include "ext2fsP.h"
26 #include "e2image.h"
27
28 struct ext2_struct_inode_scan {
29         errcode_t               magic;
30         ext2_filsys             fs;
31         ext2_ino_t              current_inode;
32         blk_t                   current_block;
33         dgrp_t                  current_group;
34         ext2_ino_t              inodes_left;
35         blk_t                   blocks_left;
36         dgrp_t                  groups_left;
37         blk_t                   inode_buffer_blocks;
38         char *                  inode_buffer;
39         int                     inode_size;
40         char *                  ptr;
41         int                     bytes_left;
42         char                    *temp_buffer;
43         errcode_t               (*done_group)(ext2_filsys fs,
44                                               ext2_inode_scan scan,
45                                               dgrp_t group,
46                                               void * priv_data);
47         void *                  done_group_data;
48         int                     bad_block_ptr;
49         int                     scan_flags;
50         int                     reserved[6];
51 };
52
53 /*
54  * This routine flushes the icache, if it exists.
55  */
56 errcode_t ext2fs_flush_icache(ext2_filsys fs)
57 {
58         int     i;
59         
60         if (!fs->icache)
61                 return 0;
62
63         for (i=0; i < fs->icache->cache_size; i++)
64                 fs->icache->cache[i].ino = 0;
65
66         return 0;
67 }
68
69 static errcode_t create_icache(ext2_filsys fs)
70 {
71         errcode_t       retval;
72         
73         if (fs->icache)
74                 return 0;
75         retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache);
76         if (retval)
77                 return retval;
78
79         memset(fs->icache, 0, sizeof(struct ext2_inode_cache));
80         retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer);
81         if (retval) {
82                 ext2fs_free_mem(&fs->icache);
83                 return retval;
84         }
85         fs->icache->buffer_blk = 0;
86         fs->icache->cache_last = -1;
87         fs->icache->cache_size = 4;
88         fs->icache->refcount = 1;
89         retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache_ent)
90                                 * fs->icache->cache_size,
91                                 &fs->icache->cache);
92         if (retval) {
93                 ext2fs_free_mem(&fs->icache->buffer);
94                 ext2fs_free_mem(&fs->icache);
95                 return retval;
96         }
97         ext2fs_flush_icache(fs);
98         return 0;
99 }
100
101 errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
102                                  ext2_inode_scan *ret_scan)
103 {
104         ext2_inode_scan scan;
105         errcode_t       retval;
106         errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks);
107
108         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
109
110         /*
111          * If fs->badblocks isn't set, then set it --- since the inode
112          * scanning functions require it.
113          */
114         if (fs->badblocks == 0) {
115                 /*
116                  * Temporarly save fs->get_blocks and set it to zero,
117                  * for compatibility with old e2fsck's.
118                  */
119                 save_get_blocks = fs->get_blocks;
120                 fs->get_blocks = 0;
121                 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
122                 if (retval && fs->badblocks) {
123                         ext2fs_badblocks_list_free(fs->badblocks);
124                         fs->badblocks = 0;
125                 }
126                 fs->get_blocks = save_get_blocks;
127         }
128
129         retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan);
130         if (retval)
131                 return retval;
132         memset(scan, 0, sizeof(struct ext2_struct_inode_scan));
133
134         scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
135         scan->fs = fs;
136         scan->inode_size = EXT2_INODE_SIZE(fs->super);
137         scan->bytes_left = 0;
138         scan->current_group = 0;
139         scan->groups_left = fs->group_desc_count - 1;
140         scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
141         scan->current_block = scan->fs->
142                 group_desc[scan->current_group].bg_inode_table;
143         scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
144         scan->blocks_left = scan->fs->inode_blocks_per_group;
145         retval = ext2fs_get_mem((size_t) (scan->inode_buffer_blocks * 
146                                           fs->blocksize),
147                                 &scan->inode_buffer);
148         scan->done_group = 0;
149         scan->done_group_data = 0;
150         scan->bad_block_ptr = 0;
151         if (retval) {
152                 ext2fs_free_mem(&scan);
153                 return retval;
154         }
155         retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer);
156         if (retval) {
157                 ext2fs_free_mem(&scan->inode_buffer);
158                 ext2fs_free_mem(&scan);
159                 return retval;
160         }
161         if (scan->fs->badblocks && scan->fs->badblocks->num)
162                 scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
163         *ret_scan = scan;
164         return 0;
165 }
166
167 void ext2fs_close_inode_scan(ext2_inode_scan scan)
168 {
169         if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
170                 return;
171         
172         ext2fs_free_mem(&scan->inode_buffer);
173         scan->inode_buffer = NULL;
174         ext2fs_free_mem(&scan->temp_buffer);
175         scan->temp_buffer = NULL;
176         ext2fs_free_mem(&scan);
177         return;
178 }
179
180 void ext2fs_set_inode_callback(ext2_inode_scan scan,
181                                errcode_t (*done_group)(ext2_filsys fs,
182                                                        ext2_inode_scan scan,
183                                                        dgrp_t group,
184                                                        void * priv_data),
185                                void *done_group_data)
186 {
187         if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
188                 return;
189         
190         scan->done_group = done_group;
191         scan->done_group_data = done_group_data;
192 }
193
194 int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
195                             int clear_flags)
196 {
197         int     old_flags;
198
199         if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
200                 return 0;
201
202         old_flags = scan->scan_flags;
203         scan->scan_flags &= ~clear_flags;
204         scan->scan_flags |= set_flags;
205         return old_flags;
206 }
207
208 /*
209  * This function is called by ext2fs_get_next_inode when it needs to
210  * get ready to read in a new blockgroup.
211  */
212 static errcode_t get_next_blockgroup(ext2_inode_scan scan)
213 {
214         scan->current_group++;
215         scan->groups_left--;
216                         
217         scan->current_block = scan->fs->
218                 group_desc[scan->current_group].bg_inode_table;
219
220         scan->current_inode = scan->current_group *
221                 EXT2_INODES_PER_GROUP(scan->fs->super);
222
223         scan->bytes_left = 0;
224         scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
225         scan->blocks_left = scan->fs->inode_blocks_per_group;
226         return 0;
227 }
228
229 errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
230                                             int group)
231 {
232         scan->current_group = group - 1;
233         scan->groups_left = scan->fs->group_desc_count - group;
234         return get_next_blockgroup(scan);
235 }
236
237 /*
238  * This function is called by get_next_blocks() to check for bad
239  * blocks in the inode table.
240  *
241  * This function assumes that badblocks_list->list is sorted in
242  * increasing order.
243  */
244 static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
245                                             blk_t *num_blocks)
246 {
247         blk_t   blk = scan->current_block;
248         badblocks_list  bb = scan->fs->badblocks;
249
250         /*
251          * If the inode table is missing, then obviously there are no
252          * bad blocks.  :-)
253          */
254         if (blk == 0)
255                 return 0;
256
257         /*
258          * If the current block is greater than the bad block listed
259          * in the bad block list, then advance the pointer until this
260          * is no longer the case.  If we run out of bad blocks, then
261          * we don't need to do any more checking!
262          */
263         while (blk > bb->list[scan->bad_block_ptr]) {
264                 if (++scan->bad_block_ptr >= bb->num) {
265                         scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
266                         return 0;
267                 }
268         }
269
270         /*
271          * If the current block is equal to the bad block listed in
272          * the bad block list, then handle that one block specially.
273          * (We could try to handle runs of bad blocks, but that
274          * only increases CPU efficiency by a small amount, at the
275          * expense of a huge expense of code complexity, and for an
276          * uncommon case at that.)
277          */
278         if (blk == bb->list[scan->bad_block_ptr]) {
279                 scan->scan_flags |= EXT2_SF_BAD_INODE_BLK;
280                 *num_blocks = 1;
281                 if (++scan->bad_block_ptr >= bb->num)
282                         scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
283                 return 0;
284         }
285
286         /*
287          * If there is a bad block in the range that we're about to
288          * read in, adjust the number of blocks to read so that we we
289          * don't read in the bad block.  (Then the next block to read
290          * will be the bad block, which is handled in the above case.)
291          */
292         if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr])
293                 *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk);
294
295         return 0;
296 }
297
298 /*
299  * This function is called by ext2fs_get_next_inode when it needs to
300  * read in more blocks from the current blockgroup's inode table.
301  */
302 static errcode_t get_next_blocks(ext2_inode_scan scan)
303 {
304         blk_t           num_blocks;
305         errcode_t       retval;
306
307         /*
308          * Figure out how many blocks to read; we read at most
309          * inode_buffer_blocks, and perhaps less if there aren't that
310          * many blocks left to read.
311          */
312         num_blocks = scan->inode_buffer_blocks;
313         if (num_blocks > scan->blocks_left)
314                 num_blocks = scan->blocks_left;
315
316         /*
317          * If the past block "read" was a bad block, then mark the
318          * left-over extra bytes as also being bad.
319          */
320         if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) {
321                 if (scan->bytes_left)
322                         scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES;
323                 scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK;
324         }
325
326         /*
327          * Do inode bad block processing, if necessary.
328          */
329         if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) {
330                 retval = check_for_inode_bad_blocks(scan, &num_blocks);
331                 if (retval)
332                         return retval;
333         }
334                 
335         if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) ||
336             (scan->current_block == 0)) {
337                 memset(scan->inode_buffer, 0,
338                        (size_t) num_blocks * scan->fs->blocksize);
339         } else {
340                 retval = io_channel_read_blk(scan->fs->io,
341                                              scan->current_block,
342                                              (int) num_blocks,
343                                              scan->inode_buffer);
344                 if (retval)
345                         return EXT2_ET_NEXT_INODE_READ;
346         }
347         scan->ptr = scan->inode_buffer;
348         scan->bytes_left = num_blocks * scan->fs->blocksize;
349
350         scan->blocks_left -= num_blocks;
351         if (scan->current_block)
352                 scan->current_block += num_blocks;
353         return 0;
354 }
355
356 #if 0
357 /*
358  * Returns 1 if the entire inode_buffer has a non-zero size and
359  * contains all zeros.  (Not just deleted inodes, since that means
360  * that part of the inode table was used at one point; we want all
361  * zeros, which means that the inode table is pristine.)
362  */
363 static inline int is_empty_scan(ext2_inode_scan scan)
364 {
365         int     i;
366         
367         if (scan->bytes_left == 0)
368                 return 0;
369
370         for (i=0; i < scan->bytes_left; i++)
371                 if (scan->ptr[i])
372                         return 0;
373         return 1;
374 }
375 #endif
376
377 errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino,
378                                 struct ext2_inode *inode)
379 {
380         errcode_t       retval;
381         int             extra_bytes = 0;
382         
383         EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
384
385         /*
386          * Do we need to start reading a new block group?
387          */
388         if (scan->inodes_left <= 0) {
389         force_new_group:
390                 if (scan->done_group) {
391                         retval = (scan->done_group)
392                                 (scan->fs, scan, scan->current_group,
393                                  scan->done_group_data);
394                         if (retval)
395                                 return retval;
396                 }
397                 if (scan->groups_left <= 0) {
398                         *ino = 0;
399                         return 0;
400                 }
401                 retval = get_next_blockgroup(scan);
402                 if (retval)
403                         return retval;
404         }
405         /*
406          * This is done outside the above if statement so that the
407          * check can be done for block group #0.
408          */
409         if (scan->current_block == 0) {
410                 if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
411                         goto force_new_group;
412                 } else
413                         return EXT2_ET_MISSING_INODE_TABLE;
414         }
415         
416
417         /*
418          * Have we run out of space in the inode buffer?  If so, we
419          * need to read in more blocks.
420          */
421         if (scan->bytes_left < scan->inode_size) {
422                 memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
423                 extra_bytes = scan->bytes_left;
424
425                 retval = get_next_blocks(scan);
426                 if (retval)
427                         return retval;
428 #if 0
429                 /*
430                  * XXX test  Need check for used inode somehow.
431                  * (Note: this is hard.)
432                  */
433                 if (is_empty_scan(scan))
434                         goto force_new_group;
435 #endif
436         }
437
438         retval = 0;
439         if (extra_bytes) {
440                 memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
441                        scan->inode_size - extra_bytes);
442                 scan->ptr += scan->inode_size - extra_bytes;
443                 scan->bytes_left -= scan->inode_size - extra_bytes;
444
445 #ifdef EXT2FS_ENABLE_SWAPFS
446                 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
447                     (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
448                         ext2fs_swap_inode(scan->fs, inode,
449                                  (struct ext2_inode *) scan->temp_buffer, 0);
450                 else
451 #endif
452                         *inode = *((struct ext2_inode *) scan->temp_buffer);
453                 if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
454                         retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
455                 scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
456         } else {
457 #ifdef EXT2FS_ENABLE_SWAPFS
458                 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
459                     (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
460                         ext2fs_swap_inode(scan->fs, inode,
461                                  (struct ext2_inode *) scan->ptr, 0);
462                 else
463 #endif
464                         *inode = *((struct ext2_inode *) scan->ptr);
465                 scan->ptr += scan->inode_size;
466                 scan->bytes_left -= scan->inode_size;
467                 if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
468                         retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
469         }
470
471         scan->inodes_left--;
472         scan->current_inode++;
473         *ino = scan->current_inode;
474         return retval;
475 }
476
477 /*
478  * Functions to read and write a single inode.
479  */
480 errcode_t ext2fs_read_inode (ext2_filsys fs, ext2_ino_t ino,
481                              struct ext2_inode * inode)
482 {
483         unsigned long   group, block, block_nr, offset;
484         char            *ptr;
485         errcode_t       retval;
486         int             clen, i, inodes_per_block;
487         unsigned int    length;
488
489         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
490
491         /* Check to see if user has an override function */
492         if (fs->read_inode) {
493                 retval = (fs->read_inode)(fs, ino, inode);
494                 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
495                         return retval;
496         }
497         /* Create inode cache if not present */
498         if (!fs->icache) {
499                 retval = create_icache(fs);
500                 if (retval)
501                         return retval;
502         }
503         /* Check to see if it's in the inode cache */
504         for (i=0; i < fs->icache->cache_size; i++) {
505                 if (fs->icache->cache[i].ino == ino) {
506                         *inode = fs->icache->cache[i].inode;
507                         return 0;
508                 }
509         }
510         if ((ino == 0) || (ino > fs->super->s_inodes_count))
511                 return EXT2_ET_BAD_INODE_NUM;
512         if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
513                 inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super);
514                 block_nr = fs->image_header->offset_inode / fs->blocksize;
515                 block_nr += (ino - 1) / inodes_per_block;
516                 offset = ((ino - 1) % inodes_per_block) *
517                         EXT2_INODE_SIZE(fs->super);
518         } else {
519                 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
520                 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
521                         EXT2_INODE_SIZE(fs->super);
522                 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
523                 if (!fs->group_desc[(unsigned)group].bg_inode_table)
524                         return EXT2_ET_MISSING_INODE_TABLE;
525                 block_nr = fs->group_desc[(unsigned)group].bg_inode_table + 
526                         block;
527         }
528         if (block_nr != fs->icache->buffer_blk) {
529                 retval = io_channel_read_blk(fs->io, block_nr, 1,
530                                              fs->icache->buffer);
531                 if (retval)
532                         return retval;
533                 fs->icache->buffer_blk = block_nr;
534         }
535         offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
536         ptr = ((char *) fs->icache->buffer) + (unsigned) offset;
537
538         memset(inode, 0, sizeof(struct ext2_inode));
539         length = EXT2_INODE_SIZE(fs->super);
540         if (length > sizeof(struct ext2_inode))
541                 length = sizeof(struct ext2_inode);
542         
543         if ((offset + length) > (unsigned) EXT2_BLOCK_SIZE(fs->super)) {
544                 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
545                 memcpy((char *) inode, ptr, clen);
546                 length -= clen;
547                 
548                 retval = io_channel_read_blk(fs->io, block_nr+1, 1,
549                                              fs->icache->buffer);
550                 if (retval) {
551                         fs->icache->buffer_blk = 0;
552                         return retval;
553                 }
554                 fs->icache->buffer_blk = block_nr+1;
555                 
556                 memcpy(((char *) inode) + clen,
557                        fs->icache->buffer, length);
558         } else
559                 memcpy((char *) inode, ptr, length);
560         
561 #ifdef EXT2FS_ENABLE_SWAPFS
562         if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
563             (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
564                 ext2fs_swap_inode(fs, inode, inode, 0);
565 #endif
566
567         /* Update the inode cache */
568         fs->icache->cache_last = (fs->icache->cache_last + 1) %
569                 fs->icache->cache_size;
570         fs->icache->cache[fs->icache->cache_last].ino = ino;
571         fs->icache->cache[fs->icache->cache_last].inode = *inode;
572         
573         return 0;
574 }
575
576 errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
577                              struct ext2_inode * inode)
578 {
579         unsigned long group, block, block_nr, offset;
580         errcode_t       retval;
581         struct ext2_inode temp_inode;
582         char *ptr;
583         int clen, i;
584         unsigned int length;
585
586         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
587
588         /* Check to see if user provided an override function */
589         if (fs->write_inode) {
590                 retval = (fs->write_inode)(fs, ino, inode);
591                 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
592                         return retval;
593         }
594
595         /* Check to see if the inode cache needs to be updated */
596         if (fs->icache) {
597                 for (i=0; i < fs->icache->cache_size; i++) {
598                         if (fs->icache->cache[i].ino == ino) {
599                                 fs->icache->cache[i].inode = *inode;
600                                 break;
601                         }
602                 }
603         } else {
604                 retval = create_icache(fs);
605                 if (retval)
606                         return retval;
607         }
608                 
609         if (!(fs->flags & EXT2_FLAG_RW))
610                 return EXT2_ET_RO_FILSYS;
611
612         if ((ino == 0) || (ino > fs->super->s_inodes_count))
613                 return EXT2_ET_BAD_INODE_NUM;
614
615 #ifdef EXT2FS_ENABLE_SWAPFS
616         if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
617             (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
618                 ext2fs_swap_inode(fs, &temp_inode, inode, 1);
619         else
620 #endif
621                 memcpy(&temp_inode, inode, sizeof(struct ext2_inode));
622         
623         group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
624         offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
625                 EXT2_INODE_SIZE(fs->super);
626         block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
627         if (!fs->group_desc[(unsigned) group].bg_inode_table)
628                 return EXT2_ET_MISSING_INODE_TABLE;
629         block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block;
630         offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
631         ptr = (char *) fs->icache->buffer + (unsigned) offset;
632
633         length = EXT2_INODE_SIZE(fs->super);
634         clen = length;
635         if (length > sizeof(struct ext2_inode))
636                 length = sizeof(struct ext2_inode);
637         
638         if (fs->icache->buffer_blk != block_nr) {
639                 retval = io_channel_read_blk(fs->io, block_nr, 1,
640                                              fs->icache->buffer);
641                 if (retval)
642                         return retval;
643                 fs->icache->buffer_blk = block_nr;
644         }
645         
646         if ((offset + length) > (unsigned) EXT2_BLOCK_SIZE(fs->super)) {
647                 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
648                 length -= clen;
649         } else {
650                 length = 0;
651         }
652         memcpy(ptr, &temp_inode, clen);
653         retval = io_channel_write_blk(fs->io, block_nr, 1, fs->icache->buffer);
654         if (retval)
655                 return retval;
656
657         if (length) {
658                 retval = io_channel_read_blk(fs->io, ++block_nr, 1,
659                                              fs->icache->buffer);
660                 if (retval) {
661                         fs->icache->buffer_blk = 0;
662                         return retval;
663                 }
664                 fs->icache->buffer_blk = block_nr;
665                 memcpy(fs->icache->buffer, ((char *) &temp_inode) + clen,
666                        length);
667
668                 retval = io_channel_write_blk(fs->io, block_nr, 1,
669                                               fs->icache->buffer);
670                 if (retval)
671                         return retval;
672         }
673         
674         fs->flags |= EXT2_FLAG_CHANGED;
675         return 0;
676 }
677
678 errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks)
679 {
680         struct ext2_inode       inode;
681         int                     i;
682         errcode_t               retval;
683         
684         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
685
686         if (ino > fs->super->s_inodes_count)
687                 return EXT2_ET_BAD_INODE_NUM;
688
689         if (fs->get_blocks) {
690                 if (!(*fs->get_blocks)(fs, ino, blocks))
691                         return 0;
692         }
693         retval = ext2fs_read_inode(fs, ino, &inode);
694         if (retval)
695                 return retval;
696         for (i=0; i < EXT2_N_BLOCKS; i++)
697                 blocks[i] = inode.i_block[i];
698         return 0;
699 }
700
701 errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino)
702 {
703         struct  ext2_inode      inode;
704         errcode_t               retval;
705         
706         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
707
708         if (ino > fs->super->s_inodes_count)
709                 return EXT2_ET_BAD_INODE_NUM;
710
711         if (fs->check_directory) {
712                 retval = (fs->check_directory)(fs, ino);
713                 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
714                         return retval;
715         }
716         retval = ext2fs_read_inode(fs, ino, &inode);
717         if (retval)
718                 return retval;
719         if (!LINUX_S_ISDIR(inode.i_mode))
720                 return EXT2_ET_NO_DIRECTORY;
721         return 0;
722 }
723