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