"Fossies" - the Fresh Open Source Software Archive 
Member "fatresize-1.1.0/fatresize.c" (5 Apr 2020, 18770 Bytes) of package /linux/privat/fatresize-1.1.0.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "fatresize.c" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
1.0.2_vs_1.0.3.
1 /*
2 * Copyright (C) 2005-2020 Anton D. Kachalov <mouse@ya.ru>
3 *
4 * The FAT16/FAT32 non-destructive resizer.
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /* activate PED_ASSERT */
21 #ifndef DEBUG
22 #define DEBUG 1
23 #endif
24
25 #include <ctype.h>
26 #include <errno.h>
27 #include <getopt.h>
28 #include <inttypes.h>
29 #include <limits.h>
30 #include <parted/debug.h>
31 #include <parted/parted.h>
32 #include <parted/unit.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <sys/stat.h>
39
40 #include "config.h"
41
42 #define FAT_ASSERT(cond, action) \
43 do { \
44 if (!(cond)) { \
45 PED_ASSERT(cond); \
46 action; \
47 } \
48 } while (0)
49
50 #define MAX_SIZE_STR "max"
51
52 static struct {
53 unsigned char *fullpath;
54 unsigned char *device;
55 int pnum;
56 PedSector size;
57 int verbose;
58 int progress;
59 int info;
60 int force_yes;
61 } opts;
62
63 typedef struct {
64 time_t last_update;
65 time_t predicted_time_left;
66 } TimerContext;
67
68 static TimerContext timer_context;
69
70 static void usage(int code) {
71 fprintf(stdout,
72 "Usage: %s [options] device (e.g. /dev/hda1, /dev/sda2)\n"
73 " Resize an FAT16/FAT32 volume non-destructively:\n\n"
74 " -s, --size SIZE Resize volume to SIZE[k|M|G|ki|Mi|Gi] bytes "
75 "or \"" MAX_SIZE_STR
76 "\"\n"
77 " -i, --info Show volume information\n"
78 " -f, --force-yes Do not ask questions\n"
79 " -n, --partition NUM Specify partition number\n"
80 " -p, --progress Show progress\n"
81 " -q, --quiet Be quiet\n"
82 " -v, --verbose Verbose\n"
83 " -h, --help Display this help\n\n"
84 "Please report bugs to %s\n",
85 PACKAGE_NAME, PACKAGE_BUGREPORT);
86
87 exit(code);
88 }
89
90 static void printd(int level, const char *fmt, ...) {
91 va_list ap;
92
93 if (opts.verbose < level) {
94 return;
95 }
96
97 va_start(ap, fmt);
98 vprintf(fmt, ap);
99 va_end(ap);
100 }
101
102 static PedSector get_size(char *s) {
103 PedSector size;
104 char *suffix;
105 int prefix_kind = 1000;
106
107 if (!strncmp(s, MAX_SIZE_STR, sizeof(MAX_SIZE_STR) - 1)) return LLONG_MAX;
108
109 size = strtoll(s, &suffix, 10);
110 if (size <= 0 || errno == ERANGE) {
111 fprintf(stderr, "Illegal new volume size\n");
112 usage(1);
113 }
114
115 if (!*suffix) {
116 return size;
117 }
118
119 if (strlen(suffix) == 2 && suffix[1] == 'i') {
120 prefix_kind = 1024;
121 } else if (strlen(suffix) > 1) {
122 usage(1);
123 }
124
125 switch (*suffix) {
126 case 'G':
127 size *= prefix_kind;
128 case 'M':
129 size *= prefix_kind;
130 case 'k':
131 size *= prefix_kind;
132 break;
133 default:
134 usage(1);
135 }
136
137 return size;
138 }
139
140 /* Code parts have been taken from _ped_device_probe(). */
141 static void probe_device(PedDevice **dev, const char *path) {
142 ped_exception_fetch_all();
143 *dev = ped_device_get(path);
144 if (!*dev) {
145 ped_exception_catch();
146 }
147 ped_exception_leave_all();
148 }
149
150 static int get_partnum(char *dev) {
151 int pnum;
152 char *p;
153
154 p = dev + strlen(dev) - 1;
155 while (*p && isdigit(*p) && *p != '/') {
156 p--;
157 }
158
159 pnum = atoi(p + 1);
160 return pnum ? pnum : 1;
161 }
162
163 static int get_device(char *dev) {
164 PedDevice *peddev = NULL;
165 int len;
166 char *devname;
167 char *p;
168 struct stat st;
169
170 opts.device = NULL;
171 opts.fullpath = strdup(dev);
172
173 if (stat(dev, &st) == -1) {
174 return 0;
175 }
176 if (!(S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))) {
177 probe_device(&peddev, dev);
178 if (!peddev) {
179 return 0;
180 }
181 ped_device_destroy(peddev);
182 opts.device = strdup(dev);
183 return 1;
184 }
185
186 len = strlen(dev);
187 p = dev + len - 1;
188 while (*p && isdigit(*p) && *p != '/') {
189 p--;
190 }
191
192 devname = malloc(len);
193 strncpy(devname, dev, p - dev + 1);
194 devname[p - dev + 1] = '\0';
195
196 if (p-dev > 2 && devname[p-dev] == 'p' && isdigit(devname[p-dev-1])) {
197 devname[p-dev] = '\0';
198 }
199
200 peddev = NULL;
201 probe_device(&peddev, devname);
202 if (!peddev) {
203 strcpy(devname, dev);
204 probe_device(&peddev, devname);
205
206 if (!peddev) {
207 free(devname);
208 return 0;
209 }
210 } else {
211 opts.pnum = get_partnum(devname);
212 }
213 ped_device_destroy(peddev);
214 opts.device = devname;
215
216 return 1;
217 }
218
219 static void resize_handler(PedTimer *timer, void *ctx) {
220 int draw_this_time;
221 TimerContext *tctx = (TimerContext *)ctx;
222
223 if (opts.verbose == -1) {
224 return;
225 } else if (opts.verbose < 3) {
226 fprintf(stdout, ".");
227 fflush(stdout);
228 return;
229 }
230
231 if (tctx->last_update != timer->now && timer->now > timer->start) {
232 tctx->predicted_time_left = timer->predicted_end - timer->now;
233 tctx->last_update = timer->now;
234 draw_this_time = 1;
235 } else {
236 draw_this_time = 1;
237 }
238
239 if (draw_this_time) {
240 printf("\r \r");
241 if (timer->state_name) {
242 printf("%s... ", timer->state_name);
243 }
244 printf("%0.f%%\t(time left %.2ld:%.2ld)", 100.0 * timer->frac,
245 tctx->predicted_time_left / 60, tctx->predicted_time_left % 60);
246
247 fflush(stdout);
248 }
249 }
250
251 static PedExceptionOption option_get_next(PedExceptionOption options,
252 PedExceptionOption current) {
253 PedExceptionOption i;
254
255 if (current == 0) {
256 i = PED_EXCEPTION_OPTION_FIRST;
257 } else {
258 i = current << 1;
259 }
260
261 for (; i <= options; i <<= 1) {
262 if (options & i) {
263 return i;
264 }
265 }
266
267 return 0;
268 }
269
270 static PedExceptionOption ask_for_option(PedException *ex) {
271 int i;
272 char *buffer;
273 size_t buffer_len;
274 PedExceptionOption opt;
275
276 for (;;) {
277 for (i = 0, opt = option_get_next(ex->options, 0); opt;
278 i++, opt = option_get_next(ex->options, opt)) {
279 printf("%s%s", i > 0 ? "/" : "\n", ped_exception_get_option_string(opt));
280 }
281 printf(": ");
282 fflush(stdout);
283
284 buffer = NULL;
285 buffer_len = 0;
286 int line_len = getline(&buffer, &buffer_len, stdin);
287 if (line_len == -1) {
288 free(buffer);
289 return PED_EXCEPTION_CANCEL;
290 }
291 buffer[line_len-1] = '\0';
292
293 for (opt = option_get_next(ex->options, 0); opt;
294 opt = option_get_next(ex->options, opt)) {
295 if (!strcasecmp(buffer, ped_exception_get_option_string(opt))) {
296 free(buffer);
297 return opt;
298 }
299 }
300 free(buffer);
301 }
302
303 /* Never reach there */
304 return PED_EXCEPTION_CANCEL;
305 }
306
307 static PedExceptionOption fatresize_handler(PedException *ex) {
308 PedExceptionOption opt;
309
310 switch (ex->type) {
311 case PED_EXCEPTION_INFORMATION:
312 case PED_EXCEPTION_WARNING:
313 fprintf(opts.force_yes ? stderr : stdout,
314 "%s: %s\n", ped_exception_get_type_string(ex->type), ex->message);
315
316 if (opts.force_yes) {
317 switch (ex->options) {
318 case PED_EXCEPTION_IGNORE_CANCEL:
319 return PED_EXCEPTION_IGNORE;
320
321 default:
322 /* Only one choice? Take it ;-) */
323 opt = option_get_next(ex->options, 0);
324 if (!option_get_next(ex->options, opt)) {
325 return opt;
326 }
327 return PED_EXCEPTION_UNHANDLED;
328 }
329 }
330
331 return ask_for_option(ex);
332
333 default:
334 if (opts.verbose != -1 || isatty(0)) {
335 fprintf(stderr, "%s: %s\n", ped_exception_get_type_string(ex->type),
336 ex->message);
337 }
338 return PED_EXCEPTION_CANCEL;
339 }
340
341 }
342
343 /* This function changes "sector" to "new_sector" if the new value lies
344 * within the required range.
345 */
346 static int snap(PedSector *sector, PedSector new_sector, PedGeometry *range) {
347 FAT_ASSERT(ped_geometry_test_sector_inside(range, *sector), return 0);
348 if (!ped_geometry_test_sector_inside(range, new_sector)) {
349 return 0;
350 }
351
352 *sector = new_sector;
353 return 1;
354 }
355
356 /* This function tries to replace the value in sector with a sequence
357 * of possible replacements, given in order of preference. The first
358 * replacement that lies within the required range is adopted.
359 */
360 static void try_snap(PedSector *sector, PedGeometry *range, ...) {
361 va_list list;
362
363 va_start(list, range);
364 while (1) {
365 PedSector new_sector = va_arg(list, PedSector);
366 if (new_sector == -1) {
367 break;
368 }
369 if (snap(sector, new_sector, range)) {
370 break;
371 }
372 }
373 va_end(list);
374 }
375
376 /* Snaps a partition to nearby partition boundaries. This is useful for
377 * gobbling up small amounts of free space, and also for reinterpreting small
378 * changes to a partition as non-changes (eg: perhaps the user only wanted to
379 * resize the end of a partition).
380 * Note that this isn't the end of the story... this function is
381 * always called before the constraint solver kicks in. So you don't need to
382 * worry too much about inadvertantly creating overlapping partitions, etc.
383 */
384 static void snap_to_boundaries(PedGeometry *new_geom, PedGeometry *old_geom,
385 PedDisk *disk, PedGeometry *start_range,
386 PedGeometry *end_range) {
387 PedPartition *start_part;
388 PedPartition *end_part;
389 PedSector start = new_geom->start;
390 PedSector end = new_geom->end;
391
392 start_part = ped_disk_get_partition_by_sector(disk, start);
393 end_part = ped_disk_get_partition_by_sector(disk, end);
394 FAT_ASSERT(start_part, return );
395 if (!end_part) {
396 return;
397 }
398
399 if (old_geom) {
400 try_snap(&start, start_range, old_geom->start, start_part->geom.start,
401 start_part->geom.end + 1, (PedSector)-1);
402 try_snap(&end, end_range, old_geom->end, end_part->geom.end,
403 end_part->geom.start - 1, (PedSector)-1);
404 } else {
405 try_snap(&start, start_range, start_part->geom.start,
406 start_part->geom.end + 1, (PedSector)-1);
407 try_snap(&end, end_range, end_part->geom.end, end_part->geom.start - 1,
408 (PedSector)-1);
409 }
410
411 FAT_ASSERT(start <= end, return );
412 ped_geometry_set(new_geom, start, end - start + 1);
413 }
414
415 /* This functions constructs a constraint from the following information:
416 * start, is_start_exact, end, is_end_exact.
417 *
418 * If is_start_exact == 1, then the constraint requires start be as given in
419 * "start". Otherwise, the constraint does not set any requirements on the
420 * start.
421 */
422 static PedConstraint *constraint_from_start_end(PedDevice *dev,
423 PedGeometry *range_start,
424 PedGeometry *range_end) {
425 return ped_constraint_new(ped_alignment_any, ped_alignment_any, range_start,
426 range_end, 1, dev->length);
427 }
428
429 static PedConstraint *constraint_intersect_and_destroy(PedConstraint *a,
430 PedConstraint *b) {
431 PedConstraint *result = ped_constraint_intersect(a, b);
432 ped_constraint_destroy(a);
433 ped_constraint_destroy(b);
434 return result;
435 }
436
437 static int partition_warn_busy(PedPartition *part) {
438 char *path = ped_partition_get_path(part);
439
440 if (ped_partition_is_busy(part)) {
441 ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
442 ("Partition %s is being used. You must unmount it "
443 "before you modify it with Parted."),
444 path);
445 free(path);
446 return 0;
447 }
448
449 free(path);
450 return 1;
451 }
452
453 int main(int argc, char **argv) {
454 int opt;
455
456 PedDevice *dev;
457 PedDisk *disk;
458 PedPartition *part;
459 PedTimer *timer = NULL;
460
461 char *old_str;
462 char *def_str;
463 PedFileSystem *fs;
464 PedSector start, end;
465 PedGeometry part_geom;
466 PedGeometry new_geom;
467 PedGeometry *old_geom;
468 PedConstraint *constraint;
469 PedGeometry *range_start, *range_end;
470
471 static const char *sopt = "-hfin:s:vpq";
472 static const struct option lopt[] = {{"help", no_argument, NULL, 'h'},
473 {"force-yes", no_argument, NULL, 'f'},
474 {"info", no_argument, NULL, 'i'},
475 {"partition", required_argument, NULL, 'n'},
476 {"progress", no_argument, NULL, 'p'},
477 {"size", required_argument, NULL, 's'},
478 {"verbose", no_argument, NULL, 'v'},
479 {"quiet", no_argument, NULL, 'q'},
480 {NULL, 0, NULL, 0}};
481
482 memset(&opts, 0, sizeof(opts));
483
484 if (argc < 2) {
485 usage(0);
486 }
487
488 opts.pnum = -1;
489
490 while ((opt = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
491 switch (opt) {
492 case 1:
493 if (!opts.device) {
494 get_device(optarg);
495 } else {
496 usage(1);
497 }
498 break;
499
500 case 'n':
501 opts.pnum = atoi(optarg);
502 break;
503
504 case 'f':
505 opts.force_yes = 1;
506 break;
507
508 case 'i':
509 opts.info = 1;
510 break;
511
512 case 'p':
513 opts.progress = 1;
514 break;
515
516 case 's':
517 opts.size = get_size(optarg);
518 break;
519
520 case 'v':
521 opts.verbose++;
522 break;
523
524 case 'q':
525 opts.verbose = -1;
526 break;
527
528 case 'h':
529 case '?':
530 default:
531 printd(0, "%s (%s)\n", PACKAGE_STRING, BUILD_DATE);
532 usage(0);
533 }
534 }
535
536 printd(0, "%s (%s)\n", PACKAGE_STRING, BUILD_DATE);
537
538 if (!opts.device) {
539 fprintf(stderr, "You must specify exactly one existing device.\n");
540 return 1;
541 } else if (!opts.size && !opts.info) {
542 fprintf(stderr, "You must specify new size.\n");
543 return 1;
544 }
545
546 ped_exception_set_handler(fatresize_handler);
547
548 if (opts.progress) {
549 timer = ped_timer_new(resize_handler, &timer_context);
550 timer_context.last_update = 0;
551 }
552
553 printd(3, "ped_device_get(%s)\n", opts.device);
554 dev = ped_device_get(opts.device);
555 if (!dev) {
556 return 1;
557 }
558
559 printd(3, "ped_device_open()\n");
560 if (!ped_device_open(dev)) {
561 return 1;
562 }
563
564 if (opts.pnum > 0) {
565 printd(3, "ped_disk_new()\n");
566 disk = ped_disk_new(dev);
567 if (!disk) {
568 return 1;
569 }
570
571 printd(3, "ped_disk_get_partition(%d)\n", opts.pnum);
572 part = ped_disk_get_partition(disk, opts.pnum);
573 if (!part || !part->fs_type) {
574 return 1;
575 }
576
577 if (strncmp(part->fs_type->name, "fat", 3)) {
578 ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
579 "%s is not valid FAT16/FAT32 partition.",
580 opts.fullpath);
581 return 1;
582 }
583
584 if (!partition_warn_busy(part)) {
585 ped_disk_destroy(disk);
586 return 1;
587 }
588 memcpy(&part_geom, &part->geom, sizeof(PedGeometry));
589 } else {
590 if (!ped_geometry_init(&part_geom, dev, 0, dev->length)) {
591 return 1;
592 }
593 }
594
595 printf("part(start=%llu, end=%llu, length=%llu)\n",
596 part_geom.start, part_geom.end, part_geom.length);
597
598 if (opts.info || opts.size == LLONG_MAX) {
599 printd(3, "ped_file_system_open()\n");
600 fs = ped_file_system_open(&part_geom);
601 if (!fs) {
602 return 1;
603 }
604
605 printd(3, "ped_file_system_get_resize_constraint()\n");
606 constraint = ped_file_system_get_resize_constraint(fs);
607 if (!constraint) {
608 return 1;
609 }
610 }
611 if (opts.info) {
612 printf("FAT: %s\n", fs->type->name);
613 printf("Cur size: %llu\n", fs->geom->length * dev->sector_size);
614 printf("Min size: %llu\n", constraint->min_size * dev->sector_size);
615 printf("Max size: %llu\n", constraint->max_size * dev->sector_size);
616
617 ped_constraint_destroy(constraint);
618 return 0;
619 }
620 if (opts.size == LLONG_MAX) {
621 opts.size = constraint->max_size * dev->sector_size;
622 ped_constraint_destroy(constraint);
623 }
624
625 start = part_geom.start;
626 printd(3, "ped_geometry_new(%llu)\n", start);
627 range_start = ped_geometry_new(dev, start, 1);
628 if (!range_start) {
629 return 1;
630 }
631
632 end = part_geom.start + opts.size / dev->sector_size;
633 printd(3, "ped_unit_parse(%llu)\n", end);
634 old_str = ped_unit_format(dev, part_geom.end);
635 def_str = ped_unit_format(dev, end);
636 if (!strcmp(old_str, def_str)) {
637 range_end = ped_geometry_new(dev, part_geom.end, 1);
638 if (!range_end) {
639 return 1;
640 }
641 } else if (!ped_unit_parse(def_str, dev, &end, &range_end)) {
642 return 1;
643 }
644 free(old_str);
645 free(def_str);
646
647 printd(3, "ped_geometry_duplicate()\n");
648 old_geom = ped_geometry_duplicate(&part_geom);
649 if (!old_geom) {
650 return 1;
651 }
652
653 printd(3, "ped_geometry_init(%llu, %llu)\n", start, end - start + 1);
654 if (!ped_geometry_init(&new_geom, dev, start, end - start + 1)) {
655 return 1;
656 }
657
658 printd(3, "snap_to_boundaries()\n");
659 snap_to_boundaries(&new_geom, &part_geom, disk, range_start, range_end);
660
661 printd(3, "ped_file_system_open()\n");
662 fs = ped_file_system_open(&part->geom);
663 if (!fs) {
664 return 1;
665 }
666
667 printd(3, "constraint_intersect_and_destroy()\n");
668 constraint = constraint_intersect_and_destroy(
669 ped_file_system_get_resize_constraint(fs),
670 constraint_from_start_end(dev, range_start, range_end));
671 if (!constraint) {
672 return 1;
673 }
674
675 /* Resize partition */
676 if (opts.pnum > 0) {
677 printd(3, "ped_disk_set_partition_geom(%llu, %llu)\n", new_geom.start,
678 new_geom.end);
679 if (!ped_disk_set_partition_geom(disk, part, constraint, new_geom.start,
680 new_geom.end)) {
681 ped_file_system_close(fs);
682 ped_constraint_destroy(constraint);
683 return 1;
684 }
685 }
686
687 printd(1, "Resizing file system.\n");
688 if (!ped_file_system_resize(fs, &new_geom, timer)) {
689 return 1;
690 }
691 if (opts.verbose == 3 && opts.progress) {
692 printf("\n");
693 }
694
695 printd(1, "Done.\n");
696 /* May have changed... fat16 -> fat32 */
697 if (opts.pnum > 0) {
698 ped_partition_set_system(part, fs->type);
699 }
700 ped_file_system_close(fs);
701 ped_constraint_destroy(constraint);
702
703 if (opts.pnum > 0) {
704 printd(1, "Committing changes.\n");
705 if (!ped_disk_commit(disk)) {
706 return 1;
707 }
708 ped_disk_destroy(disk);
709 }
710
711 if (dev->boot_dirty && dev->type != PED_DEVICE_FILE) {
712 ped_exception_throw(PED_EXCEPTION_WARNING, PED_EXCEPTION_OK,
713 ("You should reinstall your boot loader."
714 "Read section 4 of the Parted User "
715 "documentation for more information."));
716 }
717
718 ped_device_close(dev);
719
720 return 0;
721 }