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