blob: cec4341ee44c8c2987242b75a2ed5b45ce610c2f [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'o3839e651997-04-26 13:21:57 +000024
Theodore Ts'o3839e651997-04-26 13:21:57 +000025#include <linux/ext2_fs.h>
26
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000027#include "ext2fsP.h"
28
29struct ext2_struct_inode_scan {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +000030 errcode_t magic;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000031 ext2_filsys fs;
32 ino_t current_inode;
33 blk_t current_block;
34 dgrp_t current_group;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +000035 ino_t inodes_left;
36 blk_t blocks_left;
37 dgrp_t groups_left;
38 blk_t inode_buffer_blocks;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000039 char * inode_buffer;
40 int inode_size;
41 char * ptr;
42 int bytes_left;
43 char *temp_buffer;
44 errcode_t (*done_group)(ext2_filsys fs,
45 ext2_inode_scan scan,
46 dgrp_t group,
47 void * private);
48 void * done_group_data;
49 int bad_block_ptr;
50 int scan_flags;
51 int reserved[6];
52};
Theodore Ts'o3839e651997-04-26 13:21:57 +000053
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000054static errcode_t create_icache(ext2_filsys fs)
55{
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000056 errcode_t retval;
57 int i;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000058
59 if (fs->icache)
60 return 0;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000061 retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache),
62 (void **) &fs->icache);
63 if (retval)
64 return retval;
65
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000066 memset(fs->icache, 0, sizeof(struct ext2_inode_cache));
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000067 retval = ext2fs_get_mem(fs->blocksize, (void **) &fs->icache->buffer);
68 if (retval) {
69 ext2fs_free_mem((void **) &fs->icache);
70 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000071 }
72 fs->icache->buffer_blk = 0;
73 fs->icache->cache_last = -1;
74 fs->icache->cache_size = 4;
75 fs->icache->refcount = 1;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000076 retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache_ent)
77 * fs->icache->cache_size,
78 (void **) &fs->icache->cache);
79 if (retval) {
80 ext2fs_free_mem((void **) &fs->icache->buffer);
81 ext2fs_free_mem((void **) &fs->icache);
82 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000083 }
84 for (i=0; i < fs->icache->cache_size; i++)
85 fs->icache->cache[i].ino = 0;
86 return 0;
87}
88
Theodore Ts'o3839e651997-04-26 13:21:57 +000089errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
90 ext2_inode_scan *ret_scan)
91{
92 ext2_inode_scan scan;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000093 errcode_t retval;
Theodore Ts'od163b091997-10-03 17:42:28 +000094 errcode_t (*save_get_blocks)(ext2_filsys f, ino_t ino, blk_t *blocks);
Theodore Ts'o3839e651997-04-26 13:21:57 +000095
Theodore Ts'of3db3561997-04-26 13:34:30 +000096 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
97
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000098 /*
99 * If fs->badblocks isn't set, then set it --- since the inode
100 * scanning functions require it.
101 */
102 if (fs->badblocks == 0) {
Theodore Ts'o521e3681997-04-29 17:48:10 +0000103 /*
104 * Temporarly save fs->get_blocks and set it to zero,
105 * for compatibility with old e2fsck's.
106 */
107 save_get_blocks = fs->get_blocks;
108 fs->get_blocks = 0;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000109 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
110 if (retval && fs->badblocks) {
111 badblocks_list_free(fs->badblocks);
112 fs->badblocks = 0;
113 }
Theodore Ts'o521e3681997-04-29 17:48:10 +0000114 fs->get_blocks = save_get_blocks;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000115 }
116
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000117 retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan),
118 (void **) &scan);
119 if (retval)
120 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000121 memset(scan, 0, sizeof(struct ext2_struct_inode_scan));
122
Theodore Ts'of3db3561997-04-26 13:34:30 +0000123 scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000124 scan->fs = fs;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000125 scan->inode_size = EXT2_INODE_SIZE(fs->super);
126 scan->bytes_left = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000127 scan->current_group = -1;
128 scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
129 scan->groups_left = fs->group_desc_count;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000130 retval = ext2fs_get_mem((size_t) (scan->inode_buffer_blocks *
131 fs->blocksize),
132 (void **) &scan->inode_buffer);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000133 scan->done_group = 0;
134 scan->done_group_data = 0;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000135 scan->bad_block_ptr = 0;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000136 if (retval) {
137 ext2fs_free_mem((void **) &scan);
138 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000139 }
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000140 retval = ext2fs_get_mem(scan->inode_size,
141 (void **) &scan->temp_buffer);
142 if (retval) {
143 ext2fs_free_mem((void **) &scan->inode_buffer);
144 ext2fs_free_mem((void **) &scan);
145 return retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000146 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000147 if (scan->fs->badblocks && scan->fs->badblocks->num)
148 scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000149 *ret_scan = scan;
150 return 0;
151}
152
153void ext2fs_close_inode_scan(ext2_inode_scan scan)
154{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000155 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
156 return;
157
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000158 ext2fs_free_mem((void **) &scan->inode_buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000159 scan->inode_buffer = NULL;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000160 ext2fs_free_mem((void **) &scan->temp_buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000161 scan->temp_buffer = NULL;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000162 ext2fs_free_mem((void **) &scan);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000163 return;
164}
165
Theodore Ts'of3db3561997-04-26 13:34:30 +0000166void ext2fs_set_inode_callback(ext2_inode_scan scan,
167 errcode_t (*done_group)(ext2_filsys fs,
168 ext2_inode_scan scan,
169 dgrp_t group,
170 void * private),
171 void *done_group_data)
172{
173 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
174 return;
175
176 scan->done_group = done_group;
177 scan->done_group_data = done_group_data;
178}
179
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000180int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
181 int clear_flags)
182{
183 int old_flags;
184
185 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
186 return 0;
187
188 old_flags = scan->scan_flags;
189 scan->scan_flags &= ~clear_flags;
190 scan->scan_flags |= set_flags;
191 return old_flags;
192}
193
194/*
195 * This function is called by ext2fs_get_next_inode when it needs to
196 * get ready to read in a new blockgroup.
197 */
198static errcode_t get_next_blockgroup(ext2_inode_scan scan)
199{
200 scan->current_group++;
201 scan->groups_left--;
202
203 scan->current_block = scan->fs->
204 group_desc[scan->current_group].bg_inode_table;
205
206 scan->bytes_left = 0;
207 scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
208 scan->blocks_left = scan->fs->inode_blocks_per_group;
209 return 0;
210}
211
212errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
213 int group)
214{
215 scan->current_group = group - 1;
216 scan->groups_left = scan->fs->group_desc_count - group;
Theodore Ts'o9941fb71997-06-11 22:27:41 +0000217 scan->current_inode = group * EXT2_INODES_PER_GROUP(scan->fs->super);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000218 return get_next_blockgroup(scan);
219}
220
221/*
222 * This function is called by get_next_blocks() to check for bad
223 * blocks in the inode table.
224 *
225 * This function assumes that badblocks_list->list is sorted in
226 * increasing order.
227 */
228static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000229 blk_t *num_blocks)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000230{
231 blk_t blk = scan->current_block;
232 badblocks_list bb = scan->fs->badblocks;
233
234 /*
235 * If the inode table is missing, then obviously there are no
236 * bad blocks. :-)
237 */
238 if (blk == 0)
239 return 0;
240
241 /*
242 * If the current block is greater than the bad block listed
243 * in the bad block list, then advance the pointer until this
244 * is no longer the case. If we run out of bad blocks, then
245 * we don't need to do any more checking!
246 */
247 while (blk > bb->list[scan->bad_block_ptr]) {
248 if (++scan->bad_block_ptr >= bb->num) {
249 scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
250 return 0;
251 }
252 }
253
254 /*
255 * If the current block is equal to the bad block listed in
256 * the bad block list, then handle that one block specially.
257 * (We could try to handle runs of bad blocks, but that
258 * only increases CPU efficiency by a small amount, at the
259 * expense of a huge expense of code complexity, and for an
260 * uncommon case at that.)
261 */
262 if (blk == bb->list[scan->bad_block_ptr]) {
263 scan->scan_flags |= EXT2_SF_BAD_INODE_BLK;
264 *num_blocks = 1;
265 if (++scan->bad_block_ptr >= bb->num)
266 scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
267 return 0;
268 }
269
270 /*
271 * If there is a bad block in the range that we're about to
272 * read in, adjust the number of blocks to read so that we we
273 * don't read in the bad block. (Then the next block to read
274 * will be the bad block, which is handled in the above case.)
275 */
276 if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr])
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000277 *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000278
279 return 0;
280}
281
282/*
283 * This function is called by ext2fs_get_next_inode when it needs to
284 * read in more blocks from the current blockgroup's inode table.
285 */
286static errcode_t get_next_blocks(ext2_inode_scan scan)
287{
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000288 blk_t num_blocks;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000289 errcode_t retval;
290
291 /*
292 * Figure out how many blocks to read; we read at most
293 * inode_buffer_blocks, and perhaps less if there aren't that
294 * many blocks left to read.
295 */
296 num_blocks = scan->inode_buffer_blocks;
297 if (num_blocks > scan->blocks_left)
298 num_blocks = scan->blocks_left;
299
300 /*
301 * If the past block "read" was a bad block, then mark the
302 * left-over extra bytes as also being bad.
303 */
304 if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) {
305 if (scan->bytes_left)
306 scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES;
307 scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK;
308 }
309
310 /*
311 * Do inode bad block processing, if necessary.
312 */
313 if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) {
314 retval = check_for_inode_bad_blocks(scan, &num_blocks);
315 if (retval)
316 return retval;
317 }
318
319 if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) ||
320 (scan->current_block == 0)) {
321 memset(scan->inode_buffer, 0,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000322 (size_t) num_blocks * scan->fs->blocksize);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000323 } else {
324 retval = io_channel_read_blk(scan->fs->io,
325 scan->current_block,
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000326 (int) num_blocks,
327 scan->inode_buffer);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000328 if (retval)
329 return EXT2_ET_NEXT_INODE_READ;
330 }
331 scan->ptr = scan->inode_buffer;
332 scan->bytes_left = num_blocks * scan->fs->blocksize;
333
334 scan->blocks_left -= num_blocks;
335 if (scan->current_block)
336 scan->current_block += num_blocks;
337 return 0;
338}
339
Theodore Ts'o3839e651997-04-26 13:21:57 +0000340errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ino_t *ino,
341 struct ext2_inode *inode)
342{
343 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000344 int extra_bytes = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000345
Theodore Ts'of3db3561997-04-26 13:34:30 +0000346 EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
347
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000348 /*
349 * Do we need to start reading a new block group?
350 */
Theodore Ts'o3839e651997-04-26 13:21:57 +0000351 if (scan->inodes_left <= 0) {
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000352 retry:
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000353 if (scan->done_group) {
354 retval = (scan->done_group)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000355 (scan->fs, scan, scan->current_group,
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000356 scan->done_group_data);
357 if (retval)
358 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000359 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000360 if (scan->groups_left <= 0) {
361 *ino = 0;
362 return 0;
363 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000364 retval = get_next_blockgroup(scan);
365 if (retval)
366 return retval;
367 if (scan->current_block == 0) {
368 if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
369 goto retry;
370 } else
371 return EXT2_ET_MISSING_INODE_TABLE;
372 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000373 }
374
375 /*
376 * Have we run out of space in the inode buffer? If so, we
377 * need to read in more blocks.
378 */
379 if (scan->bytes_left < scan->inode_size) {
380 memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
381 extra_bytes = scan->bytes_left;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000382
383 retval = get_next_blocks(scan);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000384 if (retval)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000385 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000386 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000387
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000388 retval = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000389 if (extra_bytes) {
390 memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
391 scan->inode_size - extra_bytes);
392 scan->ptr += scan->inode_size - extra_bytes;
393 scan->bytes_left -= scan->inode_size - extra_bytes;
394
Theodore Ts'o5c576471997-04-29 15:29:49 +0000395 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
396 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000397 ext2fs_swap_inode(scan->fs, inode,
398 (struct ext2_inode *) scan->temp_buffer, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000399 else
400 *inode = *((struct ext2_inode *) scan->temp_buffer);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000401 if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
402 retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
403 scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000404 } else {
Theodore Ts'o5c576471997-04-29 15:29:49 +0000405 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
406 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000407 ext2fs_swap_inode(scan->fs, inode,
408 (struct ext2_inode *) scan->ptr, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000409 else
410 *inode = *((struct ext2_inode *) scan->ptr);
411 scan->ptr += scan->inode_size;
412 scan->bytes_left -= scan->inode_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000413 if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
414 retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000415 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000416
Theodore Ts'o3839e651997-04-26 13:21:57 +0000417 scan->inodes_left--;
418 scan->current_inode++;
419 *ino = scan->current_inode;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000420 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000421}
422
423/*
424 * Functions to read and write a single inode.
425 */
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000426errcode_t ext2fs_read_inode (ext2_filsys fs, ino_t ino,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000427 struct ext2_inode * inode)
428{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000429 unsigned long group, block, block_nr, offset;
430 char *ptr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000431 errcode_t retval;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000432 int clen, length, i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000433
Theodore Ts'of3db3561997-04-26 13:34:30 +0000434 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
435
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000436 /* Check to see if user has an override function */
437 if (fs->read_inode) {
438 retval = (fs->read_inode)(fs, ino, inode);
439 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
440 return retval;
441 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000442 /* Create inode cache if not present */
443 if (!fs->icache) {
444 retval = create_icache(fs);
445 if (retval)
446 return retval;
447 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000448 /* Check to see if it's in the inode cache */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000449 for (i=0; i < fs->icache->cache_size; i++) {
450 if (fs->icache->cache[i].ino == ino) {
451 *inode = fs->icache->cache[i].inode;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000452 return 0;
453 }
454 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000455 if (ino > fs->super->s_inodes_count)
456 return EXT2_ET_BAD_INODE_NUM;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000457 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000458 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
459 EXT2_INODE_SIZE(fs->super);
460 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000461 if (!fs->group_desc[(unsigned)group].bg_inode_table)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000462 return EXT2_ET_MISSING_INODE_TABLE;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000463 block_nr = fs->group_desc[(unsigned)group].bg_inode_table + block;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000464 if (block_nr != fs->icache->buffer_blk) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000465 retval = io_channel_read_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000466 fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000467 if (retval)
468 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000469 fs->icache->buffer_blk = block_nr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000470 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000471 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000472 ptr = ((char *) fs->icache->buffer) + (unsigned) offset;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000473
474 memset(inode, 0, sizeof(struct ext2_inode));
475 length = EXT2_INODE_SIZE(fs->super);
476 if (length > sizeof(struct ext2_inode))
477 length = sizeof(struct ext2_inode);
478
479 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000480 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000481 memcpy((char *) inode, ptr, clen);
482 length -= clen;
483
484 retval = io_channel_read_blk(fs->io, block_nr+1, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000485 fs->icache->buffer);
486 if (retval) {
487 fs->icache->buffer_blk = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000488 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000489 }
490 fs->icache->buffer_blk = block_nr+1;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000491
492 memcpy(((char *) inode) + clen,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000493 fs->icache->buffer, length);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000494 } else
495 memcpy((char *) inode, ptr, length);
496
Theodore Ts'o5c576471997-04-29 15:29:49 +0000497 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
498 (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000499 ext2fs_swap_inode(fs, inode, inode, 0);
500
501 /* Update the inode cache */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000502 fs->icache->cache_last = (fs->icache->cache_last + 1) %
503 fs->icache->cache_size;
504 fs->icache->cache[fs->icache->cache_last].ino = ino;
505 fs->icache->cache[fs->icache->cache_last].inode = *inode;
506
Theodore Ts'o3839e651997-04-26 13:21:57 +0000507 return 0;
508}
509
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000510errcode_t ext2fs_write_inode(ext2_filsys fs, ino_t ino,
511 struct ext2_inode * inode)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000512{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000513 unsigned long group, block, block_nr, offset;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000514 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000515 struct ext2_inode temp_inode;
516 char *ptr;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000517 int clen, length, i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000518
Theodore Ts'of3db3561997-04-26 13:34:30 +0000519 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
520
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000521 /* Check to see if user provided an override function */
522 if (fs->write_inode) {
523 retval = (fs->write_inode)(fs, ino, inode);
524 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
525 return retval;
526 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000527
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000528 /* Check to see if the inode cache needs to be updated */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000529 if (fs->icache) {
530 for (i=0; i < fs->icache->cache_size; i++) {
531 if (fs->icache->cache[i].ino == ino) {
532 fs->icache->cache[i].inode = *inode;
533 break;
534 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000535 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000536 } else {
537 retval = create_icache(fs);
538 if (retval)
539 return retval;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000540 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000541
Theodore Ts'o3839e651997-04-26 13:21:57 +0000542 if (!(fs->flags & EXT2_FLAG_RW))
543 return EXT2_ET_RO_FILSYS;
544
545 if (ino > fs->super->s_inodes_count)
546 return EXT2_ET_BAD_INODE_NUM;
547
Theodore Ts'o5c576471997-04-29 15:29:49 +0000548 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
549 (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000550 ext2fs_swap_inode(fs, &temp_inode, inode, 1);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000551 else
552 memcpy(&temp_inode, inode, sizeof(struct ext2_inode));
553
Theodore Ts'o3839e651997-04-26 13:21:57 +0000554 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000555 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
556 EXT2_INODE_SIZE(fs->super);
557 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000558 if (!fs->group_desc[(unsigned) group].bg_inode_table)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000559 return EXT2_ET_MISSING_INODE_TABLE;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000560 block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000561 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000562 ptr = (char *) fs->icache->buffer + (unsigned) offset;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000563
564 length = EXT2_INODE_SIZE(fs->super);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000565 clen = length;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000566 if (length > sizeof(struct ext2_inode))
567 length = sizeof(struct ext2_inode);
568
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000569 if (fs->icache->buffer_blk != block_nr) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000570 retval = io_channel_read_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000571 fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000572 if (retval)
573 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000574 fs->icache->buffer_blk = block_nr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000575 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000576
577 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000578 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000579 length -= clen;
580 } else {
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000581 length = 0;
582 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000583 memcpy(ptr, &temp_inode, clen);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000584 retval = io_channel_write_blk(fs->io, block_nr, 1, fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000585 if (retval)
586 return retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000587
588 if (length) {
589 retval = io_channel_read_blk(fs->io, ++block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000590 fs->icache->buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000591 if (retval) {
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000592 fs->icache->buffer_blk = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000593 return retval;
594 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000595 fs->icache->buffer_blk = block_nr;
596 memcpy(fs->icache->buffer, ((char *) &temp_inode) + clen,
597 length);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000598
599 retval = io_channel_write_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000600 fs->icache->buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000601 if (retval)
602 return retval;
603 }
604
Theodore Ts'o3839e651997-04-26 13:21:57 +0000605 fs->flags |= EXT2_FLAG_CHANGED;
606 return 0;
607}
608
609errcode_t ext2fs_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks)
610{
611 struct ext2_inode inode;
612 int i;
613 errcode_t retval;
614
Theodore Ts'of3db3561997-04-26 13:34:30 +0000615 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
616
Theodore Ts'o3839e651997-04-26 13:21:57 +0000617 if (ino > fs->super->s_inodes_count)
618 return EXT2_ET_BAD_INODE_NUM;
619
620 if (fs->get_blocks) {
621 if (!(*fs->get_blocks)(fs, ino, blocks))
622 return 0;
623 }
624 retval = ext2fs_read_inode(fs, ino, &inode);
625 if (retval)
626 return retval;
627 for (i=0; i < EXT2_N_BLOCKS; i++)
628 blocks[i] = inode.i_block[i];
629 return 0;
630}
631
632errcode_t ext2fs_check_directory(ext2_filsys fs, ino_t ino)
633{
634 struct ext2_inode inode;
635 errcode_t retval;
636
Theodore Ts'of3db3561997-04-26 13:34:30 +0000637 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
638
Theodore Ts'o3839e651997-04-26 13:21:57 +0000639 if (ino > fs->super->s_inodes_count)
640 return EXT2_ET_BAD_INODE_NUM;
641
Theodore Ts'od163b091997-10-03 17:42:28 +0000642 if (fs->check_directory) {
643 retval = (fs->check_directory)(fs, ino);
644 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
645 return retval;
646 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000647 retval = ext2fs_read_inode(fs, ino, &inode);
648 if (retval)
649 return retval;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000650 if (!LINUX_S_ISDIR(inode.i_mode))
Theodore Ts'oc555aeb1997-10-25 04:16:53 +0000651 return EXT2_NO_DIRECTORY;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000652 return 0;
653}
654