blob: c84b27f78ddf51bce76c4a83989f70f3e51f2540 [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'o3839e651997-04-26 13:21:57 +000017#include <stdlib.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000018#if HAVE_SYS_STAT_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000019#include <sys/stat.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000020#endif
21#if HAVE_SYS_TYPES_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000022#include <sys/types.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000023#endif
Theodore Ts'o50e1e101997-04-26 13:58:21 +000024#if HAVE_ERRNO_H
25#include <errno.h>
26#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000027
Theodore Ts'o3839e651997-04-26 13:21:57 +000028#include <linux/ext2_fs.h>
29
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,
50 void * private);
51 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{
59 int i;
60
61 if (fs->icache)
62 return 0;
63 fs->icache = malloc(sizeof(struct ext2_inode_cache));
64 memset(fs->icache, 0, sizeof(struct ext2_inode_cache));
65 fs->icache->buffer = malloc(fs->blocksize);
66 if (!fs->icache->buffer) {
67 free(fs->icache);
68 return ENOMEM;
69 }
70 fs->icache->buffer_blk = 0;
71 fs->icache->cache_last = -1;
72 fs->icache->cache_size = 4;
73 fs->icache->refcount = 1;
74 fs->icache->cache = malloc(sizeof(struct ext2_inode_cache_ent)
75 * fs->icache->cache_size);
76 if (!fs->icache->cache) {
77 free(fs->icache->buffer);
78 free(fs->icache);
79 return ENOMEM;
80 }
81 for (i=0; i < fs->icache->cache_size; i++)
82 fs->icache->cache[i].ino = 0;
83 return 0;
84}
85
Theodore Ts'o3839e651997-04-26 13:21:57 +000086errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
87 ext2_inode_scan *ret_scan)
88{
89 ext2_inode_scan scan;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000090 errcode_t retval;
Theodore Ts'od163b091997-10-03 17:42:28 +000091 errcode_t (*save_get_blocks)(ext2_filsys f, ino_t ino, blk_t *blocks);
Theodore Ts'o3839e651997-04-26 13:21:57 +000092
Theodore Ts'of3db3561997-04-26 13:34:30 +000093 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
94
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000095 /*
96 * If fs->badblocks isn't set, then set it --- since the inode
97 * scanning functions require it.
98 */
99 if (fs->badblocks == 0) {
Theodore Ts'o521e3681997-04-29 17:48:10 +0000100 /*
101 * Temporarly save fs->get_blocks and set it to zero,
102 * for compatibility with old e2fsck's.
103 */
104 save_get_blocks = fs->get_blocks;
105 fs->get_blocks = 0;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000106 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
107 if (retval && fs->badblocks) {
108 badblocks_list_free(fs->badblocks);
109 fs->badblocks = 0;
110 }
Theodore Ts'o521e3681997-04-29 17:48:10 +0000111 fs->get_blocks = save_get_blocks;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000112 }
113
Theodore Ts'o3839e651997-04-26 13:21:57 +0000114 scan = (ext2_inode_scan) malloc(sizeof(struct ext2_struct_inode_scan));
115 if (!scan)
116 return ENOMEM;
117 memset(scan, 0, sizeof(struct ext2_struct_inode_scan));
118
Theodore Ts'of3db3561997-04-26 13:34:30 +0000119 scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000120 scan->fs = fs;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000121 scan->inode_size = EXT2_INODE_SIZE(fs->super);
122 scan->bytes_left = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000123 scan->current_group = -1;
124 scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
125 scan->groups_left = fs->group_desc_count;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000126 scan->inode_buffer = malloc((size_t) (scan->inode_buffer_blocks *
127 fs->blocksize));
Theodore Ts'of3db3561997-04-26 13:34:30 +0000128 scan->done_group = 0;
129 scan->done_group_data = 0;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000130 scan->bad_block_ptr = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000131 if (!scan->inode_buffer) {
132 free(scan);
133 return ENOMEM;
134 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000135 scan->temp_buffer = malloc(scan->inode_size);
136 if (!scan->temp_buffer) {
137 free(scan->inode_buffer);
138 free(scan);
139 return ENOMEM;
140 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000141 if (scan->fs->badblocks && scan->fs->badblocks->num)
142 scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000143 *ret_scan = scan;
144 return 0;
145}
146
147void ext2fs_close_inode_scan(ext2_inode_scan scan)
148{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000149 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
150 return;
151
Theodore Ts'o3839e651997-04-26 13:21:57 +0000152 free(scan->inode_buffer);
153 scan->inode_buffer = NULL;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000154 free(scan->temp_buffer);
155 scan->temp_buffer = NULL;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000156 free(scan);
157 return;
158}
159
Theodore Ts'of3db3561997-04-26 13:34:30 +0000160void ext2fs_set_inode_callback(ext2_inode_scan scan,
161 errcode_t (*done_group)(ext2_filsys fs,
162 ext2_inode_scan scan,
163 dgrp_t group,
164 void * private),
165 void *done_group_data)
166{
167 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
168 return;
169
170 scan->done_group = done_group;
171 scan->done_group_data = done_group_data;
172}
173
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000174int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
175 int clear_flags)
176{
177 int old_flags;
178
179 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
180 return 0;
181
182 old_flags = scan->scan_flags;
183 scan->scan_flags &= ~clear_flags;
184 scan->scan_flags |= set_flags;
185 return old_flags;
186}
187
188/*
189 * This function is called by ext2fs_get_next_inode when it needs to
190 * get ready to read in a new blockgroup.
191 */
192static errcode_t get_next_blockgroup(ext2_inode_scan scan)
193{
194 scan->current_group++;
195 scan->groups_left--;
196
197 scan->current_block = scan->fs->
198 group_desc[scan->current_group].bg_inode_table;
199
200 scan->bytes_left = 0;
201 scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
202 scan->blocks_left = scan->fs->inode_blocks_per_group;
203 return 0;
204}
205
206errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
207 int group)
208{
209 scan->current_group = group - 1;
210 scan->groups_left = scan->fs->group_desc_count - group;
Theodore Ts'o9941fb71997-06-11 22:27:41 +0000211 scan->current_inode = group * EXT2_INODES_PER_GROUP(scan->fs->super);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000212 return get_next_blockgroup(scan);
213}
214
215/*
216 * This function is called by get_next_blocks() to check for bad
217 * blocks in the inode table.
218 *
219 * This function assumes that badblocks_list->list is sorted in
220 * increasing order.
221 */
222static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000223 blk_t *num_blocks)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000224{
225 blk_t blk = scan->current_block;
226 badblocks_list bb = scan->fs->badblocks;
227
228 /*
229 * If the inode table is missing, then obviously there are no
230 * bad blocks. :-)
231 */
232 if (blk == 0)
233 return 0;
234
235 /*
236 * If the current block is greater than the bad block listed
237 * in the bad block list, then advance the pointer until this
238 * is no longer the case. If we run out of bad blocks, then
239 * we don't need to do any more checking!
240 */
241 while (blk > bb->list[scan->bad_block_ptr]) {
242 if (++scan->bad_block_ptr >= bb->num) {
243 scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
244 return 0;
245 }
246 }
247
248 /*
249 * If the current block is equal to the bad block listed in
250 * the bad block list, then handle that one block specially.
251 * (We could try to handle runs of bad blocks, but that
252 * only increases CPU efficiency by a small amount, at the
253 * expense of a huge expense of code complexity, and for an
254 * uncommon case at that.)
255 */
256 if (blk == bb->list[scan->bad_block_ptr]) {
257 scan->scan_flags |= EXT2_SF_BAD_INODE_BLK;
258 *num_blocks = 1;
259 if (++scan->bad_block_ptr >= bb->num)
260 scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
261 return 0;
262 }
263
264 /*
265 * If there is a bad block in the range that we're about to
266 * read in, adjust the number of blocks to read so that we we
267 * don't read in the bad block. (Then the next block to read
268 * will be the bad block, which is handled in the above case.)
269 */
270 if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr])
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000271 *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000272
273 return 0;
274}
275
276/*
277 * This function is called by ext2fs_get_next_inode when it needs to
278 * read in more blocks from the current blockgroup's inode table.
279 */
280static errcode_t get_next_blocks(ext2_inode_scan scan)
281{
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000282 blk_t num_blocks;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000283 errcode_t retval;
284
285 /*
286 * Figure out how many blocks to read; we read at most
287 * inode_buffer_blocks, and perhaps less if there aren't that
288 * many blocks left to read.
289 */
290 num_blocks = scan->inode_buffer_blocks;
291 if (num_blocks > scan->blocks_left)
292 num_blocks = scan->blocks_left;
293
294 /*
295 * If the past block "read" was a bad block, then mark the
296 * left-over extra bytes as also being bad.
297 */
298 if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) {
299 if (scan->bytes_left)
300 scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES;
301 scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK;
302 }
303
304 /*
305 * Do inode bad block processing, if necessary.
306 */
307 if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) {
308 retval = check_for_inode_bad_blocks(scan, &num_blocks);
309 if (retval)
310 return retval;
311 }
312
313 if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) ||
314 (scan->current_block == 0)) {
315 memset(scan->inode_buffer, 0,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000316 (size_t) num_blocks * scan->fs->blocksize);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000317 } else {
318 retval = io_channel_read_blk(scan->fs->io,
319 scan->current_block,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000320 (int) num_blocks,
321 scan->inode_buffer);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000322 if (retval)
323 return EXT2_ET_NEXT_INODE_READ;
324 }
325 scan->ptr = scan->inode_buffer;
326 scan->bytes_left = num_blocks * scan->fs->blocksize;
327
328 scan->blocks_left -= num_blocks;
329 if (scan->current_block)
330 scan->current_block += num_blocks;
331 return 0;
332}
333
Theodore Ts'o3839e651997-04-26 13:21:57 +0000334errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ino_t *ino,
335 struct ext2_inode *inode)
336{
337 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000338 int extra_bytes = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000339
Theodore Ts'of3db3561997-04-26 13:34:30 +0000340 EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
341
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000342 /*
343 * Do we need to start reading a new block group?
344 */
Theodore Ts'o3839e651997-04-26 13:21:57 +0000345 if (scan->inodes_left <= 0) {
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000346 retry:
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000347 if (scan->done_group) {
348 retval = (scan->done_group)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000349 (scan->fs, scan, scan->current_group,
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000350 scan->done_group_data);
351 if (retval)
352 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000353 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000354 if (scan->groups_left <= 0) {
355 *ino = 0;
356 return 0;
357 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000358 retval = get_next_blockgroup(scan);
359 if (retval)
360 return retval;
361 if (scan->current_block == 0) {
362 if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
363 goto retry;
364 } else
365 return EXT2_ET_MISSING_INODE_TABLE;
366 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000367 }
368
369 /*
370 * Have we run out of space in the inode buffer? If so, we
371 * need to read in more blocks.
372 */
373 if (scan->bytes_left < scan->inode_size) {
374 memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
375 extra_bytes = scan->bytes_left;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000376
377 retval = get_next_blocks(scan);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000378 if (retval)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000379 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000380 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000381
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000382 retval = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000383 if (extra_bytes) {
384 memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
385 scan->inode_size - extra_bytes);
386 scan->ptr += scan->inode_size - extra_bytes;
387 scan->bytes_left -= scan->inode_size - extra_bytes;
388
Theodore Ts'o5c576471997-04-29 15:29:49 +0000389 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
390 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000391 ext2fs_swap_inode(scan->fs, inode,
392 (struct ext2_inode *) scan->temp_buffer, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000393 else
394 *inode = *((struct ext2_inode *) scan->temp_buffer);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000395 if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
396 retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
397 scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000398 } else {
Theodore Ts'o5c576471997-04-29 15:29:49 +0000399 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
400 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000401 ext2fs_swap_inode(scan->fs, inode,
402 (struct ext2_inode *) scan->ptr, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000403 else
404 *inode = *((struct ext2_inode *) scan->ptr);
405 scan->ptr += scan->inode_size;
406 scan->bytes_left -= scan->inode_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000407 if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
408 retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000409 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000410
Theodore Ts'o3839e651997-04-26 13:21:57 +0000411 scan->inodes_left--;
412 scan->current_inode++;
413 *ino = scan->current_inode;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000414 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000415}
416
417/*
418 * Functions to read and write a single inode.
419 */
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000420errcode_t ext2fs_read_inode (ext2_filsys fs, ino_t ino,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000421 struct ext2_inode * inode)
422{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000423 unsigned long group, block, block_nr, offset;
424 char *ptr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000425 errcode_t retval;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000426 int clen, length, i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000427
Theodore Ts'of3db3561997-04-26 13:34:30 +0000428 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
429
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000430 /* Check to see if user has an override function */
431 if (fs->read_inode) {
432 retval = (fs->read_inode)(fs, ino, inode);
433 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
434 return retval;
435 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000436 /* Create inode cache if not present */
437 if (!fs->icache) {
438 retval = create_icache(fs);
439 if (retval)
440 return retval;
441 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000442 /* Check to see if it's in the inode cache */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000443 for (i=0; i < fs->icache->cache_size; i++) {
444 if (fs->icache->cache[i].ino == ino) {
445 *inode = fs->icache->cache[i].inode;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000446 return 0;
447 }
448 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000449 if (ino > fs->super->s_inodes_count)
450 return EXT2_ET_BAD_INODE_NUM;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000451 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000452 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
453 EXT2_INODE_SIZE(fs->super);
454 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000455 if (!fs->group_desc[(unsigned)group].bg_inode_table)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000456 return EXT2_ET_MISSING_INODE_TABLE;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000457 block_nr = fs->group_desc[(unsigned)group].bg_inode_table + block;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000458 if (block_nr != fs->icache->buffer_blk) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000459 retval = io_channel_read_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000460 fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000461 if (retval)
462 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000463 fs->icache->buffer_blk = block_nr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000464 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000465 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000466 ptr = ((char *) fs->icache->buffer) + (unsigned) offset;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000467
468 memset(inode, 0, sizeof(struct ext2_inode));
469 length = EXT2_INODE_SIZE(fs->super);
470 if (length > sizeof(struct ext2_inode))
471 length = sizeof(struct ext2_inode);
472
473 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000474 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000475 memcpy((char *) inode, ptr, clen);
476 length -= clen;
477
478 retval = io_channel_read_blk(fs->io, block_nr+1, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000479 fs->icache->buffer);
480 if (retval) {
481 fs->icache->buffer_blk = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000482 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000483 }
484 fs->icache->buffer_blk = block_nr+1;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000485
486 memcpy(((char *) inode) + clen,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000487 fs->icache->buffer, length);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000488 } else
489 memcpy((char *) inode, ptr, length);
490
Theodore Ts'o5c576471997-04-29 15:29:49 +0000491 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
492 (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000493 ext2fs_swap_inode(fs, inode, inode, 0);
494
495 /* Update the inode cache */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000496 fs->icache->cache_last = (fs->icache->cache_last + 1) %
497 fs->icache->cache_size;
498 fs->icache->cache[fs->icache->cache_last].ino = ino;
499 fs->icache->cache[fs->icache->cache_last].inode = *inode;
500
Theodore Ts'o3839e651997-04-26 13:21:57 +0000501 return 0;
502}
503
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000504errcode_t ext2fs_write_inode(ext2_filsys fs, ino_t ino,
505 struct ext2_inode * inode)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000506{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000507 unsigned long group, block, block_nr, offset;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000508 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000509 struct ext2_inode temp_inode;
510 char *ptr;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000511 int clen, length, i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000512
Theodore Ts'of3db3561997-04-26 13:34:30 +0000513 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
514
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000515 /* Check to see if user provided an override function */
516 if (fs->write_inode) {
517 retval = (fs->write_inode)(fs, ino, inode);
518 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
519 return retval;
520 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000521
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000522 /* Check to see if the inode cache needs to be updated */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000523 if (fs->icache) {
524 for (i=0; i < fs->icache->cache_size; i++) {
525 if (fs->icache->cache[i].ino == ino) {
526 fs->icache->cache[i].inode = *inode;
527 break;
528 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000529 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000530 } else {
531 retval = create_icache(fs);
532 if (retval)
533 return retval;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000534 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000535
Theodore Ts'o3839e651997-04-26 13:21:57 +0000536 if (!(fs->flags & EXT2_FLAG_RW))
537 return EXT2_ET_RO_FILSYS;
538
539 if (ino > fs->super->s_inodes_count)
540 return EXT2_ET_BAD_INODE_NUM;
541
Theodore Ts'o5c576471997-04-29 15:29:49 +0000542 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
543 (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000544 ext2fs_swap_inode(fs, &temp_inode, inode, 1);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000545 else
546 memcpy(&temp_inode, inode, sizeof(struct ext2_inode));
547
Theodore Ts'o3839e651997-04-26 13:21:57 +0000548 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000549 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
550 EXT2_INODE_SIZE(fs->super);
551 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000552 if (!fs->group_desc[(unsigned) group].bg_inode_table)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000553 return EXT2_ET_MISSING_INODE_TABLE;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000554 block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000555 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000556 ptr = (char *) fs->icache->buffer + (unsigned) offset;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000557
558 length = EXT2_INODE_SIZE(fs->super);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000559 clen = length;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000560 if (length > sizeof(struct ext2_inode))
561 length = sizeof(struct ext2_inode);
562
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000563 if (fs->icache->buffer_blk != block_nr) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000564 retval = io_channel_read_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000565 fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000566 if (retval)
567 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000568 fs->icache->buffer_blk = block_nr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000569 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000570
571 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000572 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000573 length -= clen;
574 } else {
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000575 length = 0;
576 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000577 memcpy(ptr, &temp_inode, clen);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000578 retval = io_channel_write_blk(fs->io, block_nr, 1, fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000579 if (retval)
580 return retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000581
582 if (length) {
583 retval = io_channel_read_blk(fs->io, ++block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000584 fs->icache->buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000585 if (retval) {
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000586 fs->icache->buffer_blk = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000587 return retval;
588 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000589 fs->icache->buffer_blk = block_nr;
590 memcpy(fs->icache->buffer, ((char *) &temp_inode) + clen,
591 length);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000592
593 retval = io_channel_write_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000594 fs->icache->buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000595 if (retval)
596 return retval;
597 }
598
Theodore Ts'o3839e651997-04-26 13:21:57 +0000599 fs->flags |= EXT2_FLAG_CHANGED;
600 return 0;
601}
602
603errcode_t ext2fs_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks)
604{
605 struct ext2_inode inode;
606 int i;
607 errcode_t retval;
608
Theodore Ts'of3db3561997-04-26 13:34:30 +0000609 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
610
Theodore Ts'o3839e651997-04-26 13:21:57 +0000611 if (ino > fs->super->s_inodes_count)
612 return EXT2_ET_BAD_INODE_NUM;
613
614 if (fs->get_blocks) {
615 if (!(*fs->get_blocks)(fs, ino, blocks))
616 return 0;
617 }
618 retval = ext2fs_read_inode(fs, ino, &inode);
619 if (retval)
620 return retval;
621 for (i=0; i < EXT2_N_BLOCKS; i++)
622 blocks[i] = inode.i_block[i];
623 return 0;
624}
625
626errcode_t ext2fs_check_directory(ext2_filsys fs, ino_t ino)
627{
628 struct ext2_inode inode;
629 errcode_t retval;
630
Theodore Ts'of3db3561997-04-26 13:34:30 +0000631 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
632
Theodore Ts'o3839e651997-04-26 13:21:57 +0000633 if (ino > fs->super->s_inodes_count)
634 return EXT2_ET_BAD_INODE_NUM;
635
Theodore Ts'od163b091997-10-03 17:42:28 +0000636 if (fs->check_directory) {
637 retval = (fs->check_directory)(fs, ino);
638 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
639 return retval;
640 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000641 retval = ext2fs_read_inode(fs, ino, &inode);
642 if (retval)
643 return retval;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000644 if (!LINUX_S_ISDIR(inode.i_mode))
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000645 return ENOTDIR;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000646 return 0;
647}
648