Whamcloud - gitweb
f9d8b848341f1ecad5bc1afd125c1dc4b44cdc8b
[tools/e2fsprogs.git] / resize / main.c
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, 2002, 2003, 2004 by Theodore 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 #define _LARGEFILE_SOURCE
16 #define _LARGEFILE64_SOURCE
17
18 #ifdef HAVE_GETOPT_H
19 #include <getopt.h>
20 #else
21 extern char *optarg;
22 extern int optind;
23 #endif
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28
29 #include "e2p/e2p.h"
30
31 #include "resize2fs.h"
32
33 #include "../version.h"
34
35 char *program_name, *device_name, *io_options;
36
37 static void usage (char *prog)
38 {
39         fprintf (stderr, _("Usage: %s [-d debug_flags] [-f] [-F] [-p] "
40                            "device [new_size]\n\n"), prog);
41
42         exit (1);
43 }
44
45 static errcode_t resize_progress_func(ext2_resize_t rfs, int pass,
46                                       unsigned long cur, unsigned long max)
47 {
48         ext2_sim_progmeter progress;
49         const char      *label;
50         errcode_t       retval;
51
52         progress = (ext2_sim_progmeter) rfs->prog_data;
53         if (max == 0)
54                 return 0;
55         if (cur == 0) {
56                 if (progress)
57                         ext2fs_progress_close(progress);
58                 progress = 0;
59                 switch (pass) {
60                 case E2_RSZ_EXTEND_ITABLE_PASS:
61                         label = _("Extending the inode table");
62                         break;
63                 case E2_RSZ_BLOCK_RELOC_PASS:
64                         label = _("Relocating blocks");
65                         break;
66                 case E2_RSZ_INODE_SCAN_PASS:
67                         label = _("Scanning inode table");
68                         break;
69                 case E2_RSZ_INODE_REF_UPD_PASS:
70                         label = _("Updating inode references");
71                         break;
72                 case E2_RSZ_MOVE_ITABLE_PASS:
73                         label = _("Moving inode table");
74                         break;
75                 default:
76                         label = _("Unknown pass?!?");
77                         break;
78                 }
79                 printf(_("Begin pass %d (max = %lu)\n"), pass, max);
80                 retval = ext2fs_progress_init(&progress, label, 30,
81                                               40, max, 0);
82                 if (retval)
83                         progress = 0;
84                 rfs->prog_data = (void *) progress;
85         }
86         if (progress)
87                 ext2fs_progress_update(progress, cur);
88         if (cur >= max) {
89                 if (progress)
90                         ext2fs_progress_close(progress);
91                 progress = 0;
92                 rfs->prog_data = 0;
93         }
94         return 0;
95 }
96
97 static void determine_fs_stride(ext2_filsys fs)
98 {
99         unsigned int    group;
100         unsigned long long sum;
101         unsigned int    has_sb, prev_has_sb, num;
102         int             i_stride, b_stride;
103
104         if (fs->stride)
105                 return;
106         num = 0; sum = 0;
107         for (group = 0; group < fs->group_desc_count; group++) {
108                 has_sb = ext2fs_bg_has_super(fs, group);
109                 if (group == 0 || has_sb != prev_has_sb)
110                         goto next;
111                 b_stride = fs->group_desc[group].bg_block_bitmap - 
112                         fs->group_desc[group-1].bg_block_bitmap - 
113                         fs->super->s_blocks_per_group;
114                 i_stride = fs->group_desc[group].bg_inode_bitmap - 
115                         fs->group_desc[group-1].bg_inode_bitmap - 
116                         fs->super->s_blocks_per_group;
117                 if (b_stride != i_stride ||
118                     b_stride < 0)
119                         goto next;
120
121                 /* printf("group %d has stride %d\n", group, b_stride); */
122                 sum += b_stride;
123                 num++;
124                         
125         next:
126                 prev_has_sb = has_sb;
127         }
128
129         if (fs->group_desc_count > 12 && num < 3)
130                 sum = 0;
131
132         if (num)
133                 fs->stride = sum / num;
134         else
135                 fs->stride = 0;
136
137         fs->super->s_raid_stride = fs->stride;
138         ext2fs_mark_super_dirty(fs);
139
140 #if 0
141         if (fs->stride)
142                 printf("Using RAID stride of %d\n", fs->stride);
143 #endif
144 }
145
146 int main (int argc, char ** argv)
147 {
148         errcode_t       retval;
149         ext2_filsys     fs;
150         int             c;
151         int             flags = 0;
152         int             flush = 0;
153         int             force = 0;
154         int             io_flags = 0;
155         int             fd, ret;
156         blk_t           new_size = 0;
157         blk_t           max_size = 0;
158         io_manager      io_ptr;
159         char            *new_size_str = 0;
160         int             use_stride = -1;
161 #ifdef HAVE_FSTAT64
162         struct stat64   st_buf;
163 #else
164         struct stat     st_buf;
165 #endif
166         __s64           new_file_size;
167         unsigned int    sys_page_size = 4096;
168         long            sysval;
169         int             len, mount_flags;
170         char            *mtpt;
171
172 #ifdef ENABLE_NLS
173         setlocale(LC_MESSAGES, "");
174         setlocale(LC_CTYPE, "");
175         bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
176         textdomain(NLS_CAT_NAME);
177 #endif
178
179         add_error_table(&et_ext2_error_table);
180
181         fprintf (stderr, "resize2fs %s (%s)\n",
182                  E2FSPROGS_VERSION, E2FSPROGS_DATE);
183         if (argc && *argv)
184                 program_name = *argv;
185
186         while ((c = getopt (argc, argv, "d:fFhpS:")) != EOF) {
187                 switch (c) {
188                 case 'h':
189                         usage(program_name);
190                         break;
191                 case 'f':
192                         force = 1;
193                         break;
194                 case 'F':
195                         flush = 1;
196                         break;
197                 case 'd':
198                         flags |= atoi(optarg);
199                         break;
200                 case 'p':
201                         flags |= RESIZE_PERCENT_COMPLETE;
202                         break;
203                 case 'S':
204                         use_stride = atoi(optarg);
205                         break;
206                 default:
207                         usage(program_name);
208                 }
209         }
210         if (optind == argc)
211                 usage(program_name);
212
213         device_name = argv[optind++];
214         if (optind < argc)
215                 new_size_str = argv[optind++];
216         if (optind < argc)
217                 usage(program_name);
218         
219         io_options = strchr(device_name, '?');
220         if (io_options)
221                 *io_options++ = 0;
222
223         /*
224          * Figure out whether or not the device is mounted, and if it is
225          * where it is mounted.
226          */
227         len=80;
228         while (1) {
229                 mtpt = malloc(len);
230                 if (!mtpt)
231                         return ENOMEM;
232                 mtpt[len-1] = 0;
233                 retval = ext2fs_check_mount_point(device_name, &mount_flags, 
234                                                   mtpt, len);
235                 if (retval) {
236                         com_err("ext2fs_check_mount_point", retval,
237                                 _("while determining whether %s is mounted."),
238                                 device_name);
239                         exit(1);
240                 }
241                 if (!(mount_flags & EXT2_MF_MOUNTED) || (mtpt[len-1] == 0))
242                         break;
243                 free(mtpt);
244                 len = 2 * len;
245         }
246
247 #ifdef HAVE_OPEN64
248         fd = open64(device_name, O_RDWR);
249 #else
250         fd = open(device_name, O_RDWR);
251 #endif
252         if (fd < 0) {
253                 com_err("open", errno, _("while opening %s"),
254                         device_name);
255                 exit(1);
256         }
257
258 #ifdef HAVE_FSTAT64
259         ret = fstat64(fd, &st_buf);
260 #else
261         ret = fstat(fd, &st_buf);
262 #endif
263         if (ret < 0) {
264                 com_err("open", errno, 
265                         _("while getting stat information for %s"),
266                         device_name);
267                 exit(1);
268         }
269         
270         if (flush) {
271                 retval = ext2fs_sync_device(fd, 1);
272                 if (retval) {
273                         com_err(argv[0], retval, 
274                                 _("while trying to flush %s"),
275                                 device_name);
276                         exit(1);
277                 }
278         }
279
280         if (!S_ISREG(st_buf.st_mode )) {
281                 close(fd);
282                 fd = -1;
283         }
284
285         if (flags & RESIZE_DEBUG_IO) {
286                 io_ptr = test_io_manager;
287                 test_io_backing_manager = unix_io_manager;
288         } else 
289                 io_ptr = unix_io_manager;
290
291         if (!(mount_flags & EXT2_MF_MOUNTED))
292                 io_flags = EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE;
293         retval = ext2fs_open2(device_name, io_options, io_flags, 
294                               0, 0, io_ptr, &fs);
295         if (retval) {
296                 com_err (program_name, retval, _("while trying to open %s"),
297                          device_name);
298                 printf (_("Couldn't find valid filesystem superblock.\n"));
299                 exit (1);
300         }
301         /*
302          * Check for compatibility with the feature sets.  We need to
303          * be more stringent than ext2fs_open().
304          */
305         if (fs->super->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) {
306                 com_err(program_name, EXT2_ET_UNSUPP_FEATURE,
307                         "(%s)", device_name);
308                 exit(1);
309         }
310         
311         /* Determine the system page size if possible */
312 #ifdef HAVE_SYSCONF
313 #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE))
314 #define _SC_PAGESIZE _SC_PAGE_SIZE
315 #endif
316 #ifdef _SC_PAGESIZE
317         sysval = sysconf(_SC_PAGESIZE);
318         if (sysval > 0)
319                 sys_page_size = sysval;
320 #endif /* _SC_PAGESIZE */
321 #endif /* HAVE_SYSCONF */
322
323         /*
324          * Get the size of the containing partition, and use this for
325          * defaults and for making sure the new filesystem doesn't
326          * exceed the partition size.
327          */
328         retval = ext2fs_get_device_size(device_name, fs->blocksize,
329                                         &max_size);
330         if (retval) {
331                 com_err(program_name, retval,
332                         _("while trying to determine filesystem size"));
333                 exit(1);
334         }
335         if (new_size_str) {
336                 new_size = parse_num_blocks(new_size_str, 
337                                             fs->super->s_log_block_size);
338                 if (!new_size) {
339                         com_err(program_name, 0, _("bad filesystem size - %s"),
340                                 new_size_str);
341                         exit(1);
342                 }
343         } else {
344                 new_size = max_size;
345                 /* Round down to an even multiple of a pagesize */
346                 if (sys_page_size > fs->blocksize)
347                         new_size &= ~((sys_page_size / fs->blocksize)-1);
348         }
349
350         if (use_stride >= 0) {
351                 if (use_stride >= fs->super->s_blocks_per_group) {
352                         com_err(program_name, 0, 
353                                 _("Invalid stride length"));
354                         exit(1);
355                 }
356                 fs->stride = fs->super->s_raid_stride = use_stride;
357                 ext2fs_mark_super_dirty(fs);
358         } else
359                   determine_fs_stride(fs);
360         
361         /*
362          * If we are resizing a plain file, and it's not big enough,
363          * automatically extend it in a sparse fashion by writing the
364          * last requested block.
365          */
366         new_file_size = ((__u64) new_size) * fs->blocksize;
367         if ((__u64) new_file_size > 
368             (((__u64) 1) << (sizeof(st_buf.st_size)*8 - 1)) - 1)
369                 fd = -1;
370         if ((new_file_size > st_buf.st_size) &&
371             (fd > 0)) {
372                 if ((ext2fs_llseek(fd, new_file_size-1, SEEK_SET) >= 0) &&
373                     (write(fd, "0", 1) == 1))
374                         max_size = new_size;
375         }
376         if (!force && (new_size > max_size)) {
377                 fprintf(stderr, _("The containing partition (or device)"
378                         " is only %u (%dk) blocks.\nYou requested a new size"
379                         " of %u blocks.\n\n"), max_size,
380                         fs->blocksize / 1024, new_size);
381                 exit(1);
382         }
383         if (new_size == fs->super->s_blocks_count) {
384                 fprintf(stderr, _("The filesystem is already %u blocks "
385                         "long.  Nothing to do!\n\n"), new_size);
386                 exit(0);
387         }
388         if (mount_flags & EXT2_MF_MOUNTED) {
389                 retval = online_resize_fs(fs, mtpt, &new_size, flags);
390         } else {
391                 if (!force && ((fs->super->s_lastcheck < fs->super->s_mtime) ||
392                                (fs->super->s_state & EXT2_ERROR_FS) ||
393                                ((fs->super->s_state & EXT2_VALID_FS) == 0))) {
394                         fprintf(stderr, 
395                                 _("Please run 'e2fsck -f %s' first.\n\n"),
396                                 device_name);
397                         exit(1);
398                 }
399         printf("Resizing the filesystem on %s to %u (%dk) blocks.\n",
400                        device_name, new_size, fs->blocksize / 1024);
401                 retval = resize_fs(fs, &new_size, flags,
402                                    ((flags & RESIZE_PERCENT_COMPLETE) ?
403                                     resize_progress_func : 0));
404         }
405         if (retval) {
406                 com_err(program_name, retval, _("while trying to resize %s"),
407                         device_name);
408                 ext2fs_close (fs);
409                 exit(1);
410         }
411         printf(_("The filesystem on %s is now %u blocks long.\n\n"),
412                device_name, new_size);
413
414         if ((st_buf.st_size > new_file_size) &&
415             (fd > 0)) {
416 #ifdef HAVE_FSTAT64
417                 ftruncate64(fd, new_file_size);
418 #else
419                 ftruncate(fd, (off_t) new_file_size);
420 #endif
421         }
422         if (fd > 0)
423                 close(fd);
424         remove_error_table(&et_ext2_error_table);
425         return (0);
426 }