openfs.c revision 25567a7b0fa98b390fd1ff0d4702b29c23a75bbb
1/*
2 * openfs.c --- open an ext2 filesystem
3 *
4 * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Library
8 * General Public License, version 2.
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#include <fcntl.h>
18#include <time.h>
19#if HAVE_SYS_STAT_H
20#include <sys/stat.h>
21#endif
22#if HAVE_SYS_TYPES_H
23#include <sys/types.h>
24#endif
25
26#include "ext2_fs.h"
27
28
29#include "ext2fs.h"
30#include "e2image.h"
31
32blk64_t ext2fs_descriptor_block_loc2(ext2_filsys fs, blk64_t group_block,
33				     dgrp_t i)
34{
35	int	bg;
36	int	has_super = 0;
37	blk64_t	ret_blk;
38
39	if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
40	    (i < fs->super->s_first_meta_bg))
41		return (group_block + i + 1);
42
43	bg = EXT2_DESC_PER_BLOCK(fs->super) * i;
44	if (ext2fs_bg_has_super(fs, bg))
45		has_super = 1;
46	ret_blk = ext2fs_group_first_block2(fs, bg) + has_super;
47	/*
48	 * If group_block is not the normal value, we're trying to use
49	 * the backup group descriptors and superblock --- so use the
50	 * alternate location of the second block group in the
51	 * metablock group.  Ideally we should be testing each bg
52	 * descriptor block individually for correctness, but we don't
53	 * have the infrastructure in place to do that.
54	 */
55	if (group_block != fs->super->s_first_data_block &&
56	    ((ret_blk + fs->super->s_blocks_per_group) <
57	     ext2fs_blocks_count(fs->super)))
58		ret_blk += fs->super->s_blocks_per_group;
59	return ret_blk;
60}
61
62blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i)
63{
64	return ext2fs_descriptor_block_loc2(fs, group_block, i);
65}
66
67errcode_t ext2fs_open(const char *name, int flags, int superblock,
68		      unsigned int block_size, io_manager manager,
69		      ext2_filsys *ret_fs)
70{
71	return ext2fs_open2(name, 0, flags, superblock, block_size,
72			    manager, ret_fs);
73}
74
75/*
76 *  Note: if superblock is non-zero, block-size must also be non-zero.
77 * 	Superblock and block_size can be zero to use the default size.
78 *
79 * Valid flags for ext2fs_open()
80 *
81 * 	EXT2_FLAG_RW	- Open the filesystem for read/write.
82 * 	EXT2_FLAG_FORCE - Open the filesystem even if some of the
83 *				features aren't supported.
84 *	EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device
85 */
86errcode_t ext2fs_open2(const char *name, const char *io_options,
87		       int flags, int superblock,
88		       unsigned int block_size, io_manager manager,
89		       ext2_filsys *ret_fs)
90{
91	ext2_filsys	fs;
92	errcode_t	retval;
93	unsigned long	i, first_meta_bg;
94	__u32		features;
95	int		groups_per_block, blocks_per_group, io_flags;
96	blk64_t		group_block, blk;
97	char		*dest, *cp;
98#ifdef WORDS_BIGENDIAN
99	struct ext2_group_desc *gdp;
100	int		j;
101#endif
102
103	EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER);
104
105	retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs);
106	if (retval)
107		return retval;
108
109	memset(fs, 0, sizeof(struct struct_ext2_filsys));
110	fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS;
111	fs->flags = flags;
112	/* don't overwrite sb backups unless flag is explicitly cleared */
113	fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
114	fs->umask = 022;
115	retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name);
116	if (retval)
117		goto cleanup;
118	strcpy(fs->device_name, name);
119	cp = strchr(fs->device_name, '?');
120	if (!io_options && cp) {
121		*cp++ = 0;
122		io_options = cp;
123	}
124
125	io_flags = 0;
126	if (flags & EXT2_FLAG_RW)
127		io_flags |= IO_FLAG_RW;
128	if (flags & EXT2_FLAG_EXCLUSIVE)
129		io_flags |= IO_FLAG_EXCLUSIVE;
130	if (flags & EXT2_FLAG_DIRECT_IO)
131		io_flags |= IO_FLAG_DIRECT_IO;
132	retval = manager->open(fs->device_name, io_flags, &fs->io);
133	if (retval)
134		goto cleanup;
135	if (io_options &&
136	    (retval = io_channel_set_options(fs->io, io_options)))
137		goto cleanup;
138	fs->image_io = fs->io;
139	fs->io->app_data = fs;
140	retval = ext2fs_get_memalign(SUPERBLOCK_SIZE, 512, &fs->super);
141	if (retval)
142		goto cleanup;
143	if (flags & EXT2_FLAG_IMAGE_FILE) {
144		retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr),
145					&fs->image_header);
146		if (retval)
147			goto cleanup;
148		retval = io_channel_read_blk(fs->io, 0,
149					     -(int)sizeof(struct ext2_image_hdr),
150					     fs->image_header);
151		if (retval)
152			goto cleanup;
153		if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE)
154			return EXT2_ET_MAGIC_E2IMAGE;
155		superblock = 1;
156		block_size = fs->image_header->fs_blocksize;
157	}
158
159	/*
160	 * If the user specifies a specific block # for the
161	 * superblock, then he/she must also specify the block size!
162	 * Otherwise, read the master superblock located at offset
163	 * SUPERBLOCK_OFFSET from the start of the partition.
164	 *
165	 * Note: we only save a backup copy of the superblock if we
166	 * are reading the superblock from the primary superblock location.
167	 */
168	if (superblock) {
169		if (!block_size) {
170			retval = EXT2_ET_INVALID_ARGUMENT;
171			goto cleanup;
172		}
173		io_channel_set_blksize(fs->io, block_size);
174		group_block = superblock;
175		fs->orig_super = 0;
176	} else {
177		io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET);
178		superblock = 1;
179		group_block = 0;
180		retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super);
181		if (retval)
182			goto cleanup;
183	}
184	retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE,
185				     fs->super);
186	if (retval)
187		goto cleanup;
188	if (fs->orig_super)
189		memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE);
190
191#ifdef WORDS_BIGENDIAN
192	fs->flags |= EXT2_FLAG_SWAP_BYTES;
193	ext2fs_swap_super(fs->super);
194#else
195	if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
196		retval = EXT2_ET_UNIMPLEMENTED;
197		goto cleanup;
198	}
199#endif
200
201	if (fs->super->s_magic != EXT2_SUPER_MAGIC) {
202		retval = EXT2_ET_BAD_MAGIC;
203		goto cleanup;
204	}
205	if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) {
206		retval = EXT2_ET_REV_TOO_HIGH;
207		goto cleanup;
208	}
209
210	/*
211	 * Check for feature set incompatibility
212	 */
213	if (!(flags & EXT2_FLAG_FORCE)) {
214		features = fs->super->s_feature_incompat;
215#ifdef EXT2_LIB_SOFTSUPP_INCOMPAT
216		if (flags & EXT2_FLAG_SOFTSUPP_FEATURES)
217			features &= !EXT2_LIB_SOFTSUPP_INCOMPAT;
218#endif
219		if (features & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) {
220			retval = EXT2_ET_UNSUPP_FEATURE;
221			goto cleanup;
222		}
223
224		features = fs->super->s_feature_ro_compat;
225#ifdef EXT2_LIB_SOFTSUPP_RO_COMPAT
226		if (flags & EXT2_FLAG_SOFTSUPP_FEATURES)
227			features &= !EXT2_LIB_SOFTSUPP_RO_COMPAT;
228#endif
229		if ((flags & EXT2_FLAG_RW) &&
230		    (features & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) {
231			retval = EXT2_ET_RO_UNSUPP_FEATURE;
232			goto cleanup;
233		}
234
235		if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) &&
236		    (fs->super->s_feature_incompat &
237		     EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
238			retval = EXT2_ET_UNSUPP_FEATURE;
239			goto cleanup;
240		}
241	}
242
243	if ((fs->super->s_log_block_size + EXT2_MIN_BLOCK_LOG_SIZE) >
244	    EXT2_MAX_BLOCK_LOG_SIZE) {
245		retval = EXT2_ET_CORRUPT_SUPERBLOCK;
246		goto cleanup;
247	}
248	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
249					EXT4_FEATURE_RO_COMPAT_BIGALLOC) &&
250	    (fs->super->s_log_block_size != fs->super->s_log_cluster_size)) {
251		retval = EXT2_ET_CORRUPT_SUPERBLOCK;
252		goto cleanup;
253	}
254	fs->blocksize = EXT2_BLOCK_SIZE(fs->super);
255	if (EXT2_INODE_SIZE(fs->super) < EXT2_GOOD_OLD_INODE_SIZE) {
256		retval = EXT2_ET_CORRUPT_SUPERBLOCK;
257		goto cleanup;
258	}
259	fs->cluster_ratio_bits = fs->super->s_log_cluster_size -
260		fs->super->s_log_block_size;
261	if (EXT2_BLOCKS_PER_GROUP(fs->super) !=
262	    EXT2_CLUSTERS_PER_GROUP(fs->super) << fs->cluster_ratio_bits) {
263		retval = EXT2_ET_CORRUPT_SUPERBLOCK;
264		goto cleanup;
265	}
266	fs->inode_blocks_per_group = ((EXT2_INODES_PER_GROUP(fs->super) *
267				       EXT2_INODE_SIZE(fs->super) +
268				       EXT2_BLOCK_SIZE(fs->super) - 1) /
269				      EXT2_BLOCK_SIZE(fs->super));
270	if (block_size) {
271		if (block_size != fs->blocksize) {
272			retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE;
273			goto cleanup;
274		}
275	}
276	/*
277	 * Set the blocksize to the filesystem's blocksize.
278	 */
279	io_channel_set_blksize(fs->io, fs->blocksize);
280
281	/*
282	 * If this is an external journal device, don't try to read
283	 * the group descriptors, because they're not there.
284	 */
285	if (fs->super->s_feature_incompat &
286	    EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
287		fs->group_desc_count = 0;
288		*ret_fs = fs;
289		return 0;
290	}
291
292	if (EXT2_INODES_PER_GROUP(fs->super) == 0) {
293		retval = EXT2_ET_CORRUPT_SUPERBLOCK;
294		goto cleanup;
295	}
296
297	/*
298	 * Read group descriptors
299	 */
300	blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super);
301	if (blocks_per_group == 0 ||
302	    blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) ||
303	    fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super) ||
304           EXT2_DESC_PER_BLOCK(fs->super) == 0 ||
305           fs->super->s_first_data_block >= ext2fs_blocks_count(fs->super)) {
306		retval = EXT2_ET_CORRUPT_SUPERBLOCK;
307		goto cleanup;
308	}
309	fs->group_desc_count = ext2fs_div64_ceil(ext2fs_blocks_count(fs->super) -
310						 fs->super->s_first_data_block,
311						 blocks_per_group);
312	if (fs->group_desc_count * EXT2_INODES_PER_GROUP(fs->super) !=
313	    fs->super->s_inodes_count) {
314		retval = EXT2_ET_CORRUPT_SUPERBLOCK;
315		goto cleanup;
316	}
317	fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count,
318					  EXT2_DESC_PER_BLOCK(fs->super));
319	retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize,
320				&fs->group_desc);
321	if (retval)
322		goto cleanup;
323	if (!group_block)
324		group_block = fs->super->s_first_data_block;
325	if (group_block == 0 && fs->blocksize == 1024)
326		group_block = 1; /* Deal with 1024 blocksize && bigalloc */
327	dest = (char *) fs->group_desc;
328	groups_per_block = EXT2_DESC_PER_BLOCK(fs->super);
329	if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
330		first_meta_bg = fs->super->s_first_meta_bg;
331	else
332		first_meta_bg = fs->desc_blocks;
333	if (first_meta_bg) {
334		retval = io_channel_read_blk(fs->io, group_block+1,
335					     first_meta_bg, dest);
336		if (retval)
337			goto cleanup;
338#ifdef WORDS_BIGENDIAN
339		gdp = (struct ext2_group_desc *) dest;
340		for (j=0; j < groups_per_block*first_meta_bg; j++) {
341			gdp = ext2fs_group_desc(fs, fs->group_desc, j);
342			ext2fs_swap_group_desc2(fs, gdp);
343		}
344#endif
345		dest += fs->blocksize*first_meta_bg;
346	}
347	for (i=first_meta_bg ; i < fs->desc_blocks; i++) {
348		blk = ext2fs_descriptor_block_loc2(fs, group_block, i);
349		retval = io_channel_read_blk64(fs->io, blk, 1, dest);
350		if (retval)
351			goto cleanup;
352#ifdef WORDS_BIGENDIAN
353		for (j=0; j < groups_per_block; j++) {
354			/* The below happens to work... be careful. */
355			gdp = ext2fs_group_desc(fs, fs->group_desc, j);
356			ext2fs_swap_group_desc2(fs, gdp);
357		}
358#endif
359		dest += fs->blocksize;
360	}
361
362	fs->stride = fs->super->s_raid_stride;
363
364	/*
365	 * If recovery is from backup superblock, Clear _UNININT flags &
366	 * reset bg_itable_unused to zero
367	 */
368	if (superblock > 1 && EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
369					EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
370		dgrp_t group;
371
372		for (group = 0; group < fs->group_desc_count; group++) {
373			ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT);
374			ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT);
375			ext2fs_bg_itable_unused_set(fs, group, 0);
376		}
377		ext2fs_mark_super_dirty(fs);
378	}
379
380	fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR;
381	*ret_fs = fs;
382	return 0;
383cleanup:
384	if (flags & EXT2_FLAG_NOFREE_ON_ERROR)
385		*ret_fs = fs;
386	else
387		ext2fs_free(fs);
388	return retval;
389}
390
391/*
392 * Set/get the filesystem data I/O channel.
393 *
394 * These functions are only valid if EXT2_FLAG_IMAGE_FILE is true.
395 */
396errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io)
397{
398	if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
399		return EXT2_ET_NOT_IMAGE_FILE;
400	if (old_io) {
401		*old_io = (fs->image_io == fs->io) ? 0 : fs->io;
402	}
403	return 0;
404}
405
406errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io)
407{
408	if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
409		return EXT2_ET_NOT_IMAGE_FILE;
410	fs->io = new_io ? new_io : fs->image_io;
411	return 0;
412}
413
414errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io)
415{
416	if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
417		return EXT2_ET_NOT_IMAGE_FILE;
418	fs->io = fs->image_io = new_io;
419	fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW |
420		EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
421	fs->flags &= ~EXT2_FLAG_IMAGE_FILE;
422	return 0;
423}
424