blob: 8743476a02b9cff7c8d4acda0fde61185f3e8086 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
2 * inode.c --- utility routines to read and write inodes
3 *
4 * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed
5 * under the terms of the GNU Public License.
6 */
7
8#include <stdio.h>
9#include <string.h>
10#include <unistd.h>
11#include <stdlib.h>
12#include <sys/stat.h>
13#include <sys/types.h>
Theodore Ts'o50e1e101997-04-26 13:58:21 +000014#if HAVE_ERRNO_H
15#include <errno.h>
16#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000017
Theodore Ts'o3839e651997-04-26 13:21:57 +000018#include <linux/ext2_fs.h>
19
20#include "ext2fs.h"
21
22errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
23 ext2_inode_scan *ret_scan)
24{
25 ext2_inode_scan scan;
26
Theodore Ts'of3db3561997-04-26 13:34:30 +000027 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
28
Theodore Ts'o3839e651997-04-26 13:21:57 +000029 scan = (ext2_inode_scan) malloc(sizeof(struct ext2_struct_inode_scan));
30 if (!scan)
31 return ENOMEM;
32 memset(scan, 0, sizeof(struct ext2_struct_inode_scan));
33
Theodore Ts'of3db3561997-04-26 13:34:30 +000034 scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
Theodore Ts'o3839e651997-04-26 13:21:57 +000035 scan->fs = fs;
Theodore Ts'o7f88b041997-04-26 14:48:50 +000036 scan->inode_size = EXT2_INODE_SIZE(fs->super);
37 scan->bytes_left = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +000038 scan->current_group = -1;
39 scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
40 scan->groups_left = fs->group_desc_count;
41 scan->inode_buffer = malloc(scan->inode_buffer_blocks * fs->blocksize);
Theodore Ts'of3db3561997-04-26 13:34:30 +000042 scan->done_group = 0;
43 scan->done_group_data = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +000044 if (!scan->inode_buffer) {
45 free(scan);
46 return ENOMEM;
47 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +000048 scan->temp_buffer = malloc(scan->inode_size);
49 if (!scan->temp_buffer) {
50 free(scan->inode_buffer);
51 free(scan);
52 return ENOMEM;
53 }
Theodore Ts'o3839e651997-04-26 13:21:57 +000054 *ret_scan = scan;
55 return 0;
56}
57
58void ext2fs_close_inode_scan(ext2_inode_scan scan)
59{
Theodore Ts'of3db3561997-04-26 13:34:30 +000060 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
61 return;
62
Theodore Ts'o3839e651997-04-26 13:21:57 +000063 free(scan->inode_buffer);
64 scan->inode_buffer = NULL;
Theodore Ts'o7f88b041997-04-26 14:48:50 +000065 free(scan->temp_buffer);
66 scan->temp_buffer = NULL;
Theodore Ts'o3839e651997-04-26 13:21:57 +000067 free(scan);
68 return;
69}
70
Theodore Ts'of3db3561997-04-26 13:34:30 +000071void ext2fs_set_inode_callback(ext2_inode_scan scan,
72 errcode_t (*done_group)(ext2_filsys fs,
73 ext2_inode_scan scan,
74 dgrp_t group,
75 void * private),
76 void *done_group_data)
77{
78 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
79 return;
80
81 scan->done_group = done_group;
82 scan->done_group_data = done_group_data;
83}
84
Theodore Ts'o3839e651997-04-26 13:21:57 +000085errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ino_t *ino,
86 struct ext2_inode *inode)
87{
88 errcode_t retval;
89 int num_blocks;
Theodore Ts'o7f88b041997-04-26 14:48:50 +000090 int extra_bytes = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +000091
Theodore Ts'of3db3561997-04-26 13:34:30 +000092 EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
93
Theodore Ts'o7f88b041997-04-26 14:48:50 +000094 /*
95 * Do we need to start reading a new block group?
96 */
Theodore Ts'o3839e651997-04-26 13:21:57 +000097 if (scan->inodes_left <= 0) {
Theodore Ts'o7f88b041997-04-26 14:48:50 +000098 if (scan->done_group) {
99 retval = (scan->done_group)
100 (scan->fs, scan,
101 scan->current_group,
102 scan->done_group_data);
103 if (retval)
104 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000105 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000106 if (scan->groups_left <= 0) {
107 *ino = 0;
108 return 0;
109 }
110 scan->current_group++;
111 scan->groups_left--;
112
113 scan->current_block = scan->fs->
114 group_desc[scan->current_group].bg_inode_table;
115
116 if (scan->current_block == 0)
117 return EXT2_ET_MISSING_INODE_TABLE;
118 scan->bytes_left = 0;
119 scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
120 scan->blocks_left = scan->fs->inode_blocks_per_group;
121 }
122
123 /*
124 * Have we run out of space in the inode buffer? If so, we
125 * need to read in more blocks.
126 */
127 if (scan->bytes_left < scan->inode_size) {
128 memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
129 extra_bytes = scan->bytes_left;
130
Theodore Ts'o3839e651997-04-26 13:21:57 +0000131 scan->blocks_left -= scan->inode_buffer_blocks;
132 num_blocks = scan->inode_buffer_blocks;
133 if (scan->blocks_left < 0)
134 num_blocks += scan->blocks_left;
135
Theodore Ts'o3839e651997-04-26 13:21:57 +0000136 retval = io_channel_read_blk(scan->fs->io, scan->current_block,
137 num_blocks, scan->inode_buffer);
138 if (retval)
139 return EXT2_ET_NEXT_INODE_READ;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000140 scan->ptr = scan->inode_buffer;
141 scan->bytes_left = num_blocks * scan->fs->blocksize;
142
143 scan->current_block += scan->inode_buffer_blocks;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000144 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000145
146 if (extra_bytes) {
147 memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
148 scan->inode_size - extra_bytes);
149 scan->ptr += scan->inode_size - extra_bytes;
150 scan->bytes_left -= scan->inode_size - extra_bytes;
151
Theodore Ts'o5c576471997-04-29 15:29:49 +0000152 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
153 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000154 ext2fs_swap_inode(scan->fs, inode,
155 (struct ext2_inode *) scan->temp_buffer, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000156 else
157 *inode = *((struct ext2_inode *) scan->temp_buffer);
158 } else {
Theodore Ts'o5c576471997-04-29 15:29:49 +0000159 if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
160 (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000161 ext2fs_swap_inode(scan->fs, inode,
162 (struct ext2_inode *) scan->ptr, 0);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000163 else
164 *inode = *((struct ext2_inode *) scan->ptr);
165 scan->ptr += scan->inode_size;
166 scan->bytes_left -= scan->inode_size;
167 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000168
Theodore Ts'o3839e651997-04-26 13:21:57 +0000169 scan->inodes_left--;
170 scan->current_inode++;
171 *ino = scan->current_inode;
172 return 0;
173}
174
175/*
176 * Functions to read and write a single inode.
177 */
178static char *inode_buffer = 0;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000179static blk_t inode_buffer_block = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000180static int inode_buffer_size = 0;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000181#define INODE_CACHE_SIZE 4
182#ifdef INODE_CACHE_SIZE
183static int cache_last = -1;
184static struct {
185 ino_t inode;
186 struct ext2_inode value;
187} inode_cache[INODE_CACHE_SIZE];
188#endif
189
Theodore Ts'o3839e651997-04-26 13:21:57 +0000190
191errcode_t ext2fs_read_inode (ext2_filsys fs, unsigned long ino,
192 struct ext2_inode * inode)
193{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000194 unsigned long group, block, block_nr, offset;
195 char *ptr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000196 errcode_t retval;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000197 int clen, length, i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000198
Theodore Ts'of3db3561997-04-26 13:34:30 +0000199 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
200
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000201 /* Check to see if user has an override function */
202 if (fs->read_inode) {
203 retval = (fs->read_inode)(fs, ino, inode);
204 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
205 return retval;
206 }
207 /* Check to see if it's in the inode cache */
208#ifdef INODE_CACHE_SIZE
209 if (cache_last == -1) {
210 for (i=0; i < INODE_CACHE_SIZE; i++)
211 inode_cache[i].inode = 0;
212 cache_last = INODE_CACHE_SIZE-1;
213 } else for (i=0; i < INODE_CACHE_SIZE; i++) {
214 if (inode_cache[i].inode == ino) {
215 *inode = inode_cache[i].value;
216 return 0;
217 }
218 }
219#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000220 if (ino > fs->super->s_inodes_count)
221 return EXT2_ET_BAD_INODE_NUM;
222 if (inode_buffer_size != fs->blocksize) {
223 if (inode_buffer)
224 free(inode_buffer);
225 inode_buffer_size = 0;
226 inode_buffer = malloc(fs->blocksize);
227 if (!inode_buffer)
228 return ENOMEM;
229 inode_buffer_size = fs->blocksize;
230 inode_buffer_block = 0;
231 }
232
233 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000234 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
235 EXT2_INODE_SIZE(fs->super);
236 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000237 block_nr = fs->group_desc[group].bg_inode_table + block;
238 if (block_nr != inode_buffer_block) {
239 retval = io_channel_read_blk(fs->io, block_nr, 1,
240 inode_buffer);
241 if (retval)
242 return retval;
243 inode_buffer_block = block_nr;
244 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000245 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
246 ptr = ((char *) inode_buffer) + offset;
247
248 memset(inode, 0, sizeof(struct ext2_inode));
249 length = EXT2_INODE_SIZE(fs->super);
250 if (length > sizeof(struct ext2_inode))
251 length = sizeof(struct ext2_inode);
252
253 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
254 clen = EXT2_BLOCK_SIZE(fs->super) - offset;
255 memcpy((char *) inode, ptr, clen);
256 length -= clen;
257
258 retval = io_channel_read_blk(fs->io, block_nr+1, 1,
259 inode_buffer);
260 if (retval)
261 return retval;
262 inode_buffer_block = block_nr+1;
263
264 memcpy(((char *) inode) + clen,
265 inode_buffer, length);
266 } else
267 memcpy((char *) inode, ptr, length);
268
Theodore Ts'o5c576471997-04-29 15:29:49 +0000269 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
270 (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000271 ext2fs_swap_inode(fs, inode, inode, 0);
272
273 /* Update the inode cache */
274#ifdef INODE_CACHE_SIZE
275 cache_last = (cache_last + 1) % INODE_CACHE_SIZE;
276 inode_cache[cache_last].inode = ino;
277 inode_cache[cache_last].value = *inode;
278#endif
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000279
Theodore Ts'o3839e651997-04-26 13:21:57 +0000280 return 0;
281}
282
283errcode_t ext2fs_write_inode(ext2_filsys fs, unsigned long ino,
284 struct ext2_inode * inode)
285{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000286 unsigned long group, block, block_nr, offset;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000287 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000288 struct ext2_inode temp_inode;
289 char *ptr;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000290 int clen, length, i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000291
Theodore Ts'of3db3561997-04-26 13:34:30 +0000292 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
293
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000294 /* Check to see if user provided an override function */
295 if (fs->write_inode) {
296 retval = (fs->write_inode)(fs, ino, inode);
297 if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
298 return retval;
299 }
300 /* Check to see if the inode cache needs to be updated */
301#ifdef INODE_CACHE_SIZE
302 for (i=0; i < INODE_CACHE_SIZE; i++) {
303 if (inode_cache[i].inode == ino) {
304 inode_cache[i].value = *inode;
305 break;
306 }
307 }
308#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000309 if (!(fs->flags & EXT2_FLAG_RW))
310 return EXT2_ET_RO_FILSYS;
311
312 if (ino > fs->super->s_inodes_count)
313 return EXT2_ET_BAD_INODE_NUM;
314
315 if (inode_buffer_size != fs->blocksize) {
316 if (inode_buffer)
317 free(inode_buffer);
318 inode_buffer_size = 0;
319 inode_buffer = malloc(fs->blocksize);
320 if (!inode_buffer)
321 return ENOMEM;
322 inode_buffer_size = fs->blocksize;
323 inode_buffer_block = 0;
324 }
Theodore Ts'o5c576471997-04-29 15:29:49 +0000325 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
326 (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000327 ext2fs_swap_inode(fs, &temp_inode, inode, 1);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000328 else
329 memcpy(&temp_inode, inode, sizeof(struct ext2_inode));
330
Theodore Ts'o3839e651997-04-26 13:21:57 +0000331 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000332 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
333 EXT2_INODE_SIZE(fs->super);
334 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000335 block_nr = fs->group_desc[group].bg_inode_table + block;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000336 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
337 ptr = (char *) inode_buffer + offset;
338
339 length = EXT2_INODE_SIZE(fs->super);
340 if (length > sizeof(struct ext2_inode))
341 length = sizeof(struct ext2_inode);
342
Theodore Ts'o3839e651997-04-26 13:21:57 +0000343 if (inode_buffer_block != block_nr) {
344 retval = io_channel_read_blk(fs->io, block_nr, 1,
345 inode_buffer);
346 if (retval)
347 return retval;
348 inode_buffer_block = block_nr;
349 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000350
351 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
352 clen = EXT2_BLOCK_SIZE(fs->super) - offset;
353 memcpy(ptr, &temp_inode, clen);
354 length -= clen;
355 } else {
356 memcpy(ptr, &temp_inode, length);
357 length = 0;
358 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000359 retval = io_channel_write_blk(fs->io, block_nr, 1, inode_buffer);
360 if (retval)
361 return retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000362
363 if (length) {
364 retval = io_channel_read_blk(fs->io, ++block_nr, 1,
365 inode_buffer);
366 if (retval) {
367 inode_buffer_block = 0;
368 return retval;
369 }
370 inode_buffer_block = block_nr;
371 memcpy(inode_buffer, ((char *) &temp_inode) + clen, length);
372
373 retval = io_channel_write_blk(fs->io, block_nr, 1,
374 inode_buffer);
375 if (retval)
376 return retval;
377 }
378
Theodore Ts'o3839e651997-04-26 13:21:57 +0000379 fs->flags |= EXT2_FLAG_CHANGED;
380 return 0;
381}
382
383errcode_t ext2fs_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks)
384{
385 struct ext2_inode inode;
386 int i;
387 errcode_t retval;
388
Theodore Ts'of3db3561997-04-26 13:34:30 +0000389 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
390
Theodore Ts'o3839e651997-04-26 13:21:57 +0000391 if (ino > fs->super->s_inodes_count)
392 return EXT2_ET_BAD_INODE_NUM;
393
394 if (fs->get_blocks) {
395 if (!(*fs->get_blocks)(fs, ino, blocks))
396 return 0;
397 }
398 retval = ext2fs_read_inode(fs, ino, &inode);
399 if (retval)
400 return retval;
401 for (i=0; i < EXT2_N_BLOCKS; i++)
402 blocks[i] = inode.i_block[i];
403 return 0;
404}
405
406errcode_t ext2fs_check_directory(ext2_filsys fs, ino_t ino)
407{
408 struct ext2_inode inode;
409 errcode_t retval;
410
Theodore Ts'of3db3561997-04-26 13:34:30 +0000411 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
412
Theodore Ts'o3839e651997-04-26 13:21:57 +0000413 if (ino > fs->super->s_inodes_count)
414 return EXT2_ET_BAD_INODE_NUM;
415
416 if (fs->check_directory)
417 return (fs->check_directory)(fs, ino);
418 retval = ext2fs_read_inode(fs, ino, &inode);
419 if (retval)
420 return retval;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000421 if (!LINUX_S_ISDIR(inode.i_mode))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000422 return ENOTDIR;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000423 return 0;
424}
425