inode.c revision 5df55d7f847e29d23227592a0bb23daad1a61500
1/*
2 * inode.c --- utility routines to read and write inodes
3 *
4 * 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%
10 */
11
12#include <stdio.h>
13#include <string.h>
14#if HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17#if HAVE_SYS_STAT_H
18#include <sys/stat.h>
19#endif
20#if HAVE_SYS_TYPES_H
21#include <sys/types.h>
22#endif
23
24#include "ext2_fs.h"
25#include "ext2fsP.h"
26#include "e2image.h"
27
28struct ext2_struct_inode_scan {
29	errcode_t		magic;
30	ext2_filsys		fs;
31	ext2_ino_t		current_inode;
32	blk_t			current_block;
33	dgrp_t			current_group;
34	ext2_ino_t		inodes_left;
35	blk_t			blocks_left;
36	dgrp_t			groups_left;
37	blk_t			inode_buffer_blocks;
38	char *			inode_buffer;
39	int			inode_size;
40	char *			ptr;
41	int			bytes_left;
42	char			*temp_buffer;
43	errcode_t		(*done_group)(ext2_filsys fs,
44					      ext2_inode_scan scan,
45					      dgrp_t group,
46					      void * priv_data);
47	void *			done_group_data;
48	int			bad_block_ptr;
49	int			scan_flags;
50	int			reserved[6];
51};
52
53/*
54 * This routine flushes the icache, if it exists.
55 */
56errcode_t ext2fs_flush_icache(ext2_filsys fs)
57{
58	int	i;
59
60	if (!fs->icache)
61		return 0;
62
63	for (i=0; i < fs->icache->cache_size; i++)
64		fs->icache->cache[i].ino = 0;
65
66	return 0;
67}
68
69static errcode_t create_icache(ext2_filsys fs)
70{
71	errcode_t	retval;
72
73	if (fs->icache)
74		return 0;
75	retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache),
76				(void **) &fs->icache);
77	if (retval)
78		return retval;
79
80	memset(fs->icache, 0, sizeof(struct ext2_inode_cache));
81	retval = ext2fs_get_mem(fs->blocksize, (void **) &fs->icache->buffer);
82	if (retval) {
83		ext2fs_free_mem((void **) &fs->icache);
84		return retval;
85	}
86	fs->icache->buffer_blk = 0;
87	fs->icache->cache_last = -1;
88	fs->icache->cache_size = 4;
89	fs->icache->refcount = 1;
90	retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache_ent)
91				* fs->icache->cache_size,
92				(void **) &fs->icache->cache);
93	if (retval) {
94		ext2fs_free_mem((void **) &fs->icache->buffer);
95		ext2fs_free_mem((void **) &fs->icache);
96		return retval;
97	}
98	ext2fs_flush_icache(fs);
99	return 0;
100}
101
102errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
103				 ext2_inode_scan *ret_scan)
104{
105	ext2_inode_scan	scan;
106	errcode_t	retval;
107	errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks);
108
109	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
110
111	/*
112	 * If fs->badblocks isn't set, then set it --- since the inode
113	 * scanning functions require it.
114	 */
115	if (fs->badblocks == 0) {
116		/*
117		 * Temporarly save fs->get_blocks and set it to zero,
118		 * for compatibility with old e2fsck's.
119		 */
120		save_get_blocks = fs->get_blocks;
121		fs->get_blocks = 0;
122		retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
123		if (retval && fs->badblocks) {
124			badblocks_list_free(fs->badblocks);
125			fs->badblocks = 0;
126		}
127		fs->get_blocks = save_get_blocks;
128	}
129
130	retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan),
131				(void **) &scan);
132	if (retval)
133		return retval;
134	memset(scan, 0, sizeof(struct ext2_struct_inode_scan));
135
136	scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
137	scan->fs = fs;
138	scan->inode_size = EXT2_INODE_SIZE(fs->super);
139	scan->bytes_left = 0;
140	scan->current_group = 0;
141	scan->groups_left = fs->group_desc_count - 1;
142	scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
143	scan->current_block = scan->fs->
144		group_desc[scan->current_group].bg_inode_table;
145	scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
146	scan->blocks_left = scan->fs->inode_blocks_per_group;
147	retval = ext2fs_get_mem((size_t) (scan->inode_buffer_blocks *
148					  fs->blocksize),
149				(void **) &scan->inode_buffer);
150	scan->done_group = 0;
151	scan->done_group_data = 0;
152	scan->bad_block_ptr = 0;
153	if (retval) {
154		ext2fs_free_mem((void **) &scan);
155		return retval;
156	}
157	retval = ext2fs_get_mem(scan->inode_size,
158				(void **) &scan->temp_buffer);
159	if (retval) {
160		ext2fs_free_mem((void **) &scan->inode_buffer);
161		ext2fs_free_mem((void **) &scan);
162		return retval;
163	}
164	if (scan->fs->badblocks && scan->fs->badblocks->num)
165		scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
166	*ret_scan = scan;
167	return 0;
168}
169
170void ext2fs_close_inode_scan(ext2_inode_scan scan)
171{
172	if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
173		return;
174
175	ext2fs_free_mem((void **) &scan->inode_buffer);
176	scan->inode_buffer = NULL;
177	ext2fs_free_mem((void **) &scan->temp_buffer);
178	scan->temp_buffer = NULL;
179	ext2fs_free_mem((void **) &scan);
180	return;
181}
182
183void ext2fs_set_inode_callback(ext2_inode_scan scan,
184			       errcode_t (*done_group)(ext2_filsys fs,
185						       ext2_inode_scan scan,
186						       dgrp_t group,
187						       void * priv_data),
188			       void *done_group_data)
189{
190	if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
191		return;
192
193	scan->done_group = done_group;
194	scan->done_group_data = done_group_data;
195}
196
197int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
198			    int clear_flags)
199{
200	int	old_flags;
201
202	if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
203		return 0;
204
205	old_flags = scan->scan_flags;
206	scan->scan_flags &= ~clear_flags;
207	scan->scan_flags |= set_flags;
208	return old_flags;
209}
210
211/*
212 * This function is called by ext2fs_get_next_inode when it needs to
213 * get ready to read in a new blockgroup.
214 */
215static errcode_t get_next_blockgroup(ext2_inode_scan scan)
216{
217	scan->current_group++;
218	scan->groups_left--;
219
220	scan->current_block = scan->fs->
221		group_desc[scan->current_group].bg_inode_table;
222
223	scan->current_inode = scan->current_group *
224		EXT2_INODES_PER_GROUP(scan->fs->super);
225
226	scan->bytes_left = 0;
227	scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
228	scan->blocks_left = scan->fs->inode_blocks_per_group;
229	return 0;
230}
231
232errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
233					    int	group)
234{
235	scan->current_group = group - 1;
236	scan->groups_left = scan->fs->group_desc_count - group;
237	return get_next_blockgroup(scan);
238}
239
240/*
241 * This function is called by get_next_blocks() to check for bad
242 * blocks in the inode table.
243 *
244 * This function assumes that badblocks_list->list is sorted in
245 * increasing order.
246 */
247static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
248					    blk_t *num_blocks)
249{
250	blk_t	blk = scan->current_block;
251	badblocks_list	bb = scan->fs->badblocks;
252
253	/*
254	 * If the inode table is missing, then obviously there are no
255	 * bad blocks.  :-)
256	 */
257	if (blk == 0)
258		return 0;
259
260	/*
261	 * If the current block is greater than the bad block listed
262	 * in the bad block list, then advance the pointer until this
263	 * is no longer the case.  If we run out of bad blocks, then
264	 * we don't need to do any more checking!
265	 */
266	while (blk > bb->list[scan->bad_block_ptr]) {
267		if (++scan->bad_block_ptr >= bb->num) {
268			scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
269			return 0;
270		}
271	}
272
273	/*
274	 * If the current block is equal to the bad block listed in
275	 * the bad block list, then handle that one block specially.
276	 * (We could try to handle runs of bad blocks, but that
277	 * only increases CPU efficiency by a small amount, at the
278	 * expense of a huge expense of code complexity, and for an
279	 * uncommon case at that.)
280	 */
281	if (blk == bb->list[scan->bad_block_ptr]) {
282		scan->scan_flags |= EXT2_SF_BAD_INODE_BLK;
283		*num_blocks = 1;
284		if (++scan->bad_block_ptr >= bb->num)
285			scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
286		return 0;
287	}
288
289	/*
290	 * If there is a bad block in the range that we're about to
291	 * read in, adjust the number of blocks to read so that we we
292	 * don't read in the bad block.  (Then the next block to read
293	 * will be the bad block, which is handled in the above case.)
294	 */
295	if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr])
296		*num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk);
297
298	return 0;
299}
300
301/*
302 * This function is called by ext2fs_get_next_inode when it needs to
303 * read in more blocks from the current blockgroup's inode table.
304 */
305static errcode_t get_next_blocks(ext2_inode_scan scan)
306{
307	blk_t		num_blocks;
308	errcode_t	retval;
309
310	/*
311	 * Figure out how many blocks to read; we read at most
312	 * inode_buffer_blocks, and perhaps less if there aren't that
313	 * many blocks left to read.
314	 */
315	num_blocks = scan->inode_buffer_blocks;
316	if (num_blocks > scan->blocks_left)
317		num_blocks = scan->blocks_left;
318
319	/*
320	 * If the past block "read" was a bad block, then mark the
321	 * left-over extra bytes as also being bad.
322	 */
323	if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) {
324		if (scan->bytes_left)
325			scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES;
326		scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK;
327	}
328
329	/*
330	 * Do inode bad block processing, if necessary.
331	 */
332	if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) {
333		retval = check_for_inode_bad_blocks(scan, &num_blocks);
334		if (retval)
335			return retval;
336	}
337
338	if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) ||
339	    (scan->current_block == 0)) {
340		memset(scan->inode_buffer, 0,
341		       (size_t) num_blocks * scan->fs->blocksize);
342	} else {
343		retval = io_channel_read_blk(scan->fs->io,
344					     scan->current_block,
345					     (int) num_blocks,
346					     scan->inode_buffer);
347		if (retval)
348			return EXT2_ET_NEXT_INODE_READ;
349	}
350	scan->ptr = scan->inode_buffer;
351	scan->bytes_left = num_blocks * scan->fs->blocksize;
352
353	scan->blocks_left -= num_blocks;
354	if (scan->current_block)
355		scan->current_block += num_blocks;
356	return 0;
357}
358
359#if 0
360/*
361 * Returns 1 if the entire inode_buffer has a non-zero size and
362 * contains all zeros.  (Not just deleted inodes, since that means
363 * that part of the inode table was used at one point; we want all
364 * zeros, which means that the inode table is pristine.)
365 */
366static inline int is_empty_scan(ext2_inode_scan scan)
367{
368	int	i;
369
370	if (scan->bytes_left == 0)
371		return 0;
372
373	for (i=0; i < scan->bytes_left; i++)
374		if (scan->ptr[i])
375			return 0;
376	return 1;
377}
378#endif
379
380errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino,
381				struct ext2_inode *inode)
382{
383	errcode_t	retval;
384	int		extra_bytes = 0;
385
386	EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
387
388	/*
389	 * Do we need to start reading a new block group?
390	 */
391	if (scan->inodes_left <= 0) {
392	force_new_group:
393		if (scan->done_group) {
394			retval = (scan->done_group)
395				(scan->fs, scan, scan->current_group,
396				 scan->done_group_data);
397			if (retval)
398				return retval;
399		}
400		if (scan->groups_left <= 0) {
401			*ino = 0;
402			return 0;
403		}
404		retval = get_next_blockgroup(scan);
405		if (retval)
406			return retval;
407	}
408	/*
409	 * This is done outside the above if statement so that the
410	 * check can be done for block group #0.
411	 */
412	if (scan->current_block == 0) {
413		if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
414			goto force_new_group;
415		} else
416			return EXT2_ET_MISSING_INODE_TABLE;
417	}
418
419
420	/*
421	 * Have we run out of space in the inode buffer?  If so, we
422	 * need to read in more blocks.
423	 */
424	if (scan->bytes_left < scan->inode_size) {
425		memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
426		extra_bytes = scan->bytes_left;
427
428		retval = get_next_blocks(scan);
429		if (retval)
430			return retval;
431#if 0
432		/*
433		 * XXX test  Need check for used inode somehow.
434		 * (Note: this is hard.)
435		 */
436		if (is_empty_scan(scan))
437			goto force_new_group;
438#endif
439	}
440
441	retval = 0;
442	if (extra_bytes) {
443		memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
444		       scan->inode_size - extra_bytes);
445		scan->ptr += scan->inode_size - extra_bytes;
446		scan->bytes_left -= scan->inode_size - extra_bytes;
447
448#ifdef EXT2FS_ENABLE_SWAPFS
449		if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
450		    (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
451			ext2fs_swap_inode(scan->fs, inode,
452				 (struct ext2_inode *) scan->temp_buffer, 0);
453		else
454#endif
455			*inode = *((struct ext2_inode *) scan->temp_buffer);
456		if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
457			retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
458		scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
459	} else {
460#ifdef EXT2FS_ENABLE_SWAPFS
461		if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
462		    (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
463			ext2fs_swap_inode(scan->fs, inode,
464				 (struct ext2_inode *) scan->ptr, 0);
465		else
466#endif
467			*inode = *((struct ext2_inode *) scan->ptr);
468		scan->ptr += scan->inode_size;
469		scan->bytes_left -= scan->inode_size;
470		if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
471			retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
472	}
473
474	scan->inodes_left--;
475	scan->current_inode++;
476	*ino = scan->current_inode;
477	return retval;
478}
479
480/*
481 * Functions to read and write a single inode.
482 */
483errcode_t ext2fs_read_inode (ext2_filsys fs, ext2_ino_t ino,
484			     struct ext2_inode * inode)
485{
486	unsigned long 	group, block, block_nr, offset;
487	char 		*ptr;
488	errcode_t	retval;
489	int 		clen, length, i, inodes_per_block;
490
491	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
492
493	/* Check to see if user has an override function */
494	if (fs->read_inode) {
495		retval = (fs->read_inode)(fs, ino, inode);
496		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
497			return retval;
498	}
499	/* Create inode cache if not present */
500	if (!fs->icache) {
501		retval = create_icache(fs);
502		if (retval)
503			return retval;
504	}
505	/* Check to see if it's in the inode cache */
506	for (i=0; i < fs->icache->cache_size; i++) {
507		if (fs->icache->cache[i].ino == ino) {
508			*inode = fs->icache->cache[i].inode;
509			return 0;
510		}
511	}
512	if ((ino == 0) || (ino > fs->super->s_inodes_count))
513		return EXT2_ET_BAD_INODE_NUM;
514	if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
515		inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super);
516		block_nr = fs->image_header->offset_inode / fs->blocksize;
517		block_nr += (ino - 1) / inodes_per_block;
518		offset = ((ino - 1) % inodes_per_block) *
519			EXT2_INODE_SIZE(fs->super);
520	} else {
521		group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
522		offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
523			EXT2_INODE_SIZE(fs->super);
524		block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
525		if (!fs->group_desc[(unsigned)group].bg_inode_table)
526			return EXT2_ET_MISSING_INODE_TABLE;
527		block_nr = fs->group_desc[(unsigned)group].bg_inode_table +
528			block;
529	}
530	if (block_nr != fs->icache->buffer_blk) {
531		retval = io_channel_read_blk(fs->io, block_nr, 1,
532					     fs->icache->buffer);
533		if (retval)
534			return retval;
535		fs->icache->buffer_blk = block_nr;
536	}
537	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
538	ptr = ((char *) fs->icache->buffer) + (unsigned) offset;
539
540	memset(inode, 0, sizeof(struct ext2_inode));
541	length = EXT2_INODE_SIZE(fs->super);
542	if (length > sizeof(struct ext2_inode))
543		length = sizeof(struct ext2_inode);
544
545	if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
546		clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
547		memcpy((char *) inode, ptr, clen);
548		length -= clen;
549
550		retval = io_channel_read_blk(fs->io, block_nr+1, 1,
551					     fs->icache->buffer);
552		if (retval) {
553			fs->icache->buffer_blk = 0;
554			return retval;
555		}
556		fs->icache->buffer_blk = block_nr+1;
557
558		memcpy(((char *) inode) + clen,
559		       fs->icache->buffer, length);
560	} else
561		memcpy((char *) inode, ptr, length);
562
563#ifdef EXT2FS_ENABLE_SWAPFS
564	if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
565	    (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
566		ext2fs_swap_inode(fs, inode, inode, 0);
567#endif
568
569	/* Update the inode cache */
570	fs->icache->cache_last = (fs->icache->cache_last + 1) %
571		fs->icache->cache_size;
572	fs->icache->cache[fs->icache->cache_last].ino = ino;
573	fs->icache->cache[fs->icache->cache_last].inode = *inode;
574
575	return 0;
576}
577
578errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
579			     struct ext2_inode * inode)
580{
581	unsigned long group, block, block_nr, offset;
582	errcode_t	retval;
583	struct ext2_inode temp_inode;
584	char *ptr;
585	int clen, length, i;
586
587	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
588
589	/* Check to see if user provided an override function */
590	if (fs->write_inode) {
591		retval = (fs->write_inode)(fs, ino, inode);
592		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
593			return retval;
594	}
595
596	/* Check to see if the inode cache needs to be updated */
597	if (fs->icache) {
598		for (i=0; i < fs->icache->cache_size; i++) {
599			if (fs->icache->cache[i].ino == ino) {
600				fs->icache->cache[i].inode = *inode;
601				break;
602			}
603		}
604	} else {
605		retval = create_icache(fs);
606		if (retval)
607			return retval;
608	}
609
610	if (!(fs->flags & EXT2_FLAG_RW))
611		return EXT2_ET_RO_FILSYS;
612
613	if ((ino == 0) || (ino > fs->super->s_inodes_count))
614		return EXT2_ET_BAD_INODE_NUM;
615
616#ifdef EXT2FS_ENABLE_SWAPFS
617	if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
618	    (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
619		ext2fs_swap_inode(fs, &temp_inode, inode, 1);
620	else
621#endif
622		memcpy(&temp_inode, inode, sizeof(struct ext2_inode));
623
624	group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
625	offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
626		EXT2_INODE_SIZE(fs->super);
627	block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
628	if (!fs->group_desc[(unsigned) group].bg_inode_table)
629		return EXT2_ET_MISSING_INODE_TABLE;
630	block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block;
631	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
632	ptr = (char *) fs->icache->buffer + (unsigned) offset;
633
634	length = EXT2_INODE_SIZE(fs->super);
635	clen = length;
636	if (length > sizeof(struct ext2_inode))
637		length = sizeof(struct ext2_inode);
638
639	if (fs->icache->buffer_blk != block_nr) {
640		retval = io_channel_read_blk(fs->io, block_nr, 1,
641					     fs->icache->buffer);
642		if (retval)
643			return retval;
644		fs->icache->buffer_blk = block_nr;
645	}
646
647	if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
648		clen = (int) (EXT2_BLOCK_SIZE(fs->super) - offset);
649		length -= clen;
650	} else {
651		length = 0;
652	}
653	memcpy(ptr, &temp_inode, clen);
654	retval = io_channel_write_blk(fs->io, block_nr, 1, fs->icache->buffer);
655	if (retval)
656		return retval;
657
658	if (length) {
659		retval = io_channel_read_blk(fs->io, ++block_nr, 1,
660					     fs->icache->buffer);
661		if (retval) {
662			fs->icache->buffer_blk = 0;
663			return retval;
664		}
665		fs->icache->buffer_blk = block_nr;
666		memcpy(fs->icache->buffer, ((char *) &temp_inode) + clen,
667		       length);
668
669		retval = io_channel_write_blk(fs->io, block_nr, 1,
670					      fs->icache->buffer);
671		if (retval)
672			return retval;
673	}
674
675	fs->flags |= EXT2_FLAG_CHANGED;
676	return 0;
677}
678
679errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks)
680{
681	struct ext2_inode	inode;
682	int			i;
683	errcode_t		retval;
684
685	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
686
687	if (ino > fs->super->s_inodes_count)
688		return EXT2_ET_BAD_INODE_NUM;
689
690	if (fs->get_blocks) {
691		if (!(*fs->get_blocks)(fs, ino, blocks))
692			return 0;
693	}
694	retval = ext2fs_read_inode(fs, ino, &inode);
695	if (retval)
696		return retval;
697	for (i=0; i < EXT2_N_BLOCKS; i++)
698		blocks[i] = inode.i_block[i];
699	return 0;
700}
701
702errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino)
703{
704	struct	ext2_inode	inode;
705	errcode_t		retval;
706
707	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
708
709	if (ino > fs->super->s_inodes_count)
710		return EXT2_ET_BAD_INODE_NUM;
711
712	if (fs->check_directory) {
713		retval = (fs->check_directory)(fs, ino);
714		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
715			return retval;
716	}
717	retval = ext2fs_read_inode(fs, ino, &inode);
718	if (retval)
719		return retval;
720	if (!LINUX_S_ISDIR(inode.i_mode))
721		return EXT2_ET_NO_DIRECTORY;
722	return 0;
723}
724
725