blob: cceaa9577a8d8182764e84e2d874e2819106e8cc [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
213 scan->bytes_left = 0;
214 scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
215 scan->blocks_left = scan->fs->inode_blocks_per_group;
216 return 0;
217}
218
219errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
220 int group)
221{
222 scan->current_group = group - 1;
223 scan->groups_left = scan->fs->group_desc_count - group;
Theodore Ts'o9941fb71997-06-11 22:27:41 +0000224 scan->current_inode = group * EXT2_INODES_PER_GROUP(scan->fs->super);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000225 return get_next_blockgroup(scan);
226}
227
228/*
229 * This function is called by get_next_blocks() to check for bad
230 * blocks in the inode table.
231 *
232 * This function assumes that badblocks_list->list is sorted in
233 * increasing order.
234 */
235static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000236 blk_t *num_blocks)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000237{
238 blk_t blk = scan->current_block;
239 badblocks_list bb = scan->fs->badblocks;
240
241 /*
242 * If the inode table is missing, then obviously there are no
243 * bad blocks. :-)
244 */
245 if (blk == 0)
246 return 0;
247
248 /*
249 * If the current block is greater than the bad block listed
250 * in the bad block list, then advance the pointer until this
251 * is no longer the case. If we run out of bad blocks, then
252 * we don't need to do any more checking!
253 */
254 while (blk > bb->list[scan->bad_block_ptr]) {
255 if (++scan->bad_block_ptr >= bb->num) {
256 scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
257 return 0;
258 }
259 }
260
261 /*
262 * If the current block is equal to the bad block listed in
263 * the bad block list, then handle that one block specially.
264 * (We could try to handle runs of bad blocks, but that
265 * only increases CPU efficiency by a small amount, at the
266 * expense of a huge expense of code complexity, and for an
267 * uncommon case at that.)
268 */
269 if (blk == bb->list[scan->bad_block_ptr]) {
270 scan->scan_flags |= EXT2_SF_BAD_INODE_BLK;
271 *num_blocks = 1;
272 if (++scan->bad_block_ptr >= bb->num)
273 scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
274 return 0;
275 }
276
277 /*
278 * If there is a bad block in the range that we're about to
279 * read in, adjust the number of blocks to read so that we we
280 * don't read in the bad block. (Then the next block to read
281 * will be the bad block, which is handled in the above case.)
282 */
283 if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr])
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000284 *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000285
286 return 0;
287}
288
289/*
290 * This function is called by ext2fs_get_next_inode when it needs to
291 * read in more blocks from the current blockgroup's inode table.
292 */
293static errcode_t get_next_blocks(ext2_inode_scan scan)
294{
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000295 blk_t num_blocks;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000296 errcode_t retval;
297
298 /*
299 * Figure out how many blocks to read; we read at most
300 * inode_buffer_blocks, and perhaps less if there aren't that
301 * many blocks left to read.
302 */
303 num_blocks = scan->inode_buffer_blocks;
304 if (num_blocks > scan->blocks_left)
305 num_blocks = scan->blocks_left;
306
307 /*
308 * If the past block "read" was a bad block, then mark the
309 * left-over extra bytes as also being bad.
310 */
311 if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) {
312 if (scan->bytes_left)
313 scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES;
314 scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK;
315 }
316
317 /*
318 * Do inode bad block processing, if necessary.
319 */
320 if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) {
321 retval = check_for_inode_bad_blocks(scan, &num_blocks);
322 if (retval)
323 return retval;
324 }
325
326 if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) ||
327 (scan->current_block == 0)) {
328 memset(scan->inode_buffer, 0,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000329 (size_t) num_blocks * scan->fs->blocksize);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000330 } else {
331 retval = io_channel_read_blk(scan->fs->io,
332 scan->current_block,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000333 (int) num_blocks,
334 scan->inode_buffer);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000335 if (retval)
336 return EXT2_ET_NEXT_INODE_READ;
337 }
338 scan->ptr = scan->inode_buffer;
339 scan->bytes_left = num_blocks * scan->fs->blocksize;
340
341 scan->blocks_left -= num_blocks;
342 if (scan->current_block)
343 scan->current_block += num_blocks;
344 return 0;
345}
346
Theodore Ts'o3839e651997-04-26 13:21:57 +0000347errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ino_t *ino,
348 struct ext2_inode *inode)
349{
350 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000351 int extra_bytes = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000352
Theodore Ts'of3db3561997-04-26 13:34:30 +0000353 EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
354
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000355 /*
356 * Do we need to start reading a new block group?
357 */
Theodore Ts'o3839e651997-04-26 13:21:57 +0000358 if (scan->inodes_left <= 0) {
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000359 retry:
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000360 if (scan->done_group) {
361 retval = (scan->done_group)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000362 (scan->fs, scan, scan->current_group,
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000363 scan->done_group_data);
364 if (retval)
365 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000366 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000367 if (scan->groups_left <= 0) {
368 *ino = 0;
369 return 0;
370 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000371 retval = get_next_blockgroup(scan);
372 if (retval)
373 return retval;
374 if (scan->current_block == 0) {
375 if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
376 goto retry;
377 } else
378 return EXT2_ET_MISSING_INODE_TABLE;
379 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000380 }
381
382 /*
383 * Have we run out of space in the inode buffer? If so, we
384 * need to read in more blocks.
385 */
386 if (scan->bytes_left < scan->inode_size) {
387 memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
388 extra_bytes = scan->bytes_left;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000389
390 retval = get_next_blocks(scan);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000391 if (retval)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000392 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000393 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000394
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000395 retval = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000396 if (extra_bytes) {
397 memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
398 scan->inode_size - extra_bytes);
399 scan->ptr += scan->inode_size - extra_bytes;
400 scan->bytes_left -= scan->inode_size - extra_bytes;
401
Theodore Ts'o5c576471997-04-29 15:29:49 +0000402 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
403 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000404 ext2fs_swap_inode(scan->fs, inode,
405 (struct ext2_inode *) scan->temp_buffer, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000406 else
407 *inode = *((struct ext2_inode *) scan->temp_buffer);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000408 if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
409 retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
410 scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000411 } else {
Theodore Ts'o5c576471997-04-29 15:29:49 +0000412 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
413 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000414 ext2fs_swap_inode(scan->fs, inode,
415 (struct ext2_inode *) scan->ptr, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000416 else
417 *inode = *((struct ext2_inode *) scan->ptr);
418 scan->ptr += scan->inode_size;
419 scan->bytes_left -= scan->inode_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000420 if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
421 retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000422 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000423
Theodore Ts'o3839e651997-04-26 13:21:57 +0000424 scan->inodes_left--;
425 scan->current_inode++;
426 *ino = scan->current_inode;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000427 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000428}
429
430/*
431 * Functions to read and write a single inode.
432 */
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000433errcode_t ext2fs_read_inode (ext2_filsys fs, ino_t ino,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000434 struct ext2_inode * inode)
435{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000436 unsigned long group, block, block_nr, offset;
437 char *ptr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000438 errcode_t retval;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000439 int clen, length, i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000440
Theodore Ts'of3db3561997-04-26 13:34:30 +0000441 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
442
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000443 /* Check to see if user has an override function */
444 if (fs->read_inode) {
445 retval = (fs->read_inode)(fs, ino, inode);
446 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
447 return retval;
448 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000449 /* Create inode cache if not present */
450 if (!fs->icache) {
451 retval = create_icache(fs);
452 if (retval)
453 return retval;
454 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000455 /* Check to see if it's in the inode cache */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000456 for (i=0; i < fs->icache->cache_size; i++) {
457 if (fs->icache->cache[i].ino == ino) {
458 *inode = fs->icache->cache[i].inode;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000459 return 0;
460 }
461 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000462 if (ino > fs->super->s_inodes_count)
463 return EXT2_ET_BAD_INODE_NUM;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000464 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000465 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
466 EXT2_INODE_SIZE(fs->super);
467 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000468 if (!fs->group_desc[(unsigned)group].bg_inode_table)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000469 return EXT2_ET_MISSING_INODE_TABLE;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000470 block_nr = fs->group_desc[(unsigned)group].bg_inode_table + block;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000471 if (block_nr != fs->icache->buffer_blk) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000472 retval = io_channel_read_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000473 fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000474 if (retval)
475 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000476 fs->icache->buffer_blk = block_nr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000477 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000478 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000479 ptr = ((char *) fs->icache->buffer) + (unsigned) offset;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000480
481 memset(inode, 0, sizeof(struct ext2_inode));
482 length = EXT2_INODE_SIZE(fs->super);
483 if (length > sizeof(struct ext2_inode))
484 length = sizeof(struct ext2_inode);
485
486 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000487 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000488 memcpy((char *) inode, ptr, clen);
489 length -= clen;
490
491 retval = io_channel_read_blk(fs->io, block_nr+1, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000492 fs->icache->buffer);
493 if (retval) {
494 fs->icache->buffer_blk = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000495 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000496 }
497 fs->icache->buffer_blk = block_nr+1;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000498
499 memcpy(((char *) inode) + clen,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000500 fs->icache->buffer, length);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000501 } else
502 memcpy((char *) inode, ptr, length);
503
Theodore Ts'o5c576471997-04-29 15:29:49 +0000504 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
505 (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000506 ext2fs_swap_inode(fs, inode, inode, 0);
507
508 /* Update the inode cache */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000509 fs->icache->cache_last = (fs->icache->cache_last + 1) %
510 fs->icache->cache_size;
511 fs->icache->cache[fs->icache->cache_last].ino = ino;
512 fs->icache->cache[fs->icache->cache_last].inode = *inode;
513
Theodore Ts'o3839e651997-04-26 13:21:57 +0000514 return 0;
515}
516
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000517errcode_t ext2fs_write_inode(ext2_filsys fs, ino_t ino,
518 struct ext2_inode * inode)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000519{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000520 unsigned long group, block, block_nr, offset;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000521 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000522 struct ext2_inode temp_inode;
523 char *ptr;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000524 int clen, length, i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000525
Theodore Ts'of3db3561997-04-26 13:34:30 +0000526 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
527
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000528 /* Check to see if user provided an override function */
529 if (fs->write_inode) {
530 retval = (fs->write_inode)(fs, ino, inode);
531 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
532 return retval;
533 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000534
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000535 /* Check to see if the inode cache needs to be updated */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000536 if (fs->icache) {
537 for (i=0; i < fs->icache->cache_size; i++) {
538 if (fs->icache->cache[i].ino == ino) {
539 fs->icache->cache[i].inode = *inode;
540 break;
541 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000542 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000543 } else {
544 retval = create_icache(fs);
545 if (retval)
546 return retval;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000547 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000548
Theodore Ts'o3839e651997-04-26 13:21:57 +0000549 if (!(fs->flags & EXT2_FLAG_RW))
550 return EXT2_ET_RO_FILSYS;
551
552 if (ino > fs->super->s_inodes_count)
553 return EXT2_ET_BAD_INODE_NUM;
554
Theodore Ts'o5c576471997-04-29 15:29:49 +0000555 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
556 (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000557 ext2fs_swap_inode(fs, &temp_inode, inode, 1);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000558 else
559 memcpy(&temp_inode, inode, sizeof(struct ext2_inode));
560
Theodore Ts'o3839e651997-04-26 13:21:57 +0000561 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000562 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
563 EXT2_INODE_SIZE(fs->super);
564 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000565 if (!fs->group_desc[(unsigned) group].bg_inode_table)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000566 return EXT2_ET_MISSING_INODE_TABLE;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000567 block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000568 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000569 ptr = (char *) fs->icache->buffer + (unsigned) offset;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000570
571 length = EXT2_INODE_SIZE(fs->super);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000572 clen = length;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000573 if (length > sizeof(struct ext2_inode))
574 length = sizeof(struct ext2_inode);
575
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000576 if (fs->icache->buffer_blk != block_nr) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000577 retval = io_channel_read_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000578 fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000579 if (retval)
580 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000581 fs->icache->buffer_blk = block_nr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000582 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000583
584 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000585 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000586 length -= clen;
587 } else {
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000588 length = 0;
589 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000590 memcpy(ptr, &temp_inode, clen);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000591 retval = io_channel_write_blk(fs->io, block_nr, 1, fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000592 if (retval)
593 return retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000594
595 if (length) {
596 retval = io_channel_read_blk(fs->io, ++block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000597 fs->icache->buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000598 if (retval) {
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000599 fs->icache->buffer_blk = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000600 return retval;
601 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000602 fs->icache->buffer_blk = block_nr;
603 memcpy(fs->icache->buffer, ((char *) &temp_inode) + clen,
604 length);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000605
606 retval = io_channel_write_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000607 fs->icache->buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000608 if (retval)
609 return retval;
610 }
611
Theodore Ts'o3839e651997-04-26 13:21:57 +0000612 fs->flags |= EXT2_FLAG_CHANGED;
613 return 0;
614}
615
616errcode_t ext2fs_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks)
617{
618 struct ext2_inode inode;
619 int i;
620 errcode_t retval;
621
Theodore Ts'of3db3561997-04-26 13:34:30 +0000622 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
623
Theodore Ts'o3839e651997-04-26 13:21:57 +0000624 if (ino > fs->super->s_inodes_count)
625 return EXT2_ET_BAD_INODE_NUM;
626
627 if (fs->get_blocks) {
628 if (!(*fs->get_blocks)(fs, ino, blocks))
629 return 0;
630 }
631 retval = ext2fs_read_inode(fs, ino, &inode);
632 if (retval)
633 return retval;
634 for (i=0; i < EXT2_N_BLOCKS; i++)
635 blocks[i] = inode.i_block[i];
636 return 0;
637}
638
639errcode_t ext2fs_check_directory(ext2_filsys fs, ino_t ino)
640{
641 struct ext2_inode inode;
642 errcode_t retval;
643
Theodore Ts'of3db3561997-04-26 13:34:30 +0000644 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
645
Theodore Ts'o3839e651997-04-26 13:21:57 +0000646 if (ino > fs->super->s_inodes_count)
647 return EXT2_ET_BAD_INODE_NUM;
648
Theodore Ts'od163b091997-10-03 17:42:28 +0000649 if (fs->check_directory) {
650 retval = (fs->check_directory)(fs, ino);
651 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
652 return retval;
653 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000654 retval = ext2fs_read_inode(fs, ino, &inode);
655 if (retval)
656 return retval;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000657 if (!LINUX_S_ISDIR(inode.i_mode))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000658 return EXT2_ET_NO_DIRECTORY;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000659 return 0;
660}
661