1  /*
       2   * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
       3   *
       4   * This file may be redistributed under the terms of the
       5   * GNU Lesser General Public License.
       6   *
       7   *
       8   * Libfdisk sample to create partitions by specify all required partition
       9   * properties (partno, start and size). The default is only partition type
      10   * (except MBR where 4th partition is extended).
      11   */
      12  #include <stdlib.h>
      13  #include <unistd.h>
      14  #include <string.h>
      15  #include <errno.h>
      16  #include <sys/types.h>
      17  #include <sys/stat.h>
      18  #include <dirent.h>
      19  #include <getopt.h>
      20  
      21  #include "c.h"
      22  #include "nls.h"
      23  #include "strutils.h"
      24  #include "xalloc.h"
      25  
      26  #include "libfdisk.h"
      27  
      28  static int ask_callback(struct fdisk_context *cxt __attribute__((__unused__)),
      29  			struct fdisk_ask *ask,
      30  			void *data)
      31  {
      32  	switch(fdisk_ask_get_type(ask)) {
      33  	case FDISK_ASKTYPE_INFO:
      34  		fputs(fdisk_ask_print_get_mesg(ask), stdout);
      35  		fputc('\n', stdout);
      36  		break;
      37  	case FDISK_ASKTYPE_WARNX:
      38  		fflush(stdout);
      39  		fputs(fdisk_ask_print_get_mesg(ask), stderr);
      40  		fputc('\n', stderr);
      41  		break;
      42  	case FDISK_ASKTYPE_WARN:
      43  		fflush(stdout);
      44  		fputs(fdisk_ask_print_get_mesg(ask), stderr);
      45  		errno = fdisk_ask_print_get_errno(ask);
      46  		fprintf(stderr, ": %m\n");
      47  		break;
      48  	default:
      49  		break;
      50  	}
      51  	return 0;
      52  }
      53  
      54  int main(int argc, char *argv[])
      55  {
      56  	struct fdisk_context *cxt;
      57  	struct fdisk_partition *pa;
      58  	const char *label = NULL, *device = NULL;
      59  	int c;
      60  	size_t n = 1;
      61  
      62  	static const struct option longopts[] = {
      63  		{ "label",  required_argument, NULL, 'x' },
      64  		{ "device", required_argument, NULL, 'd' },
      65  		{ "help",   no_argument, NULL, 'h' },
      66  		{ NULL, 0, NULL, 0 },
      67  	};
      68  
      69  	setlocale(LC_ALL, "");	/* just to have enable UTF8 chars */
      70  
      71  	fdisk_init_debug(0);
      72  
      73  	while((c = getopt_long(argc, argv, "x:d:h", longopts, NULL)) != -1) {
      74  		switch(c) {
      75  		case 'x':
      76  			label = optarg;
      77  			break;
      78  		case 'd':
      79  			device = optarg;
      80  			break;
      81  		case 'h':
      82  			printf("%s [options] -- <partno,start,size> ...", program_invocation_short_name);
      83  			fputs(USAGE_SEPARATOR, stdout);
      84  			puts("Make disklabel and partitions.");
      85  			puts(" <partno>                     1..n (4th is extended for MBR), or '-' for default");
      86  			puts(" <start>                      partition start offset in sectors");
      87  			puts(" <size>                       partition size in sectors");
      88  			fputs(USAGE_OPTIONS, stdout);
      89  			puts(" -x, --label <dos,gpt,...>    disk label type (default MBR)");
      90  			puts(" -d, --device <path>          block device");
      91  			puts(" -h, --help                   this help");
      92  			fputs(USAGE_SEPARATOR, stdout);
      93  			return EXIT_SUCCESS;
      94  		}
      95  	}
      96  
      97  	if (!device)
      98  		errx(EXIT_FAILURE, "no device specified");
      99  	if (!label)
     100  		label = "dos";
     101  
     102  	cxt = fdisk_new_context();
     103  	if (!cxt)
     104  		err_oom();
     105  	fdisk_set_ask(cxt, ask_callback, NULL);
     106  
     107  	pa = fdisk_new_partition();
     108  	if (!pa)
     109  		err_oom();
     110  
     111  	if (fdisk_assign_device(cxt, device, 0))
     112  		err(EXIT_FAILURE, "failed to assign device");
     113  	if (fdisk_create_disklabel(cxt, label))
     114  		err(EXIT_FAILURE, "failed to create disk label");
     115  
     116  	fdisk_disable_dialogs(cxt, 1);
     117  
     118  	while (optind < argc) {
     119  		int rc;
     120  		unsigned int partno = 0;
     121  		uint64_t start = 0, size = 0;
     122  		const char *str = argv[optind];
     123  
     124  		fdisk_reset_partition(pa);
     125  		fdisk_partition_end_follow_default(pa, 0);
     126  
     127  		if (*str == '-') {
     128  			/* partno unspecified */
     129  			if (sscanf(str, "-,%"SCNu64",%"SCNu64"", &start, &size) != 2)
     130  				errx(EXIT_FAILURE, "failed to parse %s", str);
     131  			fdisk_partition_partno_follow_default(pa, 1);
     132  			fdisk_partition_unset_partno(pa);
     133  		} else {
     134  			/* partno specified */
     135  			if (sscanf(str, "%u,%"SCNu64",%"SCNu64"", &partno, &start, &size) != 3)
     136  				errx(EXIT_FAILURE, "failed to parse %s", str);
     137  
     138  			fdisk_partition_partno_follow_default(pa, 0);
     139  			fdisk_partition_set_partno(pa, partno - 1);     /* library uses 0..n */
     140  		}
     141  
     142  		fdisk_partition_set_start(pa, start);
     143  		fdisk_partition_set_size(pa, size);
     144  
     145  		if (fdisk_partition_has_partno(pa))
     146  			fprintf(stdout, "Requested partition: <partno=%zu,start=%ju,size=%ju>\n",
     147  					fdisk_partition_get_partno(pa),
     148  					(uintmax_t) fdisk_partition_get_start(pa),
     149  					(uintmax_t) fdisk_partition_get_size(pa));
     150  		else
     151  			fprintf(stdout, "Requested partition: <partno=<default>,start=%ju,size=%ju>\n",
     152  					(uintmax_t) fdisk_partition_get_start(pa),
     153  					(uintmax_t) fdisk_partition_get_size(pa));
     154  
     155  		if (fdisk_is_label(cxt, DOS)) {
     156  			/* Make sure last primary partition is extended if user
     157  			 * wants more than 4 partitions.
     158  			 */
     159  			if ((partno == 4 || (n == 4 && !fdisk_partition_has_partno(pa)))
     160  			    && optind + 1 < argc) {
     161  				struct fdisk_parttype *type =
     162  					fdisk_label_parse_parttype(
     163  							fdisk_get_label(cxt, NULL), "05");
     164  				if (!type)
     165  					err_oom();
     166  				fdisk_partition_set_type(pa, type);
     167  				fdisk_unref_parttype(type);
     168  			}
     169  		}
     170  
     171  		rc = fdisk_add_partition(cxt, pa, NULL);
     172  		if (rc) {
     173  			errno = -rc;
     174  			errx(EXIT_FAILURE, "failed to add #%zu partition",
     175  					fdisk_partition_has_partno(pa) ?
     176  					fdisk_partition_get_partno(pa) + 1: n);
     177  		}
     178  
     179  		fdisk_reset_partition(pa);
     180  		optind++;
     181  		n++;
     182  	}
     183  
     184  	if (fdisk_write_disklabel(cxt))
     185  		err(EXIT_FAILURE, "failed to write disk label");
     186  
     187  	fdisk_deassign_device(cxt, 1);
     188  	fdisk_unref_context(cxt);
     189  	fdisk_unref_partition(pa);
     190  
     191  	return EXIT_SUCCESS;
     192  }