b7b4047bbbea31f067838995336663757a41419e
[platform/upstream/multipath-tools.git] / kpartx / kpartx.c
1 /*
2  * Source: copy of util-linux' partx partx.c
3  *
4  * Copyrights of the original file applies
5  * Copyright (c) 2004, 2005 Christophe Varoqui
6  * Copyright (c) 2005 Kiyoshi Ueda
7  * Copyright (c) 2005 Lars Soltau
8  */
9
10 /*
11  * Given a block device and a partition table type,
12  * try to parse the partition table, and list the
13  * contents. Optionally add or remove partitions.
14  *
15  * Read wholedisk and add all partitions:
16  *      kpartx [-a|-d|-l] [-v] wholedisk
17  *
18  * aeb, 2000-03-21
19  * cva, 2002-10-26
20  */
21
22 #include <stdio.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <stdint.h>
29 #include <sys/ioctl.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <ctype.h>
33 #include <libdevmapper.h>
34
35 #include "devmapper.h"
36 #include "crc32.h"
37 #include "lopart.h"
38 #include "kpartx.h"
39
40 #define SIZE(a) (sizeof(a)/sizeof((a)[0]))
41
42 #define READ_SIZE       1024
43 #define MAXTYPES        64
44 #define MAXSLICES       256
45 #define DM_TARGET       "linear"
46 #define LO_NAME_SIZE    64
47 #define PARTNAME_SIZE   128
48 #define DELIM_SIZE      8
49
50 struct slice slices[MAXSLICES];
51
52 enum action { LIST, ADD, DELETE, UPDATE };
53
54 struct pt {
55         char *type;
56         ptreader *fn;
57 } pts[MAXTYPES];
58
59 int ptct = 0;
60 int udev_sync = 0;
61
62 static void
63 addpts(char *t, ptreader f)
64 {
65         if (ptct >= MAXTYPES) {
66                 fprintf(stderr, "addpts: too many types\n");
67                 exit(1);
68         }
69         pts[ptct].type = t;
70         pts[ptct].fn = f;
71         ptct++;
72 }
73
74 static void
75 initpts(void)
76 {
77         addpts("gpt", read_gpt_pt);
78         addpts("dos", read_dos_pt);
79         addpts("bsd", read_bsd_pt);
80         addpts("solaris", read_solaris_pt);
81         addpts("unixware", read_unixware_pt);
82         addpts("dasd", read_dasd_pt);
83         addpts("mac", read_mac_pt);
84         addpts("sun", read_sun_pt);
85         addpts("ps3", read_ps3_pt);
86 }
87
88 static char short_opts[] = "rladfgvp:t:su";
89
90 /* Used in gpt.c */
91 int force_gpt=0;
92
93 int force_devmap=0;
94
95 static int
96 usage(void) {
97         printf("usage : kpartx [-a|-d|-l] [-f] [-v] wholedisk\n");
98         printf("\t-a add partition devmappings\n");
99         printf("\t-r devmappings will be readonly\n");
100         printf("\t-d del partition devmappings\n");
101         printf("\t-u update partition devmappings\n");
102         printf("\t-l list partitions devmappings that would be added by -a\n");
103         printf("\t-p set device name-partition number delimiter\n");
104         printf("\t-g force GUID partition table (GPT)\n");
105         printf("\t-f force devmap create\n");
106         printf("\t-v verbose\n");
107         printf("\t-s sync mode. Don't return until the partitions are created\n");
108         return 1;
109 }
110
111 static void
112 set_delimiter (char * device, char * delimiter)
113 {
114         char * p = device;
115
116         while (*(p++) != 0x0)
117                 continue;
118
119         if (isdigit(*(p - 2)))
120                 *delimiter = 'p';
121 }
122
123 static void
124 strip_slash (char * device)
125 {
126         char * p = device;
127
128         while (*(p++) != 0x0) {
129
130                 if (*p == '/')
131                         *p = '!';
132         }
133 }
134
135 static int
136 find_devname_offset (char * device)
137 {
138         char *p, *q = NULL;
139
140         p = device;
141
142         while (*p++)
143                 if (*p == '/')
144                         q = p;
145
146         return (int)(q - device) + 1;
147 }
148
149 static char *
150 get_hotplug_device(void)
151 {
152         unsigned int major, minor, off, len;
153         char *mapname;
154         char *devname = NULL;
155         char *device = NULL;
156         char *var = NULL;
157         struct stat buf;
158
159         var = getenv("ACTION");
160
161         if (!var || strcmp(var, "add"))
162                 return NULL;
163
164         /* Get dm mapname for hotpluged device. */
165         if (!(devname = getenv("DEVNAME")))
166                 return NULL;
167
168         if (stat(devname, &buf))
169                 return NULL;
170
171         major = major(buf.st_rdev);
172         minor = minor(buf.st_rdev);
173
174         if (!(mapname = dm_mapname(major, minor))) /* Not dm device. */
175                 return NULL;
176
177         off = find_devname_offset(devname);
178         len = strlen(mapname);
179
180         /* Dirname + mapname + \0 */
181         if (!(device = (char *)malloc(sizeof(char) * (off + len + 1)))) {
182                 free(mapname);
183                 return NULL;
184         }
185
186         /* Create new device name. */
187         snprintf(device, off + 1, "%s", devname);
188         snprintf(device + off, len + 1, "%s", mapname);
189
190         if (strlen(device) != (off + len)) {
191                 free(device);
192                 free(mapname);
193                 return NULL;
194         }
195         free(mapname);
196         return device;
197 }
198
199 static int
200 check_uuid(char *uuid, char *part_uuid, char **err_msg) {
201         char *map_uuid = strchr(part_uuid, '-');
202         if (!map_uuid || strncmp(part_uuid, "part", 4) != 0) {
203                 *err_msg = "not a kpartx partition";
204                 return -1;
205         }
206         map_uuid++;
207         if (strcmp(uuid, map_uuid) != 0) {
208                 *err_msg = "a partition of a different device";
209                 return -1;
210         }
211         return 0;
212 }
213
214 int
215 main(int argc, char **argv){
216         int i, j, m, n, op, off, arg, c, d, ro=0;
217         int fd = -1;
218         struct slice all;
219         struct pt *ptp;
220         enum action what = LIST;
221         char *type, *diskdevice, *device, *progname;
222         int verbose = 0;
223         char partname[PARTNAME_SIZE], params[PARTNAME_SIZE + 16];
224         char * loopdev = NULL;
225         char * delim = NULL;
226         char *uuid = NULL;
227         char *mapname = NULL;
228         int hotplug = 0;
229         int loopcreated = 0;
230         struct stat buf;
231
232         initpts();
233         init_crc32();
234
235         type = device = diskdevice = NULL;
236         memset(&all, 0, sizeof(all));
237         memset(&partname, 0, sizeof(partname));
238
239         /* Check whether hotplug mode. */
240         progname = strrchr(argv[0], '/');
241
242         if (!progname)
243                 progname = argv[0];
244         else
245                 progname++;
246
247         if (!strcmp(progname, "kpartx.dev")) { /* Hotplug mode */
248                 hotplug = 1;
249
250                 /* Setup for original kpartx variables */
251                 if (!(device = get_hotplug_device()))
252                         exit(1);
253
254                 diskdevice = device;
255                 what = ADD;
256         } else if (argc < 2) {
257                 usage();
258                 exit(1);
259         }
260
261         while ((arg = getopt(argc, argv, short_opts)) != EOF) switch(arg) {
262                 case 'r':
263                         ro=1;
264                         break;
265                 case 'f':
266                         force_devmap=1;
267                         break;
268                 case 'g':
269                         force_gpt=1;
270                         break;
271                 case 't':
272                         type = optarg;
273                         break;
274                 case 'v':
275                         verbose = 1;
276                         break;
277                 case 'p':
278                         delim = optarg;
279                         break;
280                 case 'l':
281                         what = LIST;
282                         break;
283                 case 'a':
284                         what = ADD;
285                         break;
286                 case 'd':
287                         what = DELETE;
288                         break;
289                 case 's':
290                         udev_sync = 1;
291                         break;
292                 case 'u':
293                         what = UPDATE;
294                         break;
295                 default:
296                         usage();
297                         exit(1);
298         }
299
300 #ifdef LIBDM_API_COOKIE
301         if (!udev_sync)
302                 dm_udev_set_sync_support(0);
303         else
304                 dm_udev_set_sync_support(1);
305 #endif
306
307         if (dm_prereq(DM_TARGET, 0, 0, 0) && (what == ADD || what == DELETE || what == UPDATE)) {
308                 fprintf(stderr, "device mapper prerequisites not met\n");
309                 exit(1);
310         }
311
312         if (hotplug) {
313                 /* already got [disk]device */
314         } else if (optind == argc-2) {
315                 device = argv[optind];
316                 diskdevice = argv[optind+1];
317         } else if (optind == argc-1) {
318                 diskdevice = device = argv[optind];
319         } else {
320                 usage();
321                 exit(1);
322         }
323
324         if (stat(device, &buf)) {
325                 printf("failed to stat() %s\n", device);
326                 exit (1);
327         }
328
329         if (S_ISREG (buf.st_mode)) {
330                 /* already looped file ? */
331                 loopdev = find_loop_by_file(device);
332
333                 if (!loopdev && what == DELETE)
334                         exit (0);
335
336                 if (!loopdev) {
337                         loopdev = find_unused_loop_device();
338
339                         if (set_loop(loopdev, device, 0, &ro)) {
340                                 fprintf(stderr, "can't set up loop\n");
341                                 exit (1);
342                         }
343                         loopcreated = 1;
344                 }
345                 device = loopdev;
346
347                 if (stat(device, &buf)) {
348                         printf("failed to stat() %s\n", device);
349                         exit (1);
350                 }
351         }
352
353         off = find_devname_offset(device);
354
355         if (!loopdev) {
356                 uuid = dm_mapuuid(major(buf.st_rdev), minor(buf.st_rdev));
357                 mapname = dm_mapname(major(buf.st_rdev), minor(buf.st_rdev));
358         }
359
360         if (!uuid)
361                 uuid = device + off;
362
363         if (!mapname)
364                 mapname = device + off;
365         else if (!force_devmap &&
366                  dm_no_partitions(major(buf.st_rdev), minor(buf.st_rdev))) {
367                 /* Feature 'no_partitions' is set, return */
368                 return 0;
369         }
370
371         if (delim == NULL) {
372                 delim = malloc(DELIM_SIZE);
373                 memset(delim, 0, DELIM_SIZE);
374                 set_delimiter(mapname, delim);
375         }
376
377         fd = open(device, O_RDONLY);
378
379         if (fd == -1) {
380                 perror(device);
381                 exit(1);
382         }
383
384         /* add/remove partitions to the kernel devmapper tables */
385         int r = 0;
386         for (i = 0; i < ptct; i++) {
387                 ptp = &pts[i];
388
389                 if (type && strcmp(type, ptp->type))
390                         continue;
391
392                 /* here we get partitions */
393                 n = ptp->fn(fd, all, slices, SIZE(slices));
394
395 #ifdef DEBUG
396                 if (n >= 0)
397                         printf("%s: %d slices\n", ptp->type, n);
398 #endif
399
400                 if (n > 0) {
401                         close(fd);
402                         fd = -1;
403                 }
404                 else
405                         continue;
406
407                 switch(what) {
408                 case LIST:
409                         for (j = 0, c = 0, m = 0; j < n; j++) {
410                                 if (slices[j].size == 0)
411                                         continue;
412                                 if (slices[j].container > 0) {
413                                         c++;
414                                         continue;
415                                 }
416
417                                 slices[j].minor = m++;
418
419                                 printf("%s%s%d : 0 %" PRIu64 " %s %" PRIu64"\n",
420                                        mapname, delim, j+1,
421                                        slices[j].size, device,
422                                        slices[j].start);
423                         }
424                         /* Loop to resolve contained slices */
425                         d = c;
426                         while (c) {
427                                 for (j = 0; j < n; j++) {
428                                         uint64_t start;
429                                         int k = slices[j].container - 1;
430
431                                         if (slices[j].size == 0)
432                                                 continue;
433                                         if (slices[j].minor > 0)
434                                                 continue;
435                                         if (slices[j].container == 0)
436                                                 continue;
437                                         slices[j].minor = m++;
438
439                                         start = slices[j].start - slices[k].start;
440                                         printf("%s%s%d : 0 %" PRIu64 " /dev/dm-%d %" PRIu64 "\n",
441                                                mapname, delim, j+1,
442                                                slices[j].size,
443                                                slices[k].minor, start);
444                                         c--;
445                                 }
446                                 /* Terminate loop if nothing more to resolve */
447                                 if (d == c)
448                                         break;
449                         }
450
451                         break;
452
453                 case DELETE:
454                         for (j = MAXSLICES-1; j >= 0; j--) {
455                                 char *part_uuid, *reason;
456
457                                 if (safe_sprintf(partname, "%s%s%d",
458                                              mapname, delim, j+1)) {
459                                         fprintf(stderr, "partname too small\n");
460                                         exit(1);
461                                 }
462                                 strip_slash(partname);
463
464                                 if (!dm_map_present(partname, &part_uuid))
465                                         continue;
466
467                                 if (part_uuid && uuid) {
468                                         if (check_uuid(uuid, part_uuid, &reason) != 0) {
469                                                 fprintf(stderr, "%s is %s. Not removing\n", partname, reason);
470                                                 free(part_uuid);
471                                                 continue;
472                                         }
473                                         free(part_uuid);
474                                 }
475
476                                 if (!dm_simplecmd(DM_DEVICE_REMOVE, partname,
477                                                   0, 0)) {
478                                         r++;
479                                         continue;
480                                 }
481                                 if (verbose)
482                                         printf("del devmap : %s\n", partname);
483                         }
484
485                         if (loopdev) {
486                                 if (del_loop(loopdev)) {
487                                         if (verbose)
488                                                 printf("can't del loop : %s\n",
489                                                         loopdev);
490                                         exit(1);
491                                 }
492                                 printf("loop deleted : %s\n", loopdev);
493                         }
494                         break;
495
496                 case ADD:
497                 case UPDATE:
498                         /* ADD and UPDATE share the same code that adds new partitions. */
499                         for (j = 0, c = 0; j < n; j++) {
500                                 char *part_uuid, *reason;
501
502                                 if (slices[j].size == 0)
503                                         continue;
504
505                                 /* Skip all contained slices */
506                                 if (slices[j].container > 0) {
507                                         c++;
508                                         continue;
509                                 }
510
511                                 if (safe_sprintf(partname, "%s%s%d",
512                                              mapname, delim, j+1)) {
513                                         fprintf(stderr, "partname too small\n");
514                                         exit(1);
515                                 }
516                                 strip_slash(partname);
517
518                                 if (safe_sprintf(params, "%d:%d %" PRIu64 ,
519                                                  major(buf.st_rdev), minor(buf.st_rdev), slices[j].start)) {
520                                         fprintf(stderr, "params too small\n");
521                                         exit(1);
522                                 }
523
524                                 op = (dm_map_present(partname, &part_uuid) ?
525                                         DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
526
527                                 if (part_uuid && uuid) {
528                                         if (check_uuid(uuid, part_uuid, &reason) != 0) {
529                                                 fprintf(stderr, "%s is already in use, and %s\n", partname, reason);
530                                                 r++;
531                                                 free(part_uuid);
532                                                 continue;
533                                         }
534                                         free(part_uuid);
535                                 }
536
537                                 if (!dm_addmap(op, partname, DM_TARGET, params,
538                                                slices[j].size, ro, uuid, j+1,
539                                                buf.st_mode & 0777, buf.st_uid,
540                                                buf.st_gid)) {
541                                         fprintf(stderr, "create/reload failed on %s\n",
542                                                 partname);
543                                         r++;
544                                         continue;
545                                 }
546                                 if (op == DM_DEVICE_RELOAD &&
547                                     !dm_simplecmd(DM_DEVICE_RESUME, partname,
548                                                   1, MPATH_UDEV_RELOAD_FLAG)) {
549                                         fprintf(stderr, "resume failed on %s\n",
550                                                 partname);
551                                         r++;
552                                         continue;
553                                 }
554
555                                 dm_devn(partname, &slices[j].major,
556                                         &slices[j].minor);
557
558                                 if (verbose)
559                                         printf("add map %s (%d:%d): 0 %" PRIu64 " %s %s\n",
560                                                partname, slices[j].major,
561                                                slices[j].minor, slices[j].size,
562                                                DM_TARGET, params);
563                         }
564                         /* Loop to resolve contained slices */
565                         d = c;
566                         while (c) {
567                                 for (j = 0; j < n; j++) {
568                                         char *part_uuid, *reason;
569                                         int k = slices[j].container - 1;
570
571                                         if (slices[j].size == 0)
572                                                 continue;
573
574                                         /* Skip all existing slices */
575                                         if (slices[j].minor > 0)
576                                                 continue;
577
578                                         /* Skip all simple slices */
579                                         if (slices[j].container == 0)
580                                                 continue;
581
582                                         /* Check container slice */
583                                         if (slices[k].size == 0)
584                                                 fprintf(stderr, "Invalid slice %d\n",
585                                                         k);
586
587                                         if (safe_sprintf(partname, "%s%s%d",
588                                                          mapname, delim, j+1)) {
589                                                 fprintf(stderr, "partname too small\n");
590                                                 exit(1);
591                                         }
592                                         strip_slash(partname);
593
594                                         if (safe_sprintf(params, "%d:%d %" PRIu64,
595                                                          major(buf.st_rdev), minor(buf.st_rdev),
596                                                          slices[j].start)) {
597                                                 fprintf(stderr, "params too small\n");
598                                                 exit(1);
599                                         }
600
601                                         op = (dm_map_present(partname,
602                                                              &part_uuid) ?
603                                               DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
604
605                                         if (part_uuid && uuid) {
606                                                 if (check_uuid(uuid, part_uuid, &reason) != 0) {
607                                                         fprintf(stderr, "%s is already in use, and %s\n", partname, reason);
608                                                         free(part_uuid);
609                                                         continue;
610                                                 }
611                                                 free(part_uuid);
612                                         }
613
614                                         dm_addmap(op, partname, DM_TARGET, params,
615                                                   slices[j].size, ro, uuid, j+1,
616                                                   buf.st_mode & 0777,
617                                                   buf.st_uid, buf.st_gid);
618
619                                         if (op == DM_DEVICE_RELOAD)
620                                                 dm_simplecmd(DM_DEVICE_RESUME,
621                                                              partname, 1,
622                                                              MPATH_UDEV_RELOAD_FLAG);
623                                         dm_devn(partname, &slices[j].major,
624                                                 &slices[j].minor);
625
626                                         if (verbose)
627                                                 printf("add map %s (%d:%d): 0 %" PRIu64 " %s %s\n",
628                                                        partname, slices[j].major, slices[j].minor, slices[j].size,
629                                                        DM_TARGET, params);
630                                         c--;
631                                 }
632                                 /* Terminate loop */
633                                 if (d == c)
634                                         break;
635                         }
636
637                         if (what == ADD) {
638                                 /* Skip code that removes devmappings for deleted partitions */
639                                 break;
640                         }
641
642                         for (j = MAXSLICES-1; j >= 0; j--) {
643                                 char *part_uuid, *reason;
644                                 if (safe_sprintf(partname, "%s%s%d",
645                                              mapname, delim, j+1)) {
646                                         fprintf(stderr, "partname too small\n");
647                                         exit(1);
648                                 }
649                                 strip_slash(partname);
650
651                                 if (slices[j].size ||
652                                     !dm_map_present(partname, &part_uuid))
653                                         continue;
654
655                                 if (part_uuid && uuid) {
656                                         if (check_uuid(uuid, part_uuid, &reason) != 0) {
657                                                 fprintf(stderr, "%s is %s. Not removing\n", partname, reason);
658                                                 free(part_uuid);
659                                                 continue;
660                                         }
661                                         free(part_uuid);
662                                 }
663
664                                 if (!dm_simplecmd(DM_DEVICE_REMOVE,
665                                                   partname, 1, 0)) {
666                                         r++;
667                                         continue;
668                                 }
669                                 if (verbose)
670                                         printf("del devmap : %s\n", partname);
671                         }
672
673                 default:
674                         break;
675
676                 }
677                 if (n > 0)
678                         break;
679         }
680         if (what == LIST && loopcreated && S_ISREG (buf.st_mode)) {
681                 if (fd != -1)
682                         close(fd);
683                 if (del_loop(device)) {
684                         if (verbose)
685                                 printf("can't del loop : %s\n",
686                                         device);
687                         exit(1);
688                 }
689                 printf("loop deleted : %s\n", device);
690         }
691
692         dm_lib_release();
693         dm_lib_exit();
694
695         return r;
696 }
697
698 void *
699 xmalloc (size_t size) {
700         void *t;
701
702         if (size == 0)
703                 return NULL;
704
705         t = malloc (size);
706
707         if (t == NULL) {
708                 fprintf(stderr, "Out of memory\n");
709                 exit(1);
710         }
711
712         return t;
713 }
714
715 /*
716  * sseek: seek to specified sector
717  */
718
719 static int
720 sseek(int fd, unsigned int secnr) {
721         off64_t in, out;
722         in = ((off64_t) secnr << 9);
723         out = 1;
724
725         if ((out = lseek64(fd, in, SEEK_SET)) != in)
726         {
727                 fprintf(stderr, "llseek error\n");
728                 return -1;
729         }
730         return 0;
731 }
732
733 static
734 struct block {
735         unsigned int secnr;
736         char *block;
737         struct block *next;
738 } *blockhead;
739
740 char *
741 getblock (int fd, unsigned int secnr) {
742         struct block *bp;
743
744         for (bp = blockhead; bp; bp = bp->next)
745
746                 if (bp->secnr == secnr)
747                         return bp->block;
748
749         if (sseek(fd, secnr))
750                 return NULL;
751
752         bp = xmalloc(sizeof(struct block));
753         bp->secnr = secnr;
754         bp->next = blockhead;
755         blockhead = bp;
756         bp->block = (char *) xmalloc(READ_SIZE);
757
758         if (read(fd, bp->block, READ_SIZE) != READ_SIZE) {
759                 fprintf(stderr, "read error, sector %d\n", secnr);
760                 bp->block = NULL;
761         }
762
763         return bp->block;
764 }
765
766 int
767 get_sector_size(int filedes)
768 {
769         int rc, sector_size = 512;
770
771         rc = ioctl(filedes, BLKSSZGET, &sector_size);
772         if (rc)
773                 sector_size = 512;
774         return sector_size;
775 }