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