Whamcloud - gitweb
libext2fs: fix inode cache overruns
[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 Library
8  * General Public License, version 2.
9  * %End-Header%
10  */
11
12 #include "config.h"
13 #include <stdio.h>
14 #include <string.h>
15 #if HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #if HAVE_ERRNO_H
19 #include <errno.h>
20 #endif
21 #include <time.h>
22 #if HAVE_SYS_STAT_H
23 #include <sys/stat.h>
24 #endif
25 #if HAVE_SYS_TYPES_H
26 #include <sys/types.h>
27 #endif
28
29 #include "ext2_fs.h"
30 #include "ext2fsP.h"
31 #include "e2image.h"
32
33 struct ext2_struct_inode_scan {
34         errcode_t               magic;
35         ext2_filsys             fs;
36         ext2_ino_t              current_inode;
37         blk64_t                 current_block;
38         dgrp_t                  current_group;
39         ext2_ino_t              inodes_left;
40         blk_t                   blocks_left;
41         dgrp_t                  groups_left;
42         blk_t                   inode_buffer_blocks;
43         char *                  inode_buffer;
44         int                     inode_size;
45         char *                  ptr;
46         int                     bytes_left;
47         char                    *temp_buffer;
48         errcode_t               (*done_group)(ext2_filsys fs,
49                                               ext2_inode_scan scan,
50                                               dgrp_t group,
51                                               void * priv_data);
52         void *                  done_group_data;
53         int                     bad_block_ptr;
54         int                     scan_flags;
55         int                     reserved[6];
56 };
57
58 /*
59  * This routine flushes the icache, if it exists.
60  */
61 errcode_t ext2fs_flush_icache(ext2_filsys fs)
62 {
63         int     i;
64
65         if (!fs->icache)
66                 return 0;
67
68         for (i=0; i < fs->icache->cache_size; i++)
69                 fs->icache->cache[i].ino = 0;
70
71         fs->icache->buffer_blk = 0;
72         return 0;
73 }
74
75 static errcode_t create_icache(ext2_filsys fs)
76 {
77         int             i;
78         errcode_t       retval;
79         void            *p;
80
81         if (fs->icache)
82                 return 0;
83         retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache);
84         if (retval)
85                 return retval;
86
87         memset(fs->icache, 0, sizeof(struct ext2_inode_cache));
88         retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer);
89         if (retval) {
90                 ext2fs_free_mem(&fs->icache);
91                 return retval;
92         }
93         fs->icache->buffer_blk = 0;
94         fs->icache->cache_last = -1;
95         fs->icache->cache_size = 4;
96         fs->icache->refcount = 1;
97         retval = ext2fs_get_array(fs->icache->cache_size,
98                                   sizeof(struct ext2_inode_cache_ent) +
99                                   EXT2_INODE_SIZE(fs->super),
100                                   &fs->icache->cache);
101         if (retval) {
102                 ext2fs_free_mem(&fs->icache->buffer);
103                 ext2fs_free_mem(&fs->icache);
104                 return retval;
105         }
106
107         for (i = 0, p = (void *)(fs->icache->cache + fs->icache->cache_size);
108              i < fs->icache->cache_size;
109              i++, p += EXT2_INODE_SIZE(fs->super))
110                 fs->icache->cache[i].inode = p;
111
112         ext2fs_flush_icache(fs);
113         return 0;
114 }
115
116 errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
117                                  ext2_inode_scan *ret_scan)
118 {
119         ext2_inode_scan scan;
120         errcode_t       retval;
121         errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks);
122
123         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
124
125         /*
126          * If fs->badblocks isn't set, then set it --- since the inode
127          * scanning functions require it.
128          */
129         if (fs->badblocks == 0) {
130                 /*
131                  * Temporarly save fs->get_blocks and set it to zero,
132                  * for compatibility with old e2fsck's.
133                  */
134                 save_get_blocks = fs->get_blocks;
135                 fs->get_blocks = 0;
136                 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
137                 if (retval && fs->badblocks) {
138                         ext2fs_badblocks_list_free(fs->badblocks);
139                         fs->badblocks = 0;
140                 }
141                 fs->get_blocks = save_get_blocks;
142         }
143
144         retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan);
145         if (retval)
146                 return retval;
147         memset(scan, 0, sizeof(struct ext2_struct_inode_scan));
148
149         scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
150         scan->fs = fs;
151         scan->inode_size = EXT2_INODE_SIZE(fs->super);
152         scan->bytes_left = 0;
153         scan->current_group = 0;
154         scan->groups_left = fs->group_desc_count - 1;
155         scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
156         scan->current_block = ext2fs_inode_table_loc(scan->fs,
157                                                      scan->current_group);
158         scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
159         scan->blocks_left = scan->fs->inode_blocks_per_group;
160         if (ext2fs_has_group_desc_csum(fs)) {
161                 scan->inodes_left -=
162                         ext2fs_bg_itable_unused(fs, scan->current_group);
163                 scan->blocks_left =
164                         (scan->inodes_left +
165                          (fs->blocksize / scan->inode_size - 1)) *
166                         scan->inode_size / fs->blocksize;
167         }
168         retval = io_channel_alloc_buf(fs->io, scan->inode_buffer_blocks,
169                                       &scan->inode_buffer);
170         scan->done_group = 0;
171         scan->done_group_data = 0;
172         scan->bad_block_ptr = 0;
173         if (retval) {
174                 ext2fs_free_mem(&scan);
175                 return retval;
176         }
177         retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer);
178         if (retval) {
179                 ext2fs_free_mem(&scan->inode_buffer);
180                 ext2fs_free_mem(&scan);
181                 return retval;
182         }
183         if (scan->fs->badblocks && scan->fs->badblocks->num)
184                 scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
185         if (ext2fs_has_group_desc_csum(fs))
186                 scan->scan_flags |= EXT2_SF_DO_LAZY;
187         *ret_scan = scan;
188         return 0;
189 }
190
191 void ext2fs_close_inode_scan(ext2_inode_scan scan)
192 {
193         if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
194                 return;
195
196         ext2fs_free_mem(&scan->inode_buffer);
197         scan->inode_buffer = NULL;
198         ext2fs_free_mem(&scan->temp_buffer);
199         scan->temp_buffer = NULL;
200         ext2fs_free_mem(&scan);
201         return;
202 }
203
204 void ext2fs_set_inode_callback(ext2_inode_scan scan,
205                                errcode_t (*done_group)(ext2_filsys fs,
206                                                        ext2_inode_scan scan,
207                                                        dgrp_t group,
208                                                        void * priv_data),
209                                void *done_group_data)
210 {
211         if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
212                 return;
213
214         scan->done_group = done_group;
215         scan->done_group_data = done_group_data;
216 }
217
218 int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
219                             int clear_flags)
220 {
221         int     old_flags;
222
223         if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
224                 return 0;
225
226         old_flags = scan->scan_flags;
227         scan->scan_flags &= ~clear_flags;
228         scan->scan_flags |= set_flags;
229         return old_flags;
230 }
231
232 /*
233  * This function is called by ext2fs_get_next_inode when it needs to
234  * get ready to read in a new blockgroup.
235  */
236 static errcode_t get_next_blockgroup(ext2_inode_scan scan)
237 {
238         ext2_filsys fs = scan->fs;
239
240         scan->current_group++;
241         scan->groups_left--;
242
243         scan->current_block = ext2fs_inode_table_loc(scan->fs,
244                                                      scan->current_group);
245         scan->current_inode = scan->current_group *
246                 EXT2_INODES_PER_GROUP(fs->super);
247
248         scan->bytes_left = 0;
249         scan->inodes_left = EXT2_INODES_PER_GROUP(fs->super);
250         scan->blocks_left = fs->inode_blocks_per_group;
251         if (ext2fs_has_group_desc_csum(fs)) {
252                 scan->inodes_left -=
253                         ext2fs_bg_itable_unused(fs, scan->current_group);
254                 scan->blocks_left =
255                         (scan->inodes_left +
256                          (fs->blocksize / scan->inode_size - 1)) *
257                         scan->inode_size / fs->blocksize;
258         }
259
260         return 0;
261 }
262
263 errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
264                                             int group)
265 {
266         scan->current_group = group - 1;
267         scan->groups_left = scan->fs->group_desc_count - group;
268         return get_next_blockgroup(scan);
269 }
270
271 /*
272  * This function is called by get_next_blocks() to check for bad
273  * blocks in the inode table.
274  *
275  * This function assumes that badblocks_list->list is sorted in
276  * increasing order.
277  */
278 static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
279                                             blk_t *num_blocks)
280 {
281         blk_t   blk = scan->current_block;
282         badblocks_list  bb = scan->fs->badblocks;
283
284         /*
285          * If the inode table is missing, then obviously there are no
286          * bad blocks.  :-)
287          */
288         if (blk == 0)
289                 return 0;
290
291         /*
292          * If the current block is greater than the bad block listed
293          * in the bad block list, then advance the pointer until this
294          * is no longer the case.  If we run out of bad blocks, then
295          * we don't need to do any more checking!
296          */
297         while (blk > bb->list[scan->bad_block_ptr]) {
298                 if (++scan->bad_block_ptr >= bb->num) {
299                         scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
300                         return 0;
301                 }
302         }
303
304         /*
305          * If the current block is equal to the bad block listed in
306          * the bad block list, then handle that one block specially.
307          * (We could try to handle runs of bad blocks, but that
308          * only increases CPU efficiency by a small amount, at the
309          * expense of a huge expense of code complexity, and for an
310          * uncommon case at that.)
311          */
312         if (blk == bb->list[scan->bad_block_ptr]) {
313                 scan->scan_flags |= EXT2_SF_BAD_INODE_BLK;
314                 *num_blocks = 1;
315                 if (++scan->bad_block_ptr >= bb->num)
316                         scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
317                 return 0;
318         }
319
320         /*
321          * If there is a bad block in the range that we're about to
322          * read in, adjust the number of blocks to read so that we we
323          * don't read in the bad block.  (Then the next block to read
324          * will be the bad block, which is handled in the above case.)
325          */
326         if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr])
327                 *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk);
328
329         return 0;
330 }
331
332 /*
333  * This function is called by ext2fs_get_next_inode when it needs to
334  * read in more blocks from the current blockgroup's inode table.
335  */
336 static errcode_t get_next_blocks(ext2_inode_scan scan)
337 {
338         blk_t           num_blocks;
339         errcode_t       retval;
340
341         /*
342          * Figure out how many blocks to read; we read at most
343          * inode_buffer_blocks, and perhaps less if there aren't that
344          * many blocks left to read.
345          */
346         num_blocks = scan->inode_buffer_blocks;
347         if (num_blocks > scan->blocks_left)
348                 num_blocks = scan->blocks_left;
349
350         /*
351          * If the past block "read" was a bad block, then mark the
352          * left-over extra bytes as also being bad.
353          */
354         if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) {
355                 if (scan->bytes_left)
356                         scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES;
357                 scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK;
358         }
359
360         /*
361          * Do inode bad block processing, if necessary.
362          */
363         if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) {
364                 retval = check_for_inode_bad_blocks(scan, &num_blocks);
365                 if (retval)
366                         return retval;
367         }
368
369         if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) ||
370             (scan->current_block == 0)) {
371                 memset(scan->inode_buffer, 0,
372                        (size_t) num_blocks * scan->fs->blocksize);
373         } else {
374                 retval = io_channel_read_blk64(scan->fs->io,
375                                              scan->current_block,
376                                              (int) num_blocks,
377                                              scan->inode_buffer);
378                 if (retval)
379                         return EXT2_ET_NEXT_INODE_READ;
380         }
381         scan->ptr = scan->inode_buffer;
382         scan->bytes_left = num_blocks * scan->fs->blocksize;
383
384         scan->blocks_left -= num_blocks;
385         if (scan->current_block)
386                 scan->current_block += num_blocks;
387         return 0;
388 }
389
390 #if 0
391 /*
392  * Returns 1 if the entire inode_buffer has a non-zero size and
393  * contains all zeros.  (Not just deleted inodes, since that means
394  * that part of the inode table was used at one point; we want all
395  * zeros, which means that the inode table is pristine.)
396  */
397 static inline int is_empty_scan(ext2_inode_scan scan)
398 {
399         int     i;
400
401         if (scan->bytes_left == 0)
402                 return 0;
403
404         for (i=0; i < scan->bytes_left; i++)
405                 if (scan->ptr[i])
406                         return 0;
407         return 1;
408 }
409 #endif
410
411 errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
412                                      struct ext2_inode *inode, int bufsize)
413 {
414         errcode_t       retval;
415         int             extra_bytes = 0;
416         const int       length = EXT2_INODE_SIZE(scan->fs->super);
417         struct ext2_inode_large *iptr = (struct ext2_inode_large *)inode;
418
419         EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
420
421         /*
422          * Do we need to start reading a new block group?
423          */
424         if (scan->inodes_left <= 0) {
425         force_new_group:
426                 if (scan->done_group) {
427                         retval = (scan->done_group)
428                                 (scan->fs, scan, scan->current_group,
429                                  scan->done_group_data);
430                         if (retval)
431                                 return retval;
432                 }
433                 if (scan->groups_left <= 0) {
434                         *ino = 0;
435                         return 0;
436                 }
437                 retval = get_next_blockgroup(scan);
438                 if (retval)
439                         return retval;
440         }
441         /*
442          * These checks are done outside the above if statement so
443          * they can be done for block group #0.
444          */
445         if ((scan->scan_flags & EXT2_SF_DO_LAZY) &&
446             (ext2fs_bg_flags_test(scan->fs, scan->current_group, EXT2_BG_INODE_UNINIT)
447              ))
448                 goto force_new_group;
449         if (scan->inodes_left == 0)
450                 goto force_new_group;
451         if (scan->current_block == 0) {
452                 if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
453                         goto force_new_group;
454                 } else
455                         return EXT2_ET_MISSING_INODE_TABLE;
456         }
457
458
459         /*
460          * Have we run out of space in the inode buffer?  If so, we
461          * need to read in more blocks.
462          */
463         if (scan->bytes_left < scan->inode_size) {
464                 memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
465                 extra_bytes = scan->bytes_left;
466
467                 retval = get_next_blocks(scan);
468                 if (retval)
469                         return retval;
470 #if 0
471                 /*
472                  * XXX test  Need check for used inode somehow.
473                  * (Note: this is hard.)
474                  */
475                 if (is_empty_scan(scan))
476                         goto force_new_group;
477 #endif
478         }
479
480         if (bufsize < length) {
481                 retval = ext2fs_get_mem(length, &iptr);
482                 if (retval)
483                         return retval;
484         }
485
486         retval = 0;
487         if (extra_bytes) {
488                 memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
489                        scan->inode_size - extra_bytes);
490                 scan->ptr += scan->inode_size - extra_bytes;
491                 scan->bytes_left -= scan->inode_size - extra_bytes;
492
493                 /* Verify the inode checksum. */
494                 if (!(scan->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
495                     !ext2fs_inode_csum_verify(scan->fs, scan->current_inode + 1,
496                                 (struct ext2_inode_large *)scan->temp_buffer))
497                         retval = EXT2_ET_INODE_CSUM_INVALID;
498
499 #ifdef WORDS_BIGENDIAN
500                 memset(iptr, 0, length);
501                 ext2fs_swap_inode_full(scan->fs,
502                                (struct ext2_inode_large *) iptr,
503                                (struct ext2_inode_large *) scan->temp_buffer,
504                                0, length);
505 #else
506                 memcpy(iptr, scan->temp_buffer, length);
507 #endif
508                 if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
509                         retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
510                 scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
511         } else {
512                 /* Verify the inode checksum. */
513                 if (!(scan->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
514                     !ext2fs_inode_csum_verify(scan->fs, scan->current_inode + 1,
515                                 (struct ext2_inode_large *)scan->ptr))
516                         retval = EXT2_ET_INODE_CSUM_INVALID;
517
518 #ifdef WORDS_BIGENDIAN
519                 memset(iptr, 0, length);
520                 ext2fs_swap_inode_full(scan->fs,
521                                 (struct ext2_inode_large *) iptr,
522                                 (struct ext2_inode_large *) scan->ptr,
523                                 0, length);
524 #else
525                 memcpy(iptr, scan->ptr, length);
526 #endif
527                 scan->ptr += scan->inode_size;
528                 scan->bytes_left -= scan->inode_size;
529                 if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
530                         retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
531         }
532
533         scan->inodes_left--;
534         scan->current_inode++;
535         *ino = scan->current_inode;
536         if (iptr != (struct ext2_inode_large *)inode) {
537                 memcpy(inode, iptr, bufsize);
538                 ext2fs_free_mem(&iptr);
539         }
540         return retval;
541 }
542
543 errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino,
544                                 struct ext2_inode *inode)
545 {
546         return ext2fs_get_next_inode_full(scan, ino, inode,
547                                                 sizeof(struct ext2_inode));
548 }
549
550 /*
551  * Functions to read and write a single inode.
552  */
553 errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
554                                  struct ext2_inode * inode, int bufsize)
555 {
556         blk64_t         block_nr;
557         unsigned long   group, block, offset;
558         char            *ptr;
559         errcode_t       retval;
560         int             clen, i, inodes_per_block;
561         io_channel      io;
562         int             length = EXT2_INODE_SIZE(fs->super);
563         struct ext2_inode_large *iptr;
564         int             cache_slot;
565
566         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
567
568         /* Check to see if user has an override function */
569         if (fs->read_inode) {
570                 retval = (fs->read_inode)(fs, ino, inode);
571                 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
572                         return retval;
573         }
574         if ((ino == 0) || (ino > fs->super->s_inodes_count))
575                 return EXT2_ET_BAD_INODE_NUM;
576         /* Create inode cache if not present */
577         if (!fs->icache) {
578                 retval = create_icache(fs);
579                 if (retval)
580                         return retval;
581         }
582         /* Check to see if it's in the inode cache */
583         for (i = 0; i < fs->icache->cache_size; i++) {
584                 if (fs->icache->cache[i].ino == ino) {
585                         memcpy(inode, fs->icache->cache[i].inode,
586                                (bufsize > length) ? length : bufsize);
587                         return 0;
588                 }
589         }
590         if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
591                 inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super);
592                 block_nr = fs->image_header->offset_inode / fs->blocksize;
593                 block_nr += (ino - 1) / inodes_per_block;
594                 offset = ((ino - 1) % inodes_per_block) *
595                         EXT2_INODE_SIZE(fs->super);
596                 io = fs->image_io;
597         } else {
598                 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
599                 if (group > fs->group_desc_count)
600                         return EXT2_ET_BAD_INODE_NUM;
601                 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
602                         EXT2_INODE_SIZE(fs->super);
603                 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
604                 if (!ext2fs_inode_table_loc(fs, (unsigned) group))
605                         return EXT2_ET_MISSING_INODE_TABLE;
606                 block_nr = ext2fs_inode_table_loc(fs, group) +
607                         block;
608                 io = fs->io;
609         }
610         offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
611
612         cache_slot = (fs->icache->cache_last + 1) % fs->icache->cache_size;
613         iptr = (struct ext2_inode_large *)fs->icache->cache[cache_slot].inode;
614
615         ptr = (char *) iptr;
616         while (length) {
617                 clen = length;
618                 if ((offset + length) > fs->blocksize)
619                         clen = fs->blocksize - offset;
620
621                 if (block_nr != fs->icache->buffer_blk) {
622                         retval = io_channel_read_blk64(io, block_nr, 1,
623                                                      fs->icache->buffer);
624                         if (retval)
625                                 return retval;
626                         fs->icache->buffer_blk = block_nr;
627                 }
628
629                 memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset,
630                        clen);
631
632                 offset = 0;
633                 length -= clen;
634                 ptr += clen;
635                 block_nr++;
636         }
637         length = EXT2_INODE_SIZE(fs->super);
638
639         /* Verify the inode checksum. */
640         if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
641             !ext2fs_inode_csum_verify(fs, ino, iptr))
642                 return EXT2_ET_INODE_CSUM_INVALID;
643
644 #ifdef WORDS_BIGENDIAN
645         ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr,
646                                (struct ext2_inode_large *) iptr,
647                                0, length);
648 #endif
649
650         /* Update the inode cache bookkeeping */
651         fs->icache->cache_last = cache_slot;
652         fs->icache->cache[cache_slot].ino = ino;
653         memcpy(inode, iptr, (bufsize > length) ? length : bufsize);
654
655         return 0;
656 }
657
658 errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino,
659                             struct ext2_inode * inode)
660 {
661         return ext2fs_read_inode_full(fs, ino, inode,
662                                         sizeof(struct ext2_inode));
663 }
664
665 errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
666                                   struct ext2_inode * inode, int bufsize)
667 {
668         blk64_t block_nr;
669         unsigned long group, block, offset;
670         errcode_t retval = 0;
671         struct ext2_inode_large *w_inode;
672         char *ptr;
673         int clen, i;
674         int length = EXT2_INODE_SIZE(fs->super);
675
676         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
677
678         /* Check to see if user provided an override function */
679         if (fs->write_inode) {
680                 retval = (fs->write_inode)(fs, ino, inode);
681                 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
682                         return retval;
683         }
684
685         if ((ino == 0) || (ino > fs->super->s_inodes_count))
686                 return EXT2_ET_BAD_INODE_NUM;
687
688         /* Prepare our shadow buffer for read/modify/byteswap/write */
689         retval = ext2fs_get_mem(length, &w_inode);
690         if (retval)
691                 return retval;
692
693         if (bufsize < length) {
694                 int old_flags = fs->flags;
695                 fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
696                 retval = ext2fs_read_inode_full(fs, ino,
697                                                 (struct ext2_inode *)w_inode,
698                                                 length);
699                 fs->flags = old_flags;
700                 if (retval)
701                         goto errout;
702         }
703
704         /* Check to see if the inode cache needs to be updated */
705         if (fs->icache) {
706                 for (i=0; i < fs->icache->cache_size; i++) {
707                         if (fs->icache->cache[i].ino == ino) {
708                                 memcpy(fs->icache->cache[i].inode, inode,
709                                        (bufsize > length) ? length : bufsize);
710                                 break;
711                         }
712                 }
713         } else {
714                 retval = create_icache(fs);
715                 if (retval)
716                         goto errout;
717         }
718         memcpy(w_inode, inode, (bufsize > length) ? length : bufsize);
719
720         if (!(fs->flags & EXT2_FLAG_RW)) {
721                 retval = EXT2_ET_RO_FILSYS;
722                 goto errout;
723         }
724
725 #ifdef WORDS_BIGENDIAN
726         ext2fs_swap_inode_full(fs, w_inode, w_inode, 1, length);
727 #endif
728
729         retval = ext2fs_inode_csum_set(fs, ino, w_inode);
730         if (retval)
731                 goto errout;
732
733         group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
734         offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
735                 EXT2_INODE_SIZE(fs->super);
736         block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
737         if (!ext2fs_inode_table_loc(fs, (unsigned) group)) {
738                 retval = EXT2_ET_MISSING_INODE_TABLE;
739                 goto errout;
740         }
741         block_nr = ext2fs_inode_table_loc(fs, (unsigned) group) + block;
742
743         offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
744
745         ptr = (char *) w_inode;
746
747         while (length) {
748                 clen = length;
749                 if ((offset + length) > fs->blocksize)
750                         clen = fs->blocksize - offset;
751
752                 if (fs->icache->buffer_blk != block_nr) {
753                         retval = io_channel_read_blk64(fs->io, block_nr, 1,
754                                                      fs->icache->buffer);
755                         if (retval)
756                                 goto errout;
757                         fs->icache->buffer_blk = block_nr;
758                 }
759
760
761                 memcpy((char *) fs->icache->buffer + (unsigned) offset,
762                        ptr, clen);
763
764                 retval = io_channel_write_blk64(fs->io, block_nr, 1,
765                                               fs->icache->buffer);
766                 if (retval)
767                         goto errout;
768
769                 offset = 0;
770                 ptr += clen;
771                 length -= clen;
772                 block_nr++;
773         }
774
775         fs->flags |= EXT2_FLAG_CHANGED;
776 errout:
777         ext2fs_free_mem(&w_inode);
778         return retval;
779 }
780
781 errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
782                              struct ext2_inode *inode)
783 {
784         return ext2fs_write_inode_full(fs, ino, inode,
785                                        sizeof(struct ext2_inode));
786 }
787
788 /*
789  * This function should be called when writing a new inode.  It makes
790  * sure that extra part of large inodes is initialized properly.
791  */
792 errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino,
793                                  struct ext2_inode *inode)
794 {
795         struct ext2_inode       *buf;
796         int                     size = EXT2_INODE_SIZE(fs->super);
797         struct ext2_inode_large *large_inode;
798         errcode_t               retval;
799         __u32                   t = fs->now ? fs->now : time(NULL);
800
801         if (!inode->i_ctime)
802                 inode->i_ctime = t;
803         if (!inode->i_mtime)
804                 inode->i_mtime = t;
805         if (!inode->i_atime)
806                 inode->i_atime = t;
807
808         if (size == sizeof(struct ext2_inode))
809                 return ext2fs_write_inode_full(fs, ino, inode,
810                                                sizeof(struct ext2_inode));
811
812         buf = malloc(size);
813         if (!buf)
814                 return ENOMEM;
815
816         memset(buf, 0, size);
817         *buf = *inode;
818
819         large_inode = (struct ext2_inode_large *) buf;
820         large_inode->i_extra_isize = sizeof(struct ext2_inode_large) -
821                 EXT2_GOOD_OLD_INODE_SIZE;
822         if (!large_inode->i_crtime)
823                 large_inode->i_crtime = t;
824
825         retval = ext2fs_write_inode_full(fs, ino, buf, size);
826         free(buf);
827         return retval;
828 }
829
830
831 errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks)
832 {
833         struct ext2_inode       inode;
834         int                     i;
835         errcode_t               retval;
836
837         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
838
839         if (ino > fs->super->s_inodes_count)
840                 return EXT2_ET_BAD_INODE_NUM;
841
842         if (fs->get_blocks) {
843                 if (!(*fs->get_blocks)(fs, ino, blocks))
844                         return 0;
845         }
846         retval = ext2fs_read_inode(fs, ino, &inode);
847         if (retval)
848                 return retval;
849         for (i=0; i < EXT2_N_BLOCKS; i++)
850                 blocks[i] = inode.i_block[i];
851         return 0;
852 }
853
854 errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino)
855 {
856         struct  ext2_inode      inode;
857         errcode_t               retval;
858
859         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
860
861         if (ino > fs->super->s_inodes_count)
862                 return EXT2_ET_BAD_INODE_NUM;
863
864         if (fs->check_directory) {
865                 retval = (fs->check_directory)(fs, ino);
866                 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
867                         return retval;
868         }
869         retval = ext2fs_read_inode(fs, ino, &inode);
870         if (retval)
871                 return retval;
872         if (!LINUX_S_ISDIR(inode.i_mode))
873                 return EXT2_ET_NO_DIRECTORY;
874         return 0;
875 }
876