main.c revision 76dd5e5c2842fb1a7b858aad3e68b5e9c16890c9
1/*
2 * main.c --- ext2 resizer main program
3 *
4 * Copyright (C) 1997, 1998 by Theodore Ts'o and
5 * 	PowerQuest, Inc.
6 *
7 * Copyright (C) 1999, 2000, 2001 by Theosore Ts'o
8 *
9 * %Begin-Header%
10 * This file may be redistributed under the terms of the GNU Public
11 * License.
12 * %End-Header%
13 */
14
15#ifdef HAVE_GETOPT_H
16#include <getopt.h>
17#else
18extern char *optarg;
19extern int optind;
20#endif
21#include <fcntl.h>
22#include <sys/stat.h>
23
24#include "resize2fs.h"
25
26#include "../version.h"
27
28char *program_name, *device_name;
29
30static void usage (char *prog)
31{
32	fprintf (stderr, _("usage: %s [-d debug_flags] [-f] [-F] [-p] device [new-size]\n\n"), prog);
33
34	exit (1);
35}
36
37static errcode_t resize_progress_func(ext2_resize_t rfs, int pass,
38				      unsigned long cur, unsigned long max)
39{
40	ext2_sim_progmeter progress;
41	const char	*label;
42	errcode_t	retval;
43
44	progress = (ext2_sim_progmeter) rfs->prog_data;
45	if (max == 0)
46		return 0;
47	if (cur == 0) {
48		if (progress)
49			ext2fs_progress_close(progress);
50		progress = 0;
51		switch (pass) {
52		case E2_RSZ_EXTEND_ITABLE_PASS:
53			label = _("Extending the inode table");
54			break;
55		case E2_RSZ_BLOCK_RELOC_PASS:
56			label = _("Relocating blocks");
57			break;
58		case E2_RSZ_INODE_SCAN_PASS:
59			label = _("Scanning inode table");
60			break;
61		case E2_RSZ_INODE_REF_UPD_PASS:
62			label = _("Updating inode references");
63			break;
64		case E2_RSZ_MOVE_ITABLE_PASS:
65			label = _("Moving inode table");
66			break;
67		default:
68			label = _("Unknown pass?!?");
69			break;
70		}
71		printf(_("Begin pass %d (max = %lu)\n"), pass, max);
72		retval = ext2fs_progress_init(&progress, label, 30,
73					      40, max, 0);
74		if (retval)
75			progress = 0;
76		rfs->prog_data = (void *) progress;
77	}
78	if (progress)
79		ext2fs_progress_update(progress, cur);
80	if (cur >= max) {
81		if (progress)
82			ext2fs_progress_close(progress);
83		progress = 0;
84		rfs->prog_data = 0;
85	}
86	return 0;
87}
88
89static void check_mount(char *device)
90{
91	errcode_t	retval;
92	int		mount_flags;
93
94	retval = ext2fs_check_if_mounted(device, &mount_flags);
95	if (retval) {
96		com_err(_("ext2fs_check_if_mount"), retval,
97			_("while determining whether %s is mounted."),
98			device);
99		return;
100	}
101	if (!(mount_flags & EXT2_MF_MOUNTED))
102		return;
103
104	fprintf(stderr, _("%s is mounted; can't resize a "
105		"mounted filesystem!\n\n"), device);
106	exit(1);
107}
108
109
110int main (int argc, char ** argv)
111{
112	errcode_t	retval;
113	ext2_filsys	fs;
114	int		c;
115	int		flags = 0;
116	int		flush = 0;
117	int		force = 0;
118	int		fd;
119	blk_t		new_size = 0;
120	blk_t		max_size = 0;
121	io_manager	io_ptr;
122	char		*tmp;
123	struct stat	st_buf;
124
125	initialize_ext2_error_table();
126
127	fprintf (stderr, _("resize2fs %s (%s)\n"),
128		 E2FSPROGS_VERSION, E2FSPROGS_DATE);
129	if (argc && *argv)
130		program_name = *argv;
131
132	while ((c = getopt (argc, argv, "d:fFhp")) != EOF) {
133		switch (c) {
134		case 'h':
135			usage(program_name);
136			break;
137		case 'f':
138			force = 1;
139			break;
140		case 'F':
141			flush = 1;
142			break;
143		case 'd':
144			flags |= atoi(optarg);
145			break;
146		case 'p':
147			flags |= RESIZE_PERCENT_COMPLETE;
148			break;
149		default:
150			usage(program_name);
151		}
152	}
153	if (optind == argc)
154		usage(program_name);
155
156	device_name = argv[optind++];
157	if (optind < argc) {
158		new_size = strtoul(argv[optind++], &tmp, 0);
159		if (*tmp) {
160			com_err(program_name, 0, _("bad filesystem size - %s"),
161				argv[optind - 1]);
162			exit(1);
163		}
164	}
165	if (optind < argc)
166		usage(program_name);
167
168	check_mount(device_name);
169
170	if (flush) {
171		fd = open(device_name, O_RDONLY, 0);
172
173		if (fd < 0) {
174			com_err("open", errno,
175				_("while opening %s for flushing"),
176				device_name);
177			exit(1);
178		}
179		retval = ext2fs_sync_device(fd, 1);
180		if (retval) {
181			com_err(argv[0], retval,
182				_("while trying to flush %s"),
183				device_name);
184			exit(1);
185		}
186		close(fd);
187	}
188
189	if (flags & RESIZE_DEBUG_IO) {
190		io_ptr = test_io_manager;
191		test_io_backing_manager = unix_io_manager;
192	} else
193		io_ptr = unix_io_manager;
194
195	retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0,
196			      io_ptr, &fs);
197	if (retval) {
198		com_err (program_name, retval, _("while trying to open %s"),
199			 device_name);
200		printf (_("Couldn't find valid filesystem superblock.\n"));
201		exit (1);
202	}
203	/*
204	 * Check for compatibility with the feature sets.  We need to
205	 * be more stringent than ext2fs_open().
206	 */
207	if (fs->super->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) {
208		com_err(program_name, EXT2_ET_UNSUPP_FEATURE,
209			"(%s)", device_name);
210		exit(1);
211	}
212
213	/*
214	 * Get the size of the containing partition, and use this for
215	 * defaults and for making sure the new filesystme doesn't
216	 * exceed the partition size.
217	 */
218	retval = ext2fs_get_device_size(device_name, fs->blocksize,
219					&max_size);
220	if (retval) {
221		com_err(program_name, retval,
222			_("while trying to determine filesystem size"));
223		exit(1);
224	}
225	if (!new_size)
226		new_size = max_size;
227	/*
228	 * If we are resizing a plain file, and it's not big enough,
229	 * automatically extend it in a sparse fashion by writing the
230	 * last requested block.
231	 */
232	if ((new_size > max_size) &&
233	    (stat(device_name, &st_buf) == 0) &&
234	    S_ISREG(st_buf.st_mode) &&
235	    ((tmp = malloc(fs->blocksize)) != 0)) {
236		memset(tmp, 0, fs->blocksize);
237		retval = io_channel_write_blk(fs->io, new_size-1, 1, tmp);
238		if (retval == 0)
239			max_size = new_size;
240		free(tmp);
241	}
242	if (!force && (new_size > max_size)) {
243		fprintf(stderr, _("The containing partition (or device)"
244			" is only %d blocks.\nYou requested a new size"
245			" of %d blocks.\n\n"), max_size,
246			new_size);
247		exit(1);
248	}
249	if (new_size == fs->super->s_blocks_count) {
250		fprintf(stderr, _("The filesystem is already %d blocks "
251			"long.  Nothing to do!\n\n"), new_size);
252		exit(0);
253	}
254	if (!force && ((fs->super->s_lastcheck < fs->super->s_mtime) ||
255		       (fs->super->s_state & EXT2_ERROR_FS) ||
256		       ((fs->super->s_state & EXT2_VALID_FS) == 0))) {
257		fprintf(stderr, _("Please run 'e2fsck -f %s' first.\n\n"),
258			device_name);
259		exit(1);
260	}
261	retval = resize_fs(fs, &new_size, flags,
262			   ((flags & RESIZE_PERCENT_COMPLETE) ?
263			    resize_progress_func : 0));
264	if (retval) {
265		com_err(program_name, retval, _("while trying to resize %s"),
266			device_name);
267		ext2fs_close (fs);
268		exit(1);
269	}
270	printf(_("The filesystem on %s is now %d blocks long.\n\n"),
271	       device_name, new_size);
272	return (0);
273}
274