blob: 0edee7f4736f9c643ec1f372bd853dab79cdcdb3 [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;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000374 }
Theodore Ts'o218a4861998-02-21 01:41:39 +0000375 /*
376 * This is done outside the above if statement so that the
377 * check can be done for block group #0.
378 */
379 if (scan->current_block == 0) {
380 if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
381 goto retry;
382 } else
383 return EXT2_ET_MISSING_INODE_TABLE;
384 }
385
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000386
387 /*
388 * Have we run out of space in the inode buffer? If so, we
389 * need to read in more blocks.
390 */
391 if (scan->bytes_left < scan->inode_size) {
392 memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
393 extra_bytes = scan->bytes_left;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000394
395 retval = get_next_blocks(scan);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000396 if (retval)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000397 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000398 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000399
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000400 retval = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000401 if (extra_bytes) {
402 memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
403 scan->inode_size - extra_bytes);
404 scan->ptr += scan->inode_size - extra_bytes;
405 scan->bytes_left -= scan->inode_size - extra_bytes;
406
Theodore Ts'o5c576471997-04-29 15:29:49 +0000407 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
408 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000409 ext2fs_swap_inode(scan->fs, inode,
410 (struct ext2_inode *) scan->temp_buffer, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000411 else
412 *inode = *((struct ext2_inode *) scan->temp_buffer);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000413 if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
414 retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
415 scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000416 } else {
Theodore Ts'o5c576471997-04-29 15:29:49 +0000417 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
418 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000419 ext2fs_swap_inode(scan->fs, inode,
420 (struct ext2_inode *) scan->ptr, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000421 else
422 *inode = *((struct ext2_inode *) scan->ptr);
423 scan->ptr += scan->inode_size;
424 scan->bytes_left -= scan->inode_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000425 if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
426 retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000427 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000428
Theodore Ts'o3839e651997-04-26 13:21:57 +0000429 scan->inodes_left--;
430 scan->current_inode++;
431 *ino = scan->current_inode;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000432 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000433}
434
435/*
436 * Functions to read and write a single inode.
437 */
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000438errcode_t ext2fs_read_inode (ext2_filsys fs, ino_t ino,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000439 struct ext2_inode * inode)
440{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000441 unsigned long group, block, block_nr, offset;
442 char *ptr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000443 errcode_t retval;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000444 int clen, length, i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000445
Theodore Ts'of3db3561997-04-26 13:34:30 +0000446 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
447
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000448 /* Check to see if user has an override function */
449 if (fs->read_inode) {
450 retval = (fs->read_inode)(fs, ino, inode);
451 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
452 return retval;
453 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000454 /* Create inode cache if not present */
455 if (!fs->icache) {
456 retval = create_icache(fs);
457 if (retval)
458 return retval;
459 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000460 /* Check to see if it's in the inode cache */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000461 for (i=0; i < fs->icache->cache_size; i++) {
462 if (fs->icache->cache[i].ino == ino) {
463 *inode = fs->icache->cache[i].inode;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000464 return 0;
465 }
466 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000467 if (ino > fs->super->s_inodes_count)
468 return EXT2_ET_BAD_INODE_NUM;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000469 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000470 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
471 EXT2_INODE_SIZE(fs->super);
472 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000473 if (!fs->group_desc[(unsigned)group].bg_inode_table)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000474 return EXT2_ET_MISSING_INODE_TABLE;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000475 block_nr = fs->group_desc[(unsigned)group].bg_inode_table + block;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000476 if (block_nr != fs->icache->buffer_blk) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000477 retval = io_channel_read_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000478 fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000479 if (retval)
480 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000481 fs->icache->buffer_blk = block_nr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000482 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000483 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000484 ptr = ((char *) fs->icache->buffer) + (unsigned) offset;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000485
486 memset(inode, 0, sizeof(struct ext2_inode));
487 length = EXT2_INODE_SIZE(fs->super);
488 if (length > sizeof(struct ext2_inode))
489 length = sizeof(struct ext2_inode);
490
491 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000492 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000493 memcpy((char *) inode, ptr, clen);
494 length -= clen;
495
496 retval = io_channel_read_blk(fs->io, block_nr+1, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000497 fs->icache->buffer);
498 if (retval) {
499 fs->icache->buffer_blk = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000500 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000501 }
502 fs->icache->buffer_blk = block_nr+1;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000503
504 memcpy(((char *) inode) + clen,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000505 fs->icache->buffer, length);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000506 } else
507 memcpy((char *) inode, ptr, length);
508
Theodore Ts'o5c576471997-04-29 15:29:49 +0000509 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
510 (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000511 ext2fs_swap_inode(fs, inode, inode, 0);
512
513 /* Update the inode cache */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000514 fs->icache->cache_last = (fs->icache->cache_last + 1) %
515 fs->icache->cache_size;
516 fs->icache->cache[fs->icache->cache_last].ino = ino;
517 fs->icache->cache[fs->icache->cache_last].inode = *inode;
518
Theodore Ts'o3839e651997-04-26 13:21:57 +0000519 return 0;
520}
521
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000522errcode_t ext2fs_write_inode(ext2_filsys fs, ino_t ino,
523 struct ext2_inode * inode)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000524{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000525 unsigned long group, block, block_nr, offset;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000526 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000527 struct ext2_inode temp_inode;
528 char *ptr;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000529 int clen, length, i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000530
Theodore Ts'of3db3561997-04-26 13:34:30 +0000531 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
532
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000533 /* Check to see if user provided an override function */
534 if (fs->write_inode) {
535 retval = (fs->write_inode)(fs, ino, inode);
536 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
537 return retval;
538 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000539
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000540 /* Check to see if the inode cache needs to be updated */
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000541 if (fs->icache) {
542 for (i=0; i < fs->icache->cache_size; i++) {
543 if (fs->icache->cache[i].ino == ino) {
544 fs->icache->cache[i].inode = *inode;
545 break;
546 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000547 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000548 } else {
549 retval = create_icache(fs);
550 if (retval)
551 return retval;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000552 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000553
Theodore Ts'o3839e651997-04-26 13:21:57 +0000554 if (!(fs->flags & EXT2_FLAG_RW))
555 return EXT2_ET_RO_FILSYS;
556
557 if (ino > fs->super->s_inodes_count)
558 return EXT2_ET_BAD_INODE_NUM;
559
Theodore Ts'o5c576471997-04-29 15:29:49 +0000560 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
561 (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000562 ext2fs_swap_inode(fs, &temp_inode, inode, 1);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000563 else
564 memcpy(&temp_inode, inode, sizeof(struct ext2_inode));
565
Theodore Ts'o3839e651997-04-26 13:21:57 +0000566 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000567 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
568 EXT2_INODE_SIZE(fs->super);
569 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000570 if (!fs->group_desc[(unsigned) group].bg_inode_table)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000571 return EXT2_ET_MISSING_INODE_TABLE;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000572 block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000573 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000574 ptr = (char *) fs->icache->buffer + (unsigned) offset;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000575
576 length = EXT2_INODE_SIZE(fs->super);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000577 clen = length;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000578 if (length > sizeof(struct ext2_inode))
579 length = sizeof(struct ext2_inode);
580
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000581 if (fs->icache->buffer_blk != block_nr) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000582 retval = io_channel_read_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000583 fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000584 if (retval)
585 return retval;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000586 fs->icache->buffer_blk = block_nr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000587 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000588
589 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000590 clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000591 length -= clen;
592 } else {
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000593 length = 0;
594 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000595 memcpy(ptr, &temp_inode, clen);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000596 retval = io_channel_write_blk(fs->io, block_nr, 1, fs->icache->buffer);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000597 if (retval)
598 return retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000599
600 if (length) {
601 retval = io_channel_read_blk(fs->io, ++block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000602 fs->icache->buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000603 if (retval) {
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000604 fs->icache->buffer_blk = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000605 return retval;
606 }
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000607 fs->icache->buffer_blk = block_nr;
608 memcpy(fs->icache->buffer, ((char *) &temp_inode) + clen,
609 length);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000610
611 retval = io_channel_write_blk(fs->io, block_nr, 1,
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000612 fs->icache->buffer);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000613 if (retval)
614 return retval;
615 }
616
Theodore Ts'o3839e651997-04-26 13:21:57 +0000617 fs->flags |= EXT2_FLAG_CHANGED;
618 return 0;
619}
620
621errcode_t ext2fs_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks)
622{
623 struct ext2_inode inode;
624 int i;
625 errcode_t retval;
626
Theodore Ts'of3db3561997-04-26 13:34:30 +0000627 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
628
Theodore Ts'o3839e651997-04-26 13:21:57 +0000629 if (ino > fs->super->s_inodes_count)
630 return EXT2_ET_BAD_INODE_NUM;
631
632 if (fs->get_blocks) {
633 if (!(*fs->get_blocks)(fs, ino, blocks))
634 return 0;
635 }
636 retval = ext2fs_read_inode(fs, ino, &inode);
637 if (retval)
638 return retval;
639 for (i=0; i < EXT2_N_BLOCKS; i++)
640 blocks[i] = inode.i_block[i];
641 return 0;
642}
643
644errcode_t ext2fs_check_directory(ext2_filsys fs, ino_t ino)
645{
646 struct ext2_inode inode;
647 errcode_t retval;
648
Theodore Ts'of3db3561997-04-26 13:34:30 +0000649 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
650
Theodore Ts'o3839e651997-04-26 13:21:57 +0000651 if (ino > fs->super->s_inodes_count)
652 return EXT2_ET_BAD_INODE_NUM;
653
Theodore Ts'od163b091997-10-03 17:42:28 +0000654 if (fs->check_directory) {
655 retval = (fs->check_directory)(fs, ino);
656 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
657 return retval;
658 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000659 retval = ext2fs_read_inode(fs, ino, &inode);
660 if (retval)
661 return retval;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000662 if (!LINUX_S_ISDIR(inode.i_mode))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000663 return EXT2_ET_NO_DIRECTORY;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000664 return 0;
665}
666