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