blob: 58c9d5d7a039428211e387b5d9edadac6df533c7 [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#if EXT2_FLAT_INCLUDES
25#include "ext2_fs.h"
26#else
Theodore Ts'o3839e651997-04-26 13:21:57 +000027#include <linux/ext2_fs.h>
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000028#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000029
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000030#include "ext2fsP.h"
31
32struct ext2_struct_inode_scan {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +000033 errcode_t magic;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000034 ext2_filsys fs;
35 ino_t current_inode;
36 blk_t current_block;
37 dgrp_t current_group;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +000038 ino_t inodes_left;
39 blk_t blocks_left;
40 dgrp_t groups_left;
41 blk_t inode_buffer_blocks;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000042 char * inode_buffer;
43 int inode_size;
44 char * ptr;
45 int bytes_left;
46 char *temp_buffer;
47 errcode_t (*done_group)(ext2_filsys fs,
48 ext2_inode_scan scan,
49 dgrp_t group,
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000050 void * priv_data);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000051 void * done_group_data;
52 int bad_block_ptr;
53 int scan_flags;
54 int reserved[6];
55};
Theodore Ts'o3839e651997-04-26 13:21:57 +000056
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000057static errcode_t create_icache(ext2_filsys fs)
58{
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000059 errcode_t retval;
60 int i;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000061
62 if (fs->icache)
63 return 0;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000064 retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache),
65 (void **) &fs->icache);
66 if (retval)
67 return retval;
68
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000069 memset(fs->icache, 0, sizeof(struct ext2_inode_cache));
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000070 retval = ext2fs_get_mem(fs->blocksize, (void **) &fs->icache->buffer);
71 if (retval) {
72 ext2fs_free_mem((void **) &fs->icache);
73 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000074 }
75 fs->icache->buffer_blk = 0;
76 fs->icache->cache_last = -1;
77 fs->icache->cache_size = 4;
78 fs->icache->refcount = 1;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000079 retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache_ent)
80 * fs->icache->cache_size,
81 (void **) &fs->icache->cache);
82 if (retval) {
83 ext2fs_free_mem((void **) &fs->icache->buffer);
84 ext2fs_free_mem((void **) &fs->icache);
85 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000086 }
87 for (i=0; i < fs->icache->cache_size; i++)
88 fs->icache->cache[i].ino = 0;
89 return 0;
90}
91
Theodore Ts'o3839e651997-04-26 13:21:57 +000092errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
93 ext2_inode_scan *ret_scan)
94{
95 ext2_inode_scan scan;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000096 errcode_t retval;
Theodore Ts'od163b091997-10-03 17:42:28 +000097 errcode_t (*save_get_blocks)(ext2_filsys f, ino_t ino, blk_t *blocks);
Theodore Ts'o3839e651997-04-26 13:21:57 +000098
Theodore Ts'of3db3561997-04-26 13:34:30 +000099 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
100
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000101 /*
102 * If fs->badblocks isn't set, then set it --- since the inode
103 * scanning functions require it.
104 */
105 if (fs->badblocks == 0) {
Theodore Ts'o521e3681997-04-29 17:48:10 +0000106 /*
107 * Temporarly save fs->get_blocks and set it to zero,
108 * for compatibility with old e2fsck's.
109 */
110 save_get_blocks = fs->get_blocks;
111 fs->get_blocks = 0;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000112 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
113 if (retval && fs->badblocks) {
114 badblocks_list_free(fs->badblocks);
115 fs->badblocks = 0;
116 }
Theodore Ts'o521e3681997-04-29 17:48:10 +0000117 fs->get_blocks = save_get_blocks;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000118 }
119
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000120 retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan),
121 (void **) &scan);
122 if (retval)
123 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000124 memset(scan, 0, sizeof(struct ext2_struct_inode_scan));
125
Theodore Ts'of3db3561997-04-26 13:34:30 +0000126 scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000127 scan->fs = fs;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000128 scan->inode_size = EXT2_INODE_SIZE(fs->super);
129 scan->bytes_left = 0;
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +0000130 scan->current_group = 0;
131 scan->groups_left = fs->group_desc_count - 1;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000132 scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +0000133 scan->current_block = scan->fs->
134 group_desc[scan->current_group].bg_inode_table;
135 scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
136 scan->blocks_left = scan->fs->inode_blocks_per_group;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000137 retval = ext2fs_get_mem((size_t) (scan->inode_buffer_blocks *
138 fs->blocksize),
139 (void **) &scan->inode_buffer);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000140 scan->done_group = 0;
141 scan->done_group_data = 0;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000142 scan->bad_block_ptr = 0;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000143 if (retval) {
144 ext2fs_free_mem((void **) &scan);
145 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000146 }
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000147 retval = ext2fs_get_mem(scan->inode_size,
148 (void **) &scan->temp_buffer);
149 if (retval) {
150 ext2fs_free_mem((void **) &scan->inode_buffer);
151 ext2fs_free_mem((void **) &scan);
152 return retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000153 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000154 if (scan->fs->badblocks && scan->fs->badblocks->num)
155 scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000156 *ret_scan = scan;
157 return 0;
158}
159
160void ext2fs_close_inode_scan(ext2_inode_scan scan)
161{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000162 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
163 return;
164
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000165 ext2fs_free_mem((void **) &scan->inode_buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000166 scan->inode_buffer = NULL;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000167 ext2fs_free_mem((void **) &scan->temp_buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000168 scan->temp_buffer = NULL;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000169 ext2fs_free_mem((void **) &scan);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000170 return;
171}
172
Theodore Ts'of3db3561997-04-26 13:34:30 +0000173void ext2fs_set_inode_callback(ext2_inode_scan scan,
174 errcode_t (*done_group)(ext2_filsys fs,
175 ext2_inode_scan scan,
176 dgrp_t group,
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +0000177 void * priv_data),
Theodore Ts'of3db3561997-04-26 13:34:30 +0000178 void *done_group_data)
179{
180 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
181 return;
182
183 scan->done_group = done_group;
184 scan->done_group_data = done_group_data;
185}
186
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000187int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
188 int clear_flags)
189{
190 int old_flags;
191
192 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
193 return 0;
194
195 old_flags = scan->scan_flags;
196 scan->scan_flags &= ~clear_flags;
197 scan->scan_flags |= set_flags;
198 return old_flags;
199}
200
201/*
202 * This function is called by ext2fs_get_next_inode when it needs to
203 * get ready to read in a new blockgroup.
204 */
205static errcode_t get_next_blockgroup(ext2_inode_scan scan)
206{
207 scan->current_group++;
208 scan->groups_left--;
209
210 scan->current_block = scan->fs->
211 group_desc[scan->current_group].bg_inode_table;
212
Theodore Ts'o818180c1998-06-27 05:11:14 +0000213 scan->current_inode = scan->current_group *
214 EXT2_INODES_PER_GROUP(scan->fs->super);
215
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000216 scan->bytes_left = 0;
217 scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
218 scan->blocks_left = scan->fs->inode_blocks_per_group;
219 return 0;
220}
221
222errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
223 int group)
224{
225 scan->current_group = group - 1;
226 scan->groups_left = scan->fs->group_desc_count - group;
227 return get_next_blockgroup(scan);
228}
229
230/*
231 * This function is called by get_next_blocks() to check for bad
232 * blocks in the inode table.
233 *
234 * This function assumes that badblocks_list->list is sorted in
235 * increasing order.
236 */
237static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000238 blk_t *num_blocks)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000239{
240 blk_t blk = scan->current_block;
241 badblocks_list bb = scan->fs->badblocks;
242
243 /*
244 * If the inode table is missing, then obviously there are no
245 * bad blocks. :-)
246 */
247 if (blk == 0)
248 return 0;
249
250 /*
251 * If the current block is greater than the bad block listed
252 * in the bad block list, then advance the pointer until this
253 * is no longer the case. If we run out of bad blocks, then
254 * we don't need to do any more checking!
255 */
256 while (blk > bb->list[scan->bad_block_ptr]) {
257 if (++scan->bad_block_ptr >= bb->num) {
258 scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
259 return 0;
260 }
261 }
262
263 /*
264 * If the current block is equal to the bad block listed in
265 * the bad block list, then handle that one block specially.
266 * (We could try to handle runs of bad blocks, but that
267 * only increases CPU efficiency by a small amount, at the
268 * expense of a huge expense of code complexity, and for an
269 * uncommon case at that.)
270 */
271 if (blk == bb->list[scan->bad_block_ptr]) {
272 scan->scan_flags |= EXT2_SF_BAD_INODE_BLK;
273 *num_blocks = 1;
274 if (++scan->bad_block_ptr >= bb->num)
275 scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
276 return 0;
277 }
278
279 /*
280 * If there is a bad block in the range that we're about to
281 * read in, adjust the number of blocks to read so that we we
282 * don't read in the bad block. (Then the next block to read
283 * will be the bad block, which is handled in the above case.)
284 */
285 if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr])
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000286 *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000287
288 return 0;
289}
290
291/*
292 * This function is called by ext2fs_get_next_inode when it needs to
293 * read in more blocks from the current blockgroup's inode table.
294 */
295static errcode_t get_next_blocks(ext2_inode_scan scan)
296{
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000297 blk_t num_blocks;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000298 errcode_t retval;
299
300 /*
301 * Figure out how many blocks to read; we read at most
302 * inode_buffer_blocks, and perhaps less if there aren't that
303 * many blocks left to read.
304 */
305 num_blocks = scan->inode_buffer_blocks;
306 if (num_blocks > scan->blocks_left)
307 num_blocks = scan->blocks_left;
308
309 /*
310 * If the past block "read" was a bad block, then mark the
311 * left-over extra bytes as also being bad.
312 */
313 if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) {
314 if (scan->bytes_left)
315 scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES;
316 scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK;
317 }
318
319 /*
320 * Do inode bad block processing, if necessary.
321 */
322 if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) {
323 retval = check_for_inode_bad_blocks(scan, &num_blocks);
324 if (retval)
325 return retval;
326 }
327
328 if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) ||
329 (scan->current_block == 0)) {
330 memset(scan->inode_buffer, 0,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000331 (size_t) num_blocks * scan->fs->blocksize);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000332 } else {
333 retval = io_channel_read_blk(scan->fs->io,
334 scan->current_block,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000335 (int) num_blocks,
336 scan->inode_buffer);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000337 if (retval)
338 return EXT2_ET_NEXT_INODE_READ;
339 }
340 scan->ptr = scan->inode_buffer;
341 scan->bytes_left = num_blocks * scan->fs->blocksize;
342
343 scan->blocks_left -= num_blocks;
344 if (scan->current_block)
345 scan->current_block += num_blocks;
346 return 0;
347}
348
Theodore Ts'o818180c1998-06-27 05:11:14 +0000349#if 0
350/*
351 * Returns 1 if the entire inode_buffer has a non-zero size and
352 * contains all zeros. (Not just deleted inodes, since that means
353 * that part of the inode table was used at one point; we want all
354 * zeros, which means that the inode table is pristine.)
355 */
356static inline int is_empty_scan(ext2_inode_scan scan)
357{
358 int i;
359
360 if (scan->bytes_left == 0)
361 return 0;
362
363 for (i=0; i < scan->bytes_left; i++)
364 if (scan->ptr[i])
365 return 0;
366 return 1;
367}
368#endif
369
Theodore Ts'o3839e651997-04-26 13:21:57 +0000370errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ino_t *ino,
371 struct ext2_inode *inode)
372{
373 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000374 int extra_bytes = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000375
Theodore Ts'of3db3561997-04-26 13:34:30 +0000376 EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
377
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000378 /*
379 * Do we need to start reading a new block group?
380 */
Theodore Ts'o3839e651997-04-26 13:21:57 +0000381 if (scan->inodes_left <= 0) {
Theodore Ts'o818180c1998-06-27 05:11:14 +0000382 force_new_group:
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000383 if (scan->done_group) {
384 retval = (scan->done_group)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000385 (scan->fs, scan, scan->current_group,
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000386 scan->done_group_data);
387 if (retval)
388 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000389 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000390 if (scan->groups_left <= 0) {
391 *ino = 0;
392 return 0;
393 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000394 retval = get_next_blockgroup(scan);
395 if (retval)
396 return retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000397 }
Theodore Ts'o218a4861998-02-21 01:41:39 +0000398 /*
399 * This is done outside the above if statement so that the
400 * check can be done for block group #0.
401 */
402 if (scan->current_block == 0) {
403 if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
Theodore Ts'o818180c1998-06-27 05:11:14 +0000404 goto force_new_group;
Theodore Ts'o218a4861998-02-21 01:41:39 +0000405 } else
406 return EXT2_ET_MISSING_INODE_TABLE;
407 }
408
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000409
410 /*
411 * Have we run out of space in the inode buffer? If so, we
412 * need to read in more blocks.
413 */
414 if (scan->bytes_left < scan->inode_size) {
415 memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
416 extra_bytes = scan->bytes_left;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000417
418 retval = get_next_blocks(scan);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000419 if (retval)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000420 return retval;
Theodore Ts'o818180c1998-06-27 05:11:14 +0000421#if 0
422 /*
423 * XXX test Need check for used inode somehow.
424 * (Note: this is hard.)
425 */
426 if (is_empty_scan(scan))
427 goto force_new_group;
428#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000429 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000430
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000431 retval = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000432 if (extra_bytes) {
433 memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
434 scan->inode_size - extra_bytes);
435 scan->ptr += scan->inode_size - extra_bytes;
436 scan->bytes_left -= scan->inode_size - extra_bytes;
437
Theodore Ts'o5c576471997-04-29 15:29:49 +0000438 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
439 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000440 ext2fs_swap_inode(scan->fs, inode,
441 (struct ext2_inode *) scan->temp_buffer, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000442 else
443 *inode = *((struct ext2_inode *) scan->temp_buffer);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000444 if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
445 retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
446 scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000447 } else {
Theodore Ts'o5c576471997-04-29 15:29:49 +0000448 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
449 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000450 ext2fs_swap_inode(scan->fs, inode,
451 (struct ext2_inode *) scan->ptr, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000452 else
453 *inode = *((struct ext2_inode *) scan->ptr);
454 scan->ptr += scan->inode_size;
455 scan->bytes_left -= scan->inode_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000456 if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
457 retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000458 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000459
Theodore Ts'o3839e651997-04-26 13:21:57 +0000460 scan->inodes_left--;
461 scan->current_inode++;
462 *ino = scan->current_inode;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000463 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000464}
465
466/*
467 * Functions to read and write a single inode.
468 */
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000469errcode_t ext2fs_read_inode (ext2_filsys fs, ino_t ino,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000470 struct ext2_inode * inode)
471{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000472 unsigned long group, block, block_nr, offset;
473 char *ptr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000474 errcode_t retval;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000475 int clen, length, i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000476
Theodore Ts'of3db3561997-04-26 13:34:30 +0000477 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
478
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000479 /* Check to see if user has an override function */
480 if (fs->read_inode) {
481 retval = (fs->read_inode)(fs, ino, inode);
482 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
483 return retval;
484 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000485 /* Create inode cache if not present */
486 if (!fs->icache) {
487 retval = create_icache(fs);
488 if (retval)
489 return retval;
490 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000491 /* Check to see if it's in the inode cache */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000492 for (i=0; i < fs->icache->cache_size; i++) {
493 if (fs->icache->cache[i].ino == ino) {
494 *inode = fs->icache->cache[i].inode;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000495 return 0;
496 }
497 }
Theodore Ts'o665f7101999-01-08 13:33:39 +0000498 if ((ino == 0) || (ino > fs->super->s_inodes_count))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000499 return EXT2_ET_BAD_INODE_NUM;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000500 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000501 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
502 EXT2_INODE_SIZE(fs->super);
503 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000504 if (!fs->group_desc[(unsigned)group].bg_inode_table)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000505 return EXT2_ET_MISSING_INODE_TABLE;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000506 block_nr = fs->group_desc[(unsigned)group].bg_inode_table + block;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000507 if (block_nr != fs->icache->buffer_blk) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000508 retval = io_channel_read_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000509 fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000510 if (retval)
511 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000512 fs->icache->buffer_blk = block_nr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000513 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000514 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000515 ptr = ((char *) fs->icache->buffer) + (unsigned) offset;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000516
517 memset(inode, 0, sizeof(struct ext2_inode));
518 length = EXT2_INODE_SIZE(fs->super);
519 if (length > sizeof(struct ext2_inode))
520 length = sizeof(struct ext2_inode);
521
522 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000523 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000524 memcpy((char *) inode, ptr, clen);
525 length -= clen;
526
527 retval = io_channel_read_blk(fs->io, block_nr+1, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000528 fs->icache->buffer);
529 if (retval) {
530 fs->icache->buffer_blk = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000531 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000532 }
533 fs->icache->buffer_blk = block_nr+1;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000534
535 memcpy(((char *) inode) + clen,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000536 fs->icache->buffer, length);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000537 } else
538 memcpy((char *) inode, ptr, length);
539
Theodore Ts'o5c576471997-04-29 15:29:49 +0000540 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
541 (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000542 ext2fs_swap_inode(fs, inode, inode, 0);
543
544 /* Update the inode cache */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000545 fs->icache->cache_last = (fs->icache->cache_last + 1) %
546 fs->icache->cache_size;
547 fs->icache->cache[fs->icache->cache_last].ino = ino;
548 fs->icache->cache[fs->icache->cache_last].inode = *inode;
549
Theodore Ts'o3839e651997-04-26 13:21:57 +0000550 return 0;
551}
552
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000553errcode_t ext2fs_write_inode(ext2_filsys fs, ino_t ino,
554 struct ext2_inode * inode)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000555{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000556 unsigned long group, block, block_nr, offset;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000557 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000558 struct ext2_inode temp_inode;
559 char *ptr;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000560 int clen, length, i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000561
Theodore Ts'of3db3561997-04-26 13:34:30 +0000562 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
563
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000564 /* Check to see if user provided an override function */
565 if (fs->write_inode) {
566 retval = (fs->write_inode)(fs, ino, inode);
567 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
568 return retval;
569 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000570
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000571 /* Check to see if the inode cache needs to be updated */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000572 if (fs->icache) {
573 for (i=0; i < fs->icache->cache_size; i++) {
574 if (fs->icache->cache[i].ino == ino) {
575 fs->icache->cache[i].inode = *inode;
576 break;
577 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000578 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000579 } else {
580 retval = create_icache(fs);
581 if (retval)
582 return retval;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000583 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000584
Theodore Ts'o3839e651997-04-26 13:21:57 +0000585 if (!(fs->flags & EXT2_FLAG_RW))
586 return EXT2_ET_RO_FILSYS;
587
Theodore Ts'o665f7101999-01-08 13:33:39 +0000588 if ((ino == 0) || (ino > fs->super->s_inodes_count))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000589 return EXT2_ET_BAD_INODE_NUM;
590
Theodore Ts'o5c576471997-04-29 15:29:49 +0000591 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
592 (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000593 ext2fs_swap_inode(fs, &temp_inode, inode, 1);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000594 else
595 memcpy(&temp_inode, inode, sizeof(struct ext2_inode));
596
Theodore Ts'o3839e651997-04-26 13:21:57 +0000597 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000598 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
599 EXT2_INODE_SIZE(fs->super);
600 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000601 if (!fs->group_desc[(unsigned) group].bg_inode_table)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000602 return EXT2_ET_MISSING_INODE_TABLE;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000603 block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000604 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000605 ptr = (char *) fs->icache->buffer + (unsigned) offset;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000606
607 length = EXT2_INODE_SIZE(fs->super);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000608 clen = length;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000609 if (length > sizeof(struct ext2_inode))
610 length = sizeof(struct ext2_inode);
611
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000612 if (fs->icache->buffer_blk != block_nr) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000613 retval = io_channel_read_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000614 fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000615 if (retval)
616 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000617 fs->icache->buffer_blk = block_nr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000618 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000619
620 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000621 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000622 length -= clen;
623 } else {
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000624 length = 0;
625 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000626 memcpy(ptr, &temp_inode, clen);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000627 retval = io_channel_write_blk(fs->io, block_nr, 1, fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000628 if (retval)
629 return retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000630
631 if (length) {
632 retval = io_channel_read_blk(fs->io, ++block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000633 fs->icache->buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000634 if (retval) {
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000635 fs->icache->buffer_blk = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000636 return retval;
637 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000638 fs->icache->buffer_blk = block_nr;
639 memcpy(fs->icache->buffer, ((char *) &temp_inode) + clen,
640 length);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000641
642 retval = io_channel_write_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000643 fs->icache->buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000644 if (retval)
645 return retval;
646 }
647
Theodore Ts'o3839e651997-04-26 13:21:57 +0000648 fs->flags |= EXT2_FLAG_CHANGED;
649 return 0;
650}
651
652errcode_t ext2fs_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks)
653{
654 struct ext2_inode inode;
655 int i;
656 errcode_t retval;
657
Theodore Ts'of3db3561997-04-26 13:34:30 +0000658 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
659
Theodore Ts'o3839e651997-04-26 13:21:57 +0000660 if (ino > fs->super->s_inodes_count)
661 return EXT2_ET_BAD_INODE_NUM;
662
663 if (fs->get_blocks) {
664 if (!(*fs->get_blocks)(fs, ino, blocks))
665 return 0;
666 }
667 retval = ext2fs_read_inode(fs, ino, &inode);
668 if (retval)
669 return retval;
670 for (i=0; i < EXT2_N_BLOCKS; i++)
671 blocks[i] = inode.i_block[i];
672 return 0;
673}
674
675errcode_t ext2fs_check_directory(ext2_filsys fs, ino_t ino)
676{
677 struct ext2_inode inode;
678 errcode_t retval;
679
Theodore Ts'of3db3561997-04-26 13:34:30 +0000680 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
681
Theodore Ts'o3839e651997-04-26 13:21:57 +0000682 if (ino > fs->super->s_inodes_count)
683 return EXT2_ET_BAD_INODE_NUM;
684
Theodore Ts'od163b091997-10-03 17:42:28 +0000685 if (fs->check_directory) {
686 retval = (fs->check_directory)(fs, ino);
687 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
688 return retval;
689 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000690 retval = ext2fs_read_inode(fs, ino, &inode);
691 if (retval)
692 return retval;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000693 if (!LINUX_S_ISDIR(inode.i_mode))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000694 return EXT2_ET_NO_DIRECTORY;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000695 return 0;
696}
697