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