blob: d80f6c01b2f08780a1c01d095620e8ec5b56feb0 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
2 * inode.c --- utility routines to read and write inodes
3 *
Theodore Ts'o19c78dc1997-04-29 16:17:09 +00004 * 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%
Theodore Ts'o3839e651997-04-26 13:21:57 +000010 */
11
12#include <stdio.h>
13#include <string.h>
Theodore Ts'o4cbe8af1997-08-10 23:07:40 +000014#if HAVE_UNISTD_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000015#include <unistd.h>
Theodore Ts'o4cbe8af1997-08-10 23:07:40 +000016#endif
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000017#if HAVE_SYS_STAT_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000018#include <sys/stat.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000019#endif
20#if HAVE_SYS_TYPES_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000021#include <sys/types.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000022#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000023
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000024#include "ext2_fs.h"
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000025#include "ext2fsP.h"
Theodore Ts'oa78926e2001-05-03 04:02:29 +000026#include "e2image.h"
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000027
28struct ext2_struct_inode_scan {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +000029 errcode_t magic;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000030 ext2_filsys fs;
Theodore Ts'o31dbecd2001-01-11 04:54:39 +000031 ext2_ino_t current_inode;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000032 blk_t current_block;
33 dgrp_t current_group;
Theodore Ts'o31dbecd2001-01-11 04:54:39 +000034 ext2_ino_t inodes_left;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +000035 blk_t blocks_left;
36 dgrp_t groups_left;
37 blk_t inode_buffer_blocks;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000038 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,
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000046 void * priv_data);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000047 void * done_group_data;
48 int bad_block_ptr;
49 int scan_flags;
50 int reserved[6];
51};
Theodore Ts'o3839e651997-04-26 13:21:57 +000052
Theodore Ts'o6a7f4552000-11-12 19:07:06 +000053/*
54 * This routine flushes the icache, if it exists.
55 */
56errcode_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
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000069static errcode_t create_icache(ext2_filsys fs)
70{
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000071 errcode_t retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000072
73 if (fs->icache)
74 return 0;
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -040075 retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000076 if (retval)
77 return retval;
78
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000079 memset(fs->icache, 0, sizeof(struct ext2_inode_cache));
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -040080 retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000081 if (retval) {
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -040082 ext2fs_free_mem(&fs->icache);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000083 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000084 }
85 fs->icache->buffer_blk = 0;
86 fs->icache->cache_last = -1;
87 fs->icache->cache_size = 4;
88 fs->icache->refcount = 1;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000089 retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache_ent)
90 * fs->icache->cache_size,
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -040091 &fs->icache->cache);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000092 if (retval) {
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -040093 ext2fs_free_mem(&fs->icache->buffer);
94 ext2fs_free_mem(&fs->icache);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000095 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000096 }
Theodore Ts'o6a7f4552000-11-12 19:07:06 +000097 ext2fs_flush_icache(fs);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000098 return 0;
99}
100
Theodore Ts'o3839e651997-04-26 13:21:57 +0000101errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
102 ext2_inode_scan *ret_scan)
103{
104 ext2_inode_scan scan;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000105 errcode_t retval;
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000106 errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000107
Theodore Ts'of3db3561997-04-26 13:34:30 +0000108 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
109
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000110 /*
111 * If fs->badblocks isn't set, then set it --- since the inode
112 * scanning functions require it.
113 */
114 if (fs->badblocks == 0) {
Theodore Ts'o521e3681997-04-29 17:48:10 +0000115 /*
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;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000121 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
122 if (retval && fs->badblocks) {
Theodore Ts'ocbbf0312001-06-13 00:12:04 +0000123 ext2fs_badblocks_list_free(fs->badblocks);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000124 fs->badblocks = 0;
125 }
Theodore Ts'o521e3681997-04-29 17:48:10 +0000126 fs->get_blocks = save_get_blocks;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000127 }
128
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -0400129 retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000130 if (retval)
131 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000132 memset(scan, 0, sizeof(struct ext2_struct_inode_scan));
133
Theodore Ts'of3db3561997-04-26 13:34:30 +0000134 scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000135 scan->fs = fs;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000136 scan->inode_size = EXT2_INODE_SIZE(fs->super);
137 scan->bytes_left = 0;
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +0000138 scan->current_group = 0;
139 scan->groups_left = fs->group_desc_count - 1;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000140 scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +0000141 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;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000145 retval = ext2fs_get_mem((size_t) (scan->inode_buffer_blocks *
146 fs->blocksize),
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -0400147 &scan->inode_buffer);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000148 scan->done_group = 0;
149 scan->done_group_data = 0;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000150 scan->bad_block_ptr = 0;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000151 if (retval) {
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -0400152 ext2fs_free_mem(&scan);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000153 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000154 }
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -0400155 retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000156 if (retval) {
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -0400157 ext2fs_free_mem(&scan->inode_buffer);
158 ext2fs_free_mem(&scan);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000159 return retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000160 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000161 if (scan->fs->badblocks && scan->fs->badblocks->num)
162 scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000163 *ret_scan = scan;
164 return 0;
165}
166
167void ext2fs_close_inode_scan(ext2_inode_scan scan)
168{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000169 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
170 return;
171
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -0400172 ext2fs_free_mem(&scan->inode_buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000173 scan->inode_buffer = NULL;
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -0400174 ext2fs_free_mem(&scan->temp_buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000175 scan->temp_buffer = NULL;
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -0400176 ext2fs_free_mem(&scan);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000177 return;
178}
179
Theodore Ts'of3db3561997-04-26 13:34:30 +0000180void 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,
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +0000184 void * priv_data),
Theodore Ts'of3db3561997-04-26 13:34:30 +0000185 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
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000194int 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 */
212static 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
Theodore Ts'o818180c1998-06-27 05:11:14 +0000220 scan->current_inode = scan->current_group *
221 EXT2_INODES_PER_GROUP(scan->fs->super);
222
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000223 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
229errcode_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 */
244static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000245 blk_t *num_blocks)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000246{
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])
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000293 *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000294
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 */
302static errcode_t get_next_blocks(ext2_inode_scan scan)
303{
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000304 blk_t num_blocks;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000305 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,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000338 (size_t) num_blocks * scan->fs->blocksize);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000339 } else {
340 retval = io_channel_read_blk(scan->fs->io,
341 scan->current_block,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000342 (int) num_blocks,
343 scan->inode_buffer);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000344 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
Theodore Ts'o818180c1998-06-27 05:11:14 +0000356#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 */
363static 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
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000377errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000378 struct ext2_inode *inode)
379{
380 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000381 int extra_bytes = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000382
Theodore Ts'of3db3561997-04-26 13:34:30 +0000383 EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
384
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000385 /*
386 * Do we need to start reading a new block group?
387 */
Theodore Ts'o3839e651997-04-26 13:21:57 +0000388 if (scan->inodes_left <= 0) {
Theodore Ts'o818180c1998-06-27 05:11:14 +0000389 force_new_group:
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000390 if (scan->done_group) {
391 retval = (scan->done_group)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000392 (scan->fs, scan, scan->current_group,
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000393 scan->done_group_data);
394 if (retval)
395 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000396 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000397 if (scan->groups_left <= 0) {
398 *ino = 0;
399 return 0;
400 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000401 retval = get_next_blockgroup(scan);
402 if (retval)
403 return retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000404 }
Theodore Ts'o218a4861998-02-21 01:41:39 +0000405 /*
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) {
Theodore Ts'o818180c1998-06-27 05:11:14 +0000411 goto force_new_group;
Theodore Ts'o218a4861998-02-21 01:41:39 +0000412 } else
413 return EXT2_ET_MISSING_INODE_TABLE;
414 }
415
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000416
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;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000424
425 retval = get_next_blocks(scan);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000426 if (retval)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000427 return retval;
Theodore Ts'o818180c1998-06-27 05:11:14 +0000428#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
Theodore Ts'o3839e651997-04-26 13:21:57 +0000436 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000437
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000438 retval = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000439 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
Theodore Ts'o5df55d72001-06-11 07:00:04 +0000445#ifdef EXT2FS_ENABLE_SWAPFS
Theodore Ts'o5c576471997-04-29 15:29:49 +0000446 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
447 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000448 ext2fs_swap_inode(scan->fs, inode,
449 (struct ext2_inode *) scan->temp_buffer, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000450 else
Theodore Ts'o5df55d72001-06-11 07:00:04 +0000451#endif
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000452 *inode = *((struct ext2_inode *) scan->temp_buffer);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000453 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;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000456 } else {
Theodore Ts'o5df55d72001-06-11 07:00:04 +0000457#ifdef EXT2FS_ENABLE_SWAPFS
Theodore Ts'o5c576471997-04-29 15:29:49 +0000458 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
459 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000460 ext2fs_swap_inode(scan->fs, inode,
461 (struct ext2_inode *) scan->ptr, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000462 else
Theodore Ts'o5df55d72001-06-11 07:00:04 +0000463#endif
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000464 *inode = *((struct ext2_inode *) scan->ptr);
465 scan->ptr += scan->inode_size;
466 scan->bytes_left -= scan->inode_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000467 if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
468 retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000469 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000470
Theodore Ts'o3839e651997-04-26 13:21:57 +0000471 scan->inodes_left--;
472 scan->current_inode++;
473 *ino = scan->current_inode;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000474 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000475}
476
477/*
478 * Functions to read and write a single inode.
479 */
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000480errcode_t ext2fs_read_inode (ext2_filsys fs, ext2_ino_t ino,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000481 struct ext2_inode * inode)
482{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000483 unsigned long group, block, block_nr, offset;
484 char *ptr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000485 errcode_t retval;
Theodore Ts'oa78926e2001-05-03 04:02:29 +0000486 int clen, length, i, inodes_per_block;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000487
Theodore Ts'of3db3561997-04-26 13:34:30 +0000488 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
489
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000490 /* Check to see if user has an override function */
491 if (fs->read_inode) {
492 retval = (fs->read_inode)(fs, ino, inode);
493 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
494 return retval;
495 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000496 /* Create inode cache if not present */
497 if (!fs->icache) {
498 retval = create_icache(fs);
499 if (retval)
500 return retval;
501 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000502 /* Check to see if it's in the inode cache */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000503 for (i=0; i < fs->icache->cache_size; i++) {
504 if (fs->icache->cache[i].ino == ino) {
505 *inode = fs->icache->cache[i].inode;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000506 return 0;
507 }
508 }
Theodore Ts'o665f7101999-01-08 13:33:39 +0000509 if ((ino == 0) || (ino > fs->super->s_inodes_count))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000510 return EXT2_ET_BAD_INODE_NUM;
Theodore Ts'oa78926e2001-05-03 04:02:29 +0000511 if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
512 inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super);
513 block_nr = fs->image_header->offset_inode / fs->blocksize;
514 block_nr += (ino - 1) / inodes_per_block;
515 offset = ((ino - 1) % inodes_per_block) *
516 EXT2_INODE_SIZE(fs->super);
517 } else {
518 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
519 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
520 EXT2_INODE_SIZE(fs->super);
521 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
522 if (!fs->group_desc[(unsigned)group].bg_inode_table)
523 return EXT2_ET_MISSING_INODE_TABLE;
524 block_nr = fs->group_desc[(unsigned)group].bg_inode_table +
525 block;
526 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000527 if (block_nr != fs->icache->buffer_blk) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000528 retval = io_channel_read_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000529 fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000530 if (retval)
531 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000532 fs->icache->buffer_blk = block_nr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000533 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000534 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000535 ptr = ((char *) fs->icache->buffer) + (unsigned) offset;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000536
537 memset(inode, 0, sizeof(struct ext2_inode));
538 length = EXT2_INODE_SIZE(fs->super);
539 if (length > sizeof(struct ext2_inode))
540 length = sizeof(struct ext2_inode);
541
542 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000543 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000544 memcpy((char *) inode, ptr, clen);
545 length -= clen;
546
547 retval = io_channel_read_blk(fs->io, block_nr+1, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000548 fs->icache->buffer);
549 if (retval) {
550 fs->icache->buffer_blk = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000551 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000552 }
553 fs->icache->buffer_blk = block_nr+1;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000554
555 memcpy(((char *) inode) + clen,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000556 fs->icache->buffer, length);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000557 } else
558 memcpy((char *) inode, ptr, length);
559
Theodore Ts'o5df55d72001-06-11 07:00:04 +0000560#ifdef EXT2FS_ENABLE_SWAPFS
Theodore Ts'o5c576471997-04-29 15:29:49 +0000561 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
562 (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000563 ext2fs_swap_inode(fs, inode, inode, 0);
Theodore Ts'o5df55d72001-06-11 07:00:04 +0000564#endif
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000565
566 /* Update the inode cache */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000567 fs->icache->cache_last = (fs->icache->cache_last + 1) %
568 fs->icache->cache_size;
569 fs->icache->cache[fs->icache->cache_last].ino = ino;
570 fs->icache->cache[fs->icache->cache_last].inode = *inode;
571
Theodore Ts'o3839e651997-04-26 13:21:57 +0000572 return 0;
573}
574
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000575errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000576 struct ext2_inode * inode)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000577{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000578 unsigned long group, block, block_nr, offset;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000579 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000580 struct ext2_inode temp_inode;
581 char *ptr;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000582 int clen, length, i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000583
Theodore Ts'of3db3561997-04-26 13:34:30 +0000584 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
585
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000586 /* Check to see if user provided an override function */
587 if (fs->write_inode) {
588 retval = (fs->write_inode)(fs, ino, inode);
589 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
590 return retval;
591 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000592
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000593 /* Check to see if the inode cache needs to be updated */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000594 if (fs->icache) {
595 for (i=0; i < fs->icache->cache_size; i++) {
596 if (fs->icache->cache[i].ino == ino) {
597 fs->icache->cache[i].inode = *inode;
598 break;
599 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000600 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000601 } else {
602 retval = create_icache(fs);
603 if (retval)
604 return retval;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000605 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000606
Theodore Ts'o3839e651997-04-26 13:21:57 +0000607 if (!(fs->flags & EXT2_FLAG_RW))
608 return EXT2_ET_RO_FILSYS;
609
Theodore Ts'o665f7101999-01-08 13:33:39 +0000610 if ((ino == 0) || (ino > fs->super->s_inodes_count))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000611 return EXT2_ET_BAD_INODE_NUM;
612
Theodore Ts'o5df55d72001-06-11 07:00:04 +0000613#ifdef EXT2FS_ENABLE_SWAPFS
Theodore Ts'o5c576471997-04-29 15:29:49 +0000614 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
615 (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000616 ext2fs_swap_inode(fs, &temp_inode, inode, 1);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000617 else
Theodore Ts'o5df55d72001-06-11 07:00:04 +0000618#endif
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000619 memcpy(&temp_inode, inode, sizeof(struct ext2_inode));
620
Theodore Ts'o3839e651997-04-26 13:21:57 +0000621 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000622 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
623 EXT2_INODE_SIZE(fs->super);
624 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000625 if (!fs->group_desc[(unsigned) group].bg_inode_table)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000626 return EXT2_ET_MISSING_INODE_TABLE;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000627 block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000628 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000629 ptr = (char *) fs->icache->buffer + (unsigned) offset;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000630
631 length = EXT2_INODE_SIZE(fs->super);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000632 clen = length;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000633 if (length > sizeof(struct ext2_inode))
634 length = sizeof(struct ext2_inode);
635
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000636 if (fs->icache->buffer_blk != block_nr) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000637 retval = io_channel_read_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000638 fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000639 if (retval)
640 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000641 fs->icache->buffer_blk = block_nr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000642 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000643
644 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000645 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000646 length -= clen;
647 } else {
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000648 length = 0;
649 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000650 memcpy(ptr, &temp_inode, clen);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000651 retval = io_channel_write_blk(fs->io, block_nr, 1, fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000652 if (retval)
653 return retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000654
655 if (length) {
656 retval = io_channel_read_blk(fs->io, ++block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000657 fs->icache->buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000658 if (retval) {
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000659 fs->icache->buffer_blk = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000660 return retval;
661 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000662 fs->icache->buffer_blk = block_nr;
663 memcpy(fs->icache->buffer, ((char *) &temp_inode) + clen,
664 length);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000665
666 retval = io_channel_write_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000667 fs->icache->buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000668 if (retval)
669 return retval;
670 }
671
Theodore Ts'o3839e651997-04-26 13:21:57 +0000672 fs->flags |= EXT2_FLAG_CHANGED;
673 return 0;
674}
675
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000676errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000677{
678 struct ext2_inode inode;
679 int i;
680 errcode_t retval;
681
Theodore Ts'of3db3561997-04-26 13:34:30 +0000682 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
683
Theodore Ts'o3839e651997-04-26 13:21:57 +0000684 if (ino > fs->super->s_inodes_count)
685 return EXT2_ET_BAD_INODE_NUM;
686
687 if (fs->get_blocks) {
688 if (!(*fs->get_blocks)(fs, ino, blocks))
689 return 0;
690 }
691 retval = ext2fs_read_inode(fs, ino, &inode);
692 if (retval)
693 return retval;
694 for (i=0; i < EXT2_N_BLOCKS; i++)
695 blocks[i] = inode.i_block[i];
696 return 0;
697}
698
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000699errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000700{
701 struct ext2_inode inode;
702 errcode_t retval;
703
Theodore Ts'of3db3561997-04-26 13:34:30 +0000704 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
705
Theodore Ts'o3839e651997-04-26 13:21:57 +0000706 if (ino > fs->super->s_inodes_count)
707 return EXT2_ET_BAD_INODE_NUM;
708
Theodore Ts'od163b091997-10-03 17:42:28 +0000709 if (fs->check_directory) {
710 retval = (fs->check_directory)(fs, ino);
711 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
712 return retval;
713 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000714 retval = ext2fs_read_inode(fs, ino, &inode);
715 if (retval)
716 return retval;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000717 if (!LINUX_S_ISDIR(inode.i_mode))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000718 return EXT2_ET_NO_DIRECTORY;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000719 return 0;
720}
721