diff options
Diffstat (limited to 'e2fsprogs/ext2fs/inode.c')
-rw-r--r-- | e2fsprogs/ext2fs/inode.c | 768 |
1 files changed, 768 insertions, 0 deletions
diff --git a/e2fsprogs/ext2fs/inode.c b/e2fsprogs/ext2fs/inode.c new file mode 100644 index 000000000..2ff9fe6ca --- /dev/null +++ b/e2fsprogs/ext2fs/inode.c | |||
@@ -0,0 +1,768 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * inode.c --- utility routines to read and write inodes | ||
4 | * | ||
5 | * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. | ||
6 | * | ||
7 | * %Begin-Header% | ||
8 | * This file may be redistributed under the terms of the GNU Public | ||
9 | * License. | ||
10 | * %End-Header% | ||
11 | */ | ||
12 | |||
13 | #include <stdio.h> | ||
14 | #include <string.h> | ||
15 | #if HAVE_UNISTD_H | ||
16 | #include <unistd.h> | ||
17 | #endif | ||
18 | #if HAVE_ERRNO_H | ||
19 | #include <errno.h> | ||
20 | #endif | ||
21 | #if HAVE_SYS_STAT_H | ||
22 | #include <sys/stat.h> | ||
23 | #endif | ||
24 | #if HAVE_SYS_TYPES_H | ||
25 | #include <sys/types.h> | ||
26 | #endif | ||
27 | |||
28 | #include "ext2_fs.h" | ||
29 | #include "ext2fsP.h" | ||
30 | #include "e2image.h" | ||
31 | |||
32 | struct ext2_struct_inode_scan { | ||
33 | errcode_t magic; | ||
34 | ext2_filsys fs; | ||
35 | ext2_ino_t current_inode; | ||
36 | blk_t current_block; | ||
37 | dgrp_t current_group; | ||
38 | ext2_ino_t inodes_left; | ||
39 | blk_t blocks_left; | ||
40 | dgrp_t groups_left; | ||
41 | blk_t inode_buffer_blocks; | ||
42 | 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 | 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) { | ||
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 | 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 | |||
197 | int 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 | */ | ||
215 | static 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 | |||
232 | errcode_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 | */ | ||
247 | static 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 | */ | ||
305 | static 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 | errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino, | ||
360 | struct ext2_inode *inode, int bufsize) | ||
361 | { | ||
362 | errcode_t retval; | ||
363 | int extra_bytes = 0; | ||
364 | |||
365 | EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN); | ||
366 | |||
367 | /* | ||
368 | * Do we need to start reading a new block group? | ||
369 | */ | ||
370 | if (scan->inodes_left <= 0) { | ||
371 | force_new_group: | ||
372 | if (scan->done_group) { | ||
373 | retval = (scan->done_group) | ||
374 | (scan->fs, scan->current_group, | ||
375 | scan->done_group_data); | ||
376 | if (retval) | ||
377 | return retval; | ||
378 | } | ||
379 | if (scan->groups_left <= 0) { | ||
380 | *ino = 0; | ||
381 | return 0; | ||
382 | } | ||
383 | retval = get_next_blockgroup(scan); | ||
384 | if (retval) | ||
385 | return retval; | ||
386 | } | ||
387 | /* | ||
388 | * This is done outside the above if statement so that the | ||
389 | * check can be done for block group #0. | ||
390 | */ | ||
391 | if (scan->current_block == 0) { | ||
392 | if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) { | ||
393 | goto force_new_group; | ||
394 | } else | ||
395 | return EXT2_ET_MISSING_INODE_TABLE; | ||
396 | } | ||
397 | |||
398 | |||
399 | /* | ||
400 | * Have we run out of space in the inode buffer? If so, we | ||
401 | * need to read in more blocks. | ||
402 | */ | ||
403 | if (scan->bytes_left < scan->inode_size) { | ||
404 | memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left); | ||
405 | extra_bytes = scan->bytes_left; | ||
406 | |||
407 | retval = get_next_blocks(scan); | ||
408 | if (retval) | ||
409 | return retval; | ||
410 | #if 0 | ||
411 | /* | ||
412 | * XXX test Need check for used inode somehow. | ||
413 | * (Note: this is hard.) | ||
414 | */ | ||
415 | if (is_empty_scan(scan)) | ||
416 | goto force_new_group; | ||
417 | #endif | ||
418 | } | ||
419 | |||
420 | retval = 0; | ||
421 | if (extra_bytes) { | ||
422 | memcpy(scan->temp_buffer+extra_bytes, scan->ptr, | ||
423 | scan->inode_size - extra_bytes); | ||
424 | scan->ptr += scan->inode_size - extra_bytes; | ||
425 | scan->bytes_left -= scan->inode_size - extra_bytes; | ||
426 | |||
427 | #if BB_BIG_ENDIAN | ||
428 | if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) || | ||
429 | (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) | ||
430 | ext2fs_swap_inode_full(scan->fs, | ||
431 | (struct ext2_inode_large *) inode, | ||
432 | (struct ext2_inode_large *) scan->temp_buffer, | ||
433 | 0, bufsize); | ||
434 | else | ||
435 | #endif | ||
436 | *inode = *((struct ext2_inode *) scan->temp_buffer); | ||
437 | if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES) | ||
438 | retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; | ||
439 | scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES; | ||
440 | } else { | ||
441 | #if BB_BIG_ENDIAN | ||
442 | if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) || | ||
443 | (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) | ||
444 | ext2fs_swap_inode_full(scan->fs, | ||
445 | (struct ext2_inode_large *) inode, | ||
446 | (struct ext2_inode_large *) scan->ptr, | ||
447 | 0, bufsize); | ||
448 | else | ||
449 | #endif | ||
450 | memcpy(inode, scan->ptr, bufsize); | ||
451 | scan->ptr += scan->inode_size; | ||
452 | scan->bytes_left -= scan->inode_size; | ||
453 | if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) | ||
454 | retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; | ||
455 | } | ||
456 | |||
457 | scan->inodes_left--; | ||
458 | scan->current_inode++; | ||
459 | *ino = scan->current_inode; | ||
460 | return retval; | ||
461 | } | ||
462 | |||
463 | errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, | ||
464 | struct ext2_inode *inode) | ||
465 | { | ||
466 | return ext2fs_get_next_inode_full(scan, ino, inode, | ||
467 | sizeof(struct ext2_inode)); | ||
468 | } | ||
469 | |||
470 | /* | ||
471 | * Functions to read and write a single inode. | ||
472 | */ | ||
473 | errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, | ||
474 | struct ext2_inode * inode, int bufsize) | ||
475 | { | ||
476 | unsigned long group, block, block_nr, offset; | ||
477 | char *ptr; | ||
478 | errcode_t retval; | ||
479 | int clen, i, inodes_per_block, length; | ||
480 | io_channel io; | ||
481 | |||
482 | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); | ||
483 | |||
484 | /* Check to see if user has an override function */ | ||
485 | if (fs->read_inode) { | ||
486 | retval = (fs->read_inode)(fs, ino, inode); | ||
487 | if (retval != EXT2_ET_CALLBACK_NOTHANDLED) | ||
488 | return retval; | ||
489 | } | ||
490 | /* Create inode cache if not present */ | ||
491 | if (!fs->icache) { | ||
492 | retval = create_icache(fs); | ||
493 | if (retval) | ||
494 | return retval; | ||
495 | } | ||
496 | /* Check to see if it's in the inode cache */ | ||
497 | if (bufsize == sizeof(struct ext2_inode)) { | ||
498 | /* only old good inode can be retrieve from the cache */ | ||
499 | for (i=0; i < fs->icache->cache_size; i++) { | ||
500 | if (fs->icache->cache[i].ino == ino) { | ||
501 | *inode = fs->icache->cache[i].inode; | ||
502 | return 0; | ||
503 | } | ||
504 | } | ||
505 | } | ||
506 | if ((ino == 0) || (ino > fs->super->s_inodes_count)) | ||
507 | return EXT2_ET_BAD_INODE_NUM; | ||
508 | if (fs->flags & EXT2_FLAG_IMAGE_FILE) { | ||
509 | inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super); | ||
510 | block_nr = fs->image_header->offset_inode / fs->blocksize; | ||
511 | block_nr += (ino - 1) / inodes_per_block; | ||
512 | offset = ((ino - 1) % inodes_per_block) * | ||
513 | EXT2_INODE_SIZE(fs->super); | ||
514 | io = fs->image_io; | ||
515 | } else { | ||
516 | group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); | ||
517 | offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * | ||
518 | EXT2_INODE_SIZE(fs->super); | ||
519 | block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); | ||
520 | if (!fs->group_desc[(unsigned)group].bg_inode_table) | ||
521 | return EXT2_ET_MISSING_INODE_TABLE; | ||
522 | block_nr = fs->group_desc[(unsigned)group].bg_inode_table + | ||
523 | block; | ||
524 | io = fs->io; | ||
525 | } | ||
526 | offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); | ||
527 | |||
528 | length = EXT2_INODE_SIZE(fs->super); | ||
529 | if (bufsize < length) | ||
530 | length = bufsize; | ||
531 | |||
532 | ptr = (char *) inode; | ||
533 | while (length) { | ||
534 | clen = length; | ||
535 | if ((offset + length) > fs->blocksize) | ||
536 | clen = fs->blocksize - offset; | ||
537 | |||
538 | if (block_nr != fs->icache->buffer_blk) { | ||
539 | retval = io_channel_read_blk(io, block_nr, 1, | ||
540 | fs->icache->buffer); | ||
541 | if (retval) | ||
542 | return retval; | ||
543 | fs->icache->buffer_blk = block_nr; | ||
544 | } | ||
545 | |||
546 | memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset, | ||
547 | clen); | ||
548 | |||
549 | offset = 0; | ||
550 | length -= clen; | ||
551 | ptr += clen; | ||
552 | block_nr++; | ||
553 | } | ||
554 | |||
555 | #if BB_BIG_ENDIAN | ||
556 | if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || | ||
557 | (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) | ||
558 | ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode, | ||
559 | (struct ext2_inode_large *) inode, | ||
560 | 0, length); | ||
561 | #endif | ||
562 | |||
563 | /* Update the inode cache */ | ||
564 | fs->icache->cache_last = (fs->icache->cache_last + 1) % | ||
565 | fs->icache->cache_size; | ||
566 | fs->icache->cache[fs->icache->cache_last].ino = ino; | ||
567 | fs->icache->cache[fs->icache->cache_last].inode = *inode; | ||
568 | |||
569 | return 0; | ||
570 | } | ||
571 | |||
572 | errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino, | ||
573 | struct ext2_inode * inode) | ||
574 | { | ||
575 | return ext2fs_read_inode_full(fs, ino, inode, | ||
576 | sizeof(struct ext2_inode)); | ||
577 | } | ||
578 | |||
579 | errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, | ||
580 | struct ext2_inode * inode, int bufsize) | ||
581 | { | ||
582 | unsigned long group, block, block_nr, offset; | ||
583 | errcode_t retval = 0; | ||
584 | struct ext2_inode_large temp_inode, *w_inode; | ||
585 | char *ptr; | ||
586 | int clen, i, length; | ||
587 | |||
588 | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); | ||
589 | |||
590 | /* Check to see if user provided an override function */ | ||
591 | if (fs->write_inode) { | ||
592 | retval = (fs->write_inode)(fs, ino, inode); | ||
593 | if (retval != EXT2_ET_CALLBACK_NOTHANDLED) | ||
594 | return retval; | ||
595 | } | ||
596 | |||
597 | /* Check to see if the inode cache needs to be updated */ | ||
598 | if (fs->icache) { | ||
599 | for (i=0; i < fs->icache->cache_size; i++) { | ||
600 | if (fs->icache->cache[i].ino == ino) { | ||
601 | fs->icache->cache[i].inode = *inode; | ||
602 | break; | ||
603 | } | ||
604 | } | ||
605 | } else { | ||
606 | retval = create_icache(fs); | ||
607 | if (retval) | ||
608 | return retval; | ||
609 | } | ||
610 | |||
611 | if (!(fs->flags & EXT2_FLAG_RW)) | ||
612 | return EXT2_ET_RO_FILSYS; | ||
613 | |||
614 | if ((ino == 0) || (ino > fs->super->s_inodes_count)) | ||
615 | return EXT2_ET_BAD_INODE_NUM; | ||
616 | |||
617 | length = bufsize; | ||
618 | if (length < EXT2_INODE_SIZE(fs->super)) | ||
619 | length = EXT2_INODE_SIZE(fs->super); | ||
620 | |||
621 | if (length > (int) sizeof(struct ext2_inode_large)) { | ||
622 | w_inode = xmalloc(length); | ||
623 | } else | ||
624 | w_inode = &temp_inode; | ||
625 | memset(w_inode, 0, length); | ||
626 | |||
627 | #if BB_BIG_ENDIAN | ||
628 | if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || | ||
629 | (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) | ||
630 | ext2fs_swap_inode_full(fs, w_inode, | ||
631 | (struct ext2_inode_large *) inode, | ||
632 | 1, bufsize); | ||
633 | else | ||
634 | #endif | ||
635 | memcpy(w_inode, inode, bufsize); | ||
636 | |||
637 | group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); | ||
638 | offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * | ||
639 | EXT2_INODE_SIZE(fs->super); | ||
640 | block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); | ||
641 | if (!fs->group_desc[(unsigned) group].bg_inode_table) | ||
642 | return EXT2_ET_MISSING_INODE_TABLE; | ||
643 | block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block; | ||
644 | |||
645 | offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); | ||
646 | |||
647 | length = EXT2_INODE_SIZE(fs->super); | ||
648 | if (length > bufsize) | ||
649 | length = bufsize; | ||
650 | |||
651 | ptr = (char *) w_inode; | ||
652 | |||
653 | while (length) { | ||
654 | clen = length; | ||
655 | if ((offset + length) > fs->blocksize) | ||
656 | clen = fs->blocksize - offset; | ||
657 | |||
658 | if (fs->icache->buffer_blk != block_nr) { | ||
659 | retval = io_channel_read_blk(fs->io, block_nr, 1, | ||
660 | fs->icache->buffer); | ||
661 | if (retval) | ||
662 | goto errout; | ||
663 | fs->icache->buffer_blk = block_nr; | ||
664 | } | ||
665 | |||
666 | |||
667 | memcpy((char *) fs->icache->buffer + (unsigned) offset, | ||
668 | ptr, clen); | ||
669 | |||
670 | retval = io_channel_write_blk(fs->io, block_nr, 1, | ||
671 | fs->icache->buffer); | ||
672 | if (retval) | ||
673 | goto errout; | ||
674 | |||
675 | offset = 0; | ||
676 | ptr += clen; | ||
677 | length -= clen; | ||
678 | block_nr++; | ||
679 | } | ||
680 | |||
681 | fs->flags |= EXT2_FLAG_CHANGED; | ||
682 | errout: | ||
683 | if (w_inode && w_inode != &temp_inode) | ||
684 | free(w_inode); | ||
685 | return retval; | ||
686 | } | ||
687 | |||
688 | errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, | ||
689 | struct ext2_inode *inode) | ||
690 | { | ||
691 | return ext2fs_write_inode_full(fs, ino, inode, | ||
692 | sizeof(struct ext2_inode)); | ||
693 | } | ||
694 | |||
695 | /* | ||
696 | * This function should be called when writing a new inode. It makes | ||
697 | * sure that extra part of large inodes is initialized properly. | ||
698 | */ | ||
699 | errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, | ||
700 | struct ext2_inode *inode) | ||
701 | { | ||
702 | struct ext2_inode *buf; | ||
703 | int size = EXT2_INODE_SIZE(fs->super); | ||
704 | struct ext2_inode_large *large_inode; | ||
705 | |||
706 | if (size == sizeof(struct ext2_inode)) | ||
707 | return ext2fs_write_inode_full(fs, ino, inode, | ||
708 | sizeof(struct ext2_inode)); | ||
709 | |||
710 | buf = xmalloc(size); | ||
711 | |||
712 | memset(buf, 0, size); | ||
713 | *buf = *inode; | ||
714 | |||
715 | large_inode = (struct ext2_inode_large *) buf; | ||
716 | large_inode->i_extra_isize = sizeof(struct ext2_inode_large) - | ||
717 | EXT2_GOOD_OLD_INODE_SIZE; | ||
718 | |||
719 | return ext2fs_write_inode_full(fs, ino, buf, size); | ||
720 | } | ||
721 | |||
722 | |||
723 | errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks) | ||
724 | { | ||
725 | struct ext2_inode inode; | ||
726 | int i; | ||
727 | errcode_t retval; | ||
728 | |||
729 | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); | ||
730 | |||
731 | if (ino > fs->super->s_inodes_count) | ||
732 | return EXT2_ET_BAD_INODE_NUM; | ||
733 | |||
734 | if (fs->get_blocks) { | ||
735 | if (!(*fs->get_blocks)(fs, ino, blocks)) | ||
736 | return 0; | ||
737 | } | ||
738 | retval = ext2fs_read_inode(fs, ino, &inode); | ||
739 | if (retval) | ||
740 | return retval; | ||
741 | for (i=0; i < EXT2_N_BLOCKS; i++) | ||
742 | blocks[i] = inode.i_block[i]; | ||
743 | return 0; | ||
744 | } | ||
745 | |||
746 | errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino) | ||
747 | { | ||
748 | struct ext2_inode inode; | ||
749 | errcode_t retval; | ||
750 | |||
751 | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); | ||
752 | |||
753 | if (ino > fs->super->s_inodes_count) | ||
754 | return EXT2_ET_BAD_INODE_NUM; | ||
755 | |||
756 | if (fs->check_directory) { | ||
757 | retval = (fs->check_directory)(fs, ino); | ||
758 | if (retval != EXT2_ET_CALLBACK_NOTHANDLED) | ||
759 | return retval; | ||
760 | } | ||
761 | retval = ext2fs_read_inode(fs, ino, &inode); | ||
762 | if (retval) | ||
763 | return retval; | ||
764 | if (!LINUX_S_ISDIR(inode.i_mode)) | ||
765 | return EXT2_ET_NO_DIRECTORY; | ||
766 | return 0; | ||
767 | } | ||
768 | |||