blob: 04c5e4d1331c00ca3c86ce69e0821e4f4e7447b2 [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
Theodore Ts'o50e1e101997-04-26 13:58:21 +000022static void inocpy_with_swap(struct ext2_inode *t, struct ext2_inode *f);
23
Theodore Ts'o3839e651997-04-26 13:21:57 +000024errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
25 ext2_inode_scan *ret_scan)
26{
27 ext2_inode_scan scan;
28
Theodore Ts'of3db3561997-04-26 13:34:30 +000029 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
30
Theodore Ts'o3839e651997-04-26 13:21:57 +000031 scan = (ext2_inode_scan) malloc(sizeof(struct ext2_struct_inode_scan));
32 if (!scan)
33 return ENOMEM;
34 memset(scan, 0, sizeof(struct ext2_struct_inode_scan));
35
Theodore Ts'of3db3561997-04-26 13:34:30 +000036 scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
Theodore Ts'o3839e651997-04-26 13:21:57 +000037 scan->fs = fs;
Theodore Ts'o7f88b041997-04-26 14:48:50 +000038 scan->inode_size = EXT2_INODE_SIZE(fs->super);
39 scan->bytes_left = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +000040 scan->current_group = -1;
41 scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
42 scan->groups_left = fs->group_desc_count;
43 scan->inode_buffer = malloc(scan->inode_buffer_blocks * fs->blocksize);
Theodore Ts'of3db3561997-04-26 13:34:30 +000044 scan->done_group = 0;
45 scan->done_group_data = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +000046 if (!scan->inode_buffer) {
47 free(scan);
48 return ENOMEM;
49 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +000050 scan->temp_buffer = malloc(scan->inode_size);
51 if (!scan->temp_buffer) {
52 free(scan->inode_buffer);
53 free(scan);
54 return ENOMEM;
55 }
Theodore Ts'o3839e651997-04-26 13:21:57 +000056 *ret_scan = scan;
57 return 0;
58}
59
60void ext2fs_close_inode_scan(ext2_inode_scan scan)
61{
Theodore Ts'of3db3561997-04-26 13:34:30 +000062 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
63 return;
64
Theodore Ts'o3839e651997-04-26 13:21:57 +000065 free(scan->inode_buffer);
66 scan->inode_buffer = NULL;
Theodore Ts'o7f88b041997-04-26 14:48:50 +000067 free(scan->temp_buffer);
68 scan->temp_buffer = NULL;
Theodore Ts'o3839e651997-04-26 13:21:57 +000069 free(scan);
70 return;
71}
72
Theodore Ts'of3db3561997-04-26 13:34:30 +000073void ext2fs_set_inode_callback(ext2_inode_scan scan,
74 errcode_t (*done_group)(ext2_filsys fs,
75 ext2_inode_scan scan,
76 dgrp_t group,
77 void * private),
78 void *done_group_data)
79{
80 if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
81 return;
82
83 scan->done_group = done_group;
84 scan->done_group_data = done_group_data;
85}
86
Theodore Ts'o3839e651997-04-26 13:21:57 +000087errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ino_t *ino,
88 struct ext2_inode *inode)
89{
90 errcode_t retval;
91 int num_blocks;
Theodore Ts'o7f88b041997-04-26 14:48:50 +000092 int extra_bytes = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +000093
Theodore Ts'of3db3561997-04-26 13:34:30 +000094 EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
95
Theodore Ts'o7f88b041997-04-26 14:48:50 +000096 /*
97 * Do we need to start reading a new block group?
98 */
Theodore Ts'o3839e651997-04-26 13:21:57 +000099 if (scan->inodes_left <= 0) {
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000100 if (scan->done_group) {
101 retval = (scan->done_group)
102 (scan->fs, scan,
103 scan->current_group,
104 scan->done_group_data);
105 if (retval)
106 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000107 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000108 if (scan->groups_left <= 0) {
109 *ino = 0;
110 return 0;
111 }
112 scan->current_group++;
113 scan->groups_left--;
114
115 scan->current_block = scan->fs->
116 group_desc[scan->current_group].bg_inode_table;
117
118 if (scan->current_block == 0)
119 return EXT2_ET_MISSING_INODE_TABLE;
120 scan->bytes_left = 0;
121 scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
122 scan->blocks_left = scan->fs->inode_blocks_per_group;
123 }
124
125 /*
126 * Have we run out of space in the inode buffer? If so, we
127 * need to read in more blocks.
128 */
129 if (scan->bytes_left < scan->inode_size) {
130 memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
131 extra_bytes = scan->bytes_left;
132
Theodore Ts'o3839e651997-04-26 13:21:57 +0000133 scan->blocks_left -= scan->inode_buffer_blocks;
134 num_blocks = scan->inode_buffer_blocks;
135 if (scan->blocks_left < 0)
136 num_blocks += scan->blocks_left;
137
Theodore Ts'o3839e651997-04-26 13:21:57 +0000138 retval = io_channel_read_blk(scan->fs->io, scan->current_block,
139 num_blocks, scan->inode_buffer);
140 if (retval)
141 return EXT2_ET_NEXT_INODE_READ;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000142 scan->ptr = scan->inode_buffer;
143 scan->bytes_left = num_blocks * scan->fs->blocksize;
144
145 scan->current_block += scan->inode_buffer_blocks;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000146 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000147
148 if (extra_bytes) {
149 memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
150 scan->inode_size - extra_bytes);
151 scan->ptr += scan->inode_size - extra_bytes;
152 scan->bytes_left -= scan->inode_size - extra_bytes;
153
154 if (scan->fs->flags & EXT2_SWAP_BYTES)
155 inocpy_with_swap(inode, (struct ext2_inode *)
156 scan->temp_buffer);
157 else
158 *inode = *((struct ext2_inode *) scan->temp_buffer);
159 } else {
160 if (scan->fs->flags & EXT2_SWAP_BYTES)
161 inocpy_with_swap(inode, (struct ext2_inode *)
162 scan->ptr);
163 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;
181
182errcode_t ext2fs_read_inode (ext2_filsys fs, unsigned long ino,
183 struct ext2_inode * inode)
184{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000185 unsigned long group, block, block_nr, offset;
186 char *ptr;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000187 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000188 int clen, length;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000189
Theodore Ts'of3db3561997-04-26 13:34:30 +0000190 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
191
Theodore Ts'o3839e651997-04-26 13:21:57 +0000192 if (ino > fs->super->s_inodes_count)
193 return EXT2_ET_BAD_INODE_NUM;
194 if (inode_buffer_size != fs->blocksize) {
195 if (inode_buffer)
196 free(inode_buffer);
197 inode_buffer_size = 0;
198 inode_buffer = malloc(fs->blocksize);
199 if (!inode_buffer)
200 return ENOMEM;
201 inode_buffer_size = fs->blocksize;
202 inode_buffer_block = 0;
203 }
204
205 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000206 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
207 EXT2_INODE_SIZE(fs->super);
208 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000209 block_nr = fs->group_desc[group].bg_inode_table + block;
210 if (block_nr != inode_buffer_block) {
211 retval = io_channel_read_blk(fs->io, block_nr, 1,
212 inode_buffer);
213 if (retval)
214 return retval;
215 inode_buffer_block = block_nr;
216 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000217 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
218 ptr = ((char *) inode_buffer) + offset;
219
220 memset(inode, 0, sizeof(struct ext2_inode));
221 length = EXT2_INODE_SIZE(fs->super);
222 if (length > sizeof(struct ext2_inode))
223 length = sizeof(struct ext2_inode);
224
225 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
226 clen = EXT2_BLOCK_SIZE(fs->super) - offset;
227 memcpy((char *) inode, ptr, clen);
228 length -= clen;
229
230 retval = io_channel_read_blk(fs->io, block_nr+1, 1,
231 inode_buffer);
232 if (retval)
233 return retval;
234 inode_buffer_block = block_nr+1;
235
236 memcpy(((char *) inode) + clen,
237 inode_buffer, length);
238 } else
239 memcpy((char *) inode, ptr, length);
240
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000241 if (fs->flags & EXT2_SWAP_BYTES)
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000242 inocpy_with_swap(inode, inode);
243
Theodore Ts'o3839e651997-04-26 13:21:57 +0000244 return 0;
245}
246
247errcode_t ext2fs_write_inode(ext2_filsys fs, unsigned long ino,
248 struct ext2_inode * inode)
249{
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000250 unsigned long group, block, block_nr, offset;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000251 errcode_t retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000252 struct ext2_inode temp_inode;
253 char *ptr;
254 int i, clen, length;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000255
Theodore Ts'of3db3561997-04-26 13:34:30 +0000256 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
257
Theodore Ts'o3839e651997-04-26 13:21:57 +0000258 if (!(fs->flags & EXT2_FLAG_RW))
259 return EXT2_ET_RO_FILSYS;
260
261 if (ino > fs->super->s_inodes_count)
262 return EXT2_ET_BAD_INODE_NUM;
263
264 if (inode_buffer_size != fs->blocksize) {
265 if (inode_buffer)
266 free(inode_buffer);
267 inode_buffer_size = 0;
268 inode_buffer = malloc(fs->blocksize);
269 if (!inode_buffer)
270 return ENOMEM;
271 inode_buffer_size = fs->blocksize;
272 inode_buffer_block = 0;
273 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000274 if (fs->flags & EXT2_SWAP_BYTES)
275 inocpy_with_swap(&temp_inode, inode);
276 else
277 memcpy(&temp_inode, inode, sizeof(struct ext2_inode));
278
Theodore Ts'o3839e651997-04-26 13:21:57 +0000279 group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000280 offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
281 EXT2_INODE_SIZE(fs->super);
282 block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000283 block_nr = fs->group_desc[group].bg_inode_table + block;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000284 offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
285 ptr = (char *) inode_buffer + offset;
286
287 length = EXT2_INODE_SIZE(fs->super);
288 if (length > sizeof(struct ext2_inode))
289 length = sizeof(struct ext2_inode);
290
Theodore Ts'o3839e651997-04-26 13:21:57 +0000291 if (inode_buffer_block != block_nr) {
292 retval = io_channel_read_blk(fs->io, block_nr, 1,
293 inode_buffer);
294 if (retval)
295 return retval;
296 inode_buffer_block = block_nr;
297 }
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000298
299 if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
300 clen = EXT2_BLOCK_SIZE(fs->super) - offset;
301 memcpy(ptr, &temp_inode, clen);
302 length -= clen;
303 } else {
304 memcpy(ptr, &temp_inode, length);
305 length = 0;
306 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000307 retval = io_channel_write_blk(fs->io, block_nr, 1, inode_buffer);
308 if (retval)
309 return retval;
Theodore Ts'o7f88b041997-04-26 14:48:50 +0000310
311 if (length) {
312 retval = io_channel_read_blk(fs->io, ++block_nr, 1,
313 inode_buffer);
314 if (retval) {
315 inode_buffer_block = 0;
316 return retval;
317 }
318 inode_buffer_block = block_nr;
319 memcpy(inode_buffer, ((char *) &temp_inode) + clen, length);
320
321 retval = io_channel_write_blk(fs->io, block_nr, 1,
322 inode_buffer);
323 if (retval)
324 return retval;
325 }
326
Theodore Ts'o3839e651997-04-26 13:21:57 +0000327 fs->flags |= EXT2_FLAG_CHANGED;
328 return 0;
329}
330
331errcode_t ext2fs_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks)
332{
333 struct ext2_inode inode;
334 int i;
335 errcode_t retval;
336
Theodore Ts'of3db3561997-04-26 13:34:30 +0000337 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
338
Theodore Ts'o3839e651997-04-26 13:21:57 +0000339 if (ino > fs->super->s_inodes_count)
340 return EXT2_ET_BAD_INODE_NUM;
341
342 if (fs->get_blocks) {
343 if (!(*fs->get_blocks)(fs, ino, blocks))
344 return 0;
345 }
346 retval = ext2fs_read_inode(fs, ino, &inode);
347 if (retval)
348 return retval;
349 for (i=0; i < EXT2_N_BLOCKS; i++)
350 blocks[i] = inode.i_block[i];
351 return 0;
352}
353
354errcode_t ext2fs_check_directory(ext2_filsys fs, ino_t ino)
355{
356 struct ext2_inode inode;
357 errcode_t retval;
358
Theodore Ts'of3db3561997-04-26 13:34:30 +0000359 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
360
Theodore Ts'o3839e651997-04-26 13:21:57 +0000361 if (ino > fs->super->s_inodes_count)
362 return EXT2_ET_BAD_INODE_NUM;
363
364 if (fs->check_directory)
365 return (fs->check_directory)(fs, ino);
366 retval = ext2fs_read_inode(fs, ino, &inode);
367 if (retval)
368 return retval;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000369 if (!LINUX_S_ISDIR(inode.i_mode))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000370 return ENOTDIR;
371 return 0;
372}
373
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000374static void inocpy_with_swap(struct ext2_inode *t, struct ext2_inode *f)
375{
376 unsigned i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000377
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000378 t->i_mode = ext2fs_swab16(f->i_mode);
379 t->i_uid = ext2fs_swab16(f->i_uid);
380 t->i_size = ext2fs_swab32(f->i_size);
381 t->i_atime = ext2fs_swab32(f->i_atime);
382 t->i_ctime = ext2fs_swab32(f->i_ctime);
383 t->i_mtime = ext2fs_swab32(f->i_mtime);
384 t->i_dtime = ext2fs_swab32(f->i_dtime);
385 t->i_gid = ext2fs_swab16(f->i_gid);
386 t->i_links_count = ext2fs_swab16(f->i_links_count);
387 t->i_blocks = ext2fs_swab32(f->i_blocks);
388 t->i_flags = ext2fs_swab32(f->i_flags);
389 for (i = 0; i < EXT2_N_BLOCKS; i++)
390 t->i_block[i] = ext2fs_swab32(f->i_block[i]);
391 t->i_version = ext2fs_swab32(f->i_version);
392 t->i_file_acl = ext2fs_swab32(f->i_file_acl);
393 t->i_dir_acl = ext2fs_swab32(f->i_dir_acl);
394 t->i_faddr = ext2fs_swab32(f->i_faddr);
395 t->osd2.linux2.l_i_frag = f->osd2.linux2.l_i_frag;
396 t->osd2.linux2.l_i_fsize = f->osd2.linux2.l_i_fsize;
397 t->osd2.linux2.i_pad1 = ext2fs_swab16(f->osd2.linux2.i_pad1);
398}