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