[kpartx] bug fixes for dos extended partition [3/4]
[platform/upstream/multipath-tools.git] / kpartx / kpartx.c
1 /*
2  * Source: copy of util-linux' partx partx.c
3  *
4  * Copyrights of the original file applies
5  * Copyright (c) 2004, 2005 Christophe Varoqui
6  * Copyright (c) 2005 Kiyoshi Ueda
7  * Copyright (c) 2005 Lars Soltau
8  */
9
10 /*
11  * Given a block device and a partition table type,
12  * try to parse the partition table, and list the
13  * contents. Optionally add or remove partitions.
14  *
15  * Read wholedisk and add all partitions:
16  *      kpartx [-a|-d|-l] [-v] wholedisk
17  *
18  * aeb, 2000-03-21
19  * cva, 2002-10-26
20  */
21
22 #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 <sys/stat.h>
29 #include <sys/types.h>
30 #include <ctype.h>
31 #include <libdevmapper.h>
32 #include <linux/kdev_t.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 }
81
82 static char short_opts[] = "ladgvnp:t:";
83
84 /* Used in gpt.c */
85 int force_gpt=0;
86
87 static int
88 usage(void) {
89         printf("usage : kpartx [-a|-d|-l] [-v] wholedisk\n");
90         printf("\t-a add partition devmappings\n");
91         printf("\t-d del partition devmappings\n");
92         printf("\t-l list partitions devmappings that would be added by -a\n");
93         printf("\t-p set device name-partition number delimiter\n");
94         printf("\t-v verbose\n");
95         return 1;
96 }
97
98 static void
99 set_delimiter (char * device, char * delimiter)
100 {
101         char * p = device;
102
103         while (*(p++) != 0x0)
104                 continue;
105
106         if (isdigit(*(p - 2)))
107                 *delimiter = 'p';
108 }
109
110 static void
111 strip_slash (char * device)
112 {
113         char * p = device;
114
115         while (*(p++) != 0x0) {
116                 
117                 if (*p == '/')
118                         *p = '!';
119         }
120 }
121
122 static int
123 find_devname_offset (char * device)
124 {
125         char *p, *q = NULL;
126         
127         p = device;
128         
129         while (*p++)
130                 if (*p == '/')
131                         q = p;
132
133         return (int)(q - device) + 1;
134 }
135
136 static char *
137 get_hotplug_device(void)
138 {
139         unsigned int major, minor, off, len;
140         const char *mapname;
141         char *devname = NULL;
142         char *device = NULL;
143         char *var = NULL;
144         struct stat buf;
145
146         var = getenv("ACTION");
147
148         if (!var || strcmp(var, "add"))
149                 return NULL;
150
151         /* Get dm mapname for hotpluged device. */
152         if (!(devname = getenv("DEVNAME")))
153                 return NULL;
154
155         if (stat(devname, &buf))
156                 return NULL;
157
158         major = (unsigned int)MAJOR(buf.st_rdev);
159         minor = (unsigned int)MINOR(buf.st_rdev);
160
161         if (!(mapname = dm_mapname(major, minor))) /* Not dm device. */
162                 return NULL;
163
164         off = find_devname_offset(devname);
165         len = strlen(mapname);
166
167         /* Dirname + mapname + \0 */
168         if (!(device = (char *)malloc(sizeof(char) * (off + len + 1))))
169                 return NULL;
170
171         /* Create new device name. */
172         snprintf(device, off + 1, "%s", devname);
173         snprintf(device + off, len + 1, "%s", mapname);
174
175         if (strlen(device) != (off + len))
176                 return NULL;
177
178         return device;
179 }
180
181 int
182 main(int argc, char **argv){
183         int fd, i, j, k, n, op, off, arg;
184         struct slice all;
185         struct pt *ptp;
186         enum action what = LIST;
187         char *p, *type, *diskdevice, *device, *progname;
188         int lower, upper;
189         int verbose = 0;
190         char partname[PARTNAME_SIZE], params[PARTNAME_SIZE + 16];
191         char * loopdev = NULL;
192         char * delim = NULL;
193         int loopro = 0;
194         int hotplug = 0;
195         struct stat buf;
196
197         initpts();
198         init_crc32();
199
200         lower = upper = 0;
201         type = device = diskdevice = NULL;
202         memset(&all, 0, sizeof(all));
203         memset(&partname, 0, sizeof(partname));
204         
205         /* Check whether hotplug mode. */
206         progname = strrchr(argv[0], '/');
207
208         if (!progname)
209                 progname = argv[0];
210         else
211                 progname++;
212
213         if (!strcmp(progname, "kpartx.dev")) { /* Hotplug mode */
214                 hotplug = 1;
215
216                 /* Setup for original kpartx variables */
217                 if (!(device = get_hotplug_device()))
218                         exit(1);
219
220                 diskdevice = device;
221                 what = ADD;
222         } else if (argc < 2) {
223                 usage();
224                 exit(1);
225         }
226
227         while ((arg = getopt(argc, argv, short_opts)) != EOF) switch(arg) {
228                 case 'g':
229                         force_gpt=1;
230                         break;
231                 case 't':
232                         type = optarg;
233                         break;
234                 case 'v':
235                         verbose = 1;
236                         break;
237                 case 'n':
238                         p = optarg;
239                         lower = atoi(p);
240                         if ((p[1] == '-') && p[2])
241                                 upper = atoi(p+2);
242                         else
243                                 upper = lower;
244                         break;
245                 case 'p':
246                         delim = optarg;
247                         break;
248                 case 'l':
249                         what = LIST;
250                         break;
251                 case 'a':
252                         what = ADD;
253                         break;
254                 case 'd':
255                         what = DELETE;
256                         break;
257                 default:
258                         usage();
259                         exit(1);
260         }
261
262         if (dm_prereq(DM_TARGET, 0, 0, 0) && (what == ADD || what == DELETE)) {
263                 fprintf(stderr, "device mapper prerequisites not met\n"); 
264                 exit(1);
265         }
266
267         if (hotplug) {
268                 /* already got [disk]device */
269         } else if (optind == argc-2) {
270                 device = argv[optind];
271                 diskdevice = argv[optind+1];
272         } else if (optind == argc-1) {
273                 diskdevice = device = argv[optind];
274         } else {
275                 usage();
276                 exit(1);
277         }
278
279         if (stat(device, &buf)) {
280                 printf("failed to stat() %s\n", device);
281                 exit (1);
282         }
283
284         if (S_ISREG (buf.st_mode)) {
285                 loopdev = malloc(LO_NAME_SIZE * sizeof(char));
286                 
287                 if (!loopdev)
288                         exit(1);
289
290                 /* already looped file ? */
291                 loopdev = find_loop_by_file(device);
292
293                 if (!loopdev && what == DELETE)
294                         exit (0);
295                                 
296                 if (!loopdev) {
297                         loopdev = find_unused_loop_device();
298
299                         if (set_loop(loopdev, device, 0, &loopro)) {
300                                 fprintf(stderr, "can't set up loop\n");
301                                 exit (1);
302                         }
303                 }
304                 device = loopdev;
305         }
306
307         if (delim == NULL) {
308                 delim = malloc(DELIM_SIZE);
309                 memset(delim, 0, DELIM_SIZE);
310                 set_delimiter(device, delim);
311         }
312         
313         off = find_devname_offset(device);
314         fd = open(device, O_RDONLY);
315
316         if (fd == -1) {
317                 perror(device);
318                 exit(1);
319         }
320         if (!lower)
321                 lower = 1;
322
323         /* add/remove partitions to the kernel devmapper tables */
324         for (i = 0; i < ptct; i++) {
325                 ptp = &pts[i];
326
327                 if (type && strcmp(type, ptp->type))
328                         continue;
329                 
330                 /* here we get partitions */
331                 n = ptp->fn(fd, all, slices, SIZE(slices));
332
333 #ifdef DEBUG
334                 if (n >= 0)
335                         printf("%s: %d slices\n", ptp->type, n);
336 #endif
337
338                 if (n > 0)
339                         close(fd);
340                 else
341                         continue;
342
343                 /*
344                  * test for overlap, as in the case of an extended partition
345                  * zero their size to avoid mapping
346                  */
347                 for (j=0; j<n; j++) {
348                         for (k=j+1; k<n; k++) {
349                                 if (slices[k].start > slices[j].start &&
350                                     slices[k].start < slices[j].start +
351                                     slices[j].size)
352                                         slices[j].size = 0;
353                         }
354                 }
355
356                 switch(what) {
357                 case LIST:
358                         for (j = 0; j < n; j++) {
359                                 if (slices[j].size == 0)
360                                         continue;
361
362                                 printf("%s%s%d : 0 %lu %s %lu\n",
363                                         device + off, delim, j+1,
364                                         (unsigned long) slices[j].size, device,
365                                         (unsigned long) slices[j].start);
366                         }
367                         break;
368
369                 case DELETE:
370                         for (j = 0; j < n; j++) {
371                                 if (safe_sprintf(partname, "%s%s%d",
372                                              device + off , delim, j+1)) {
373                                         fprintf(stderr, "partname too small\n");
374                                         exit(1);
375                                 }
376                                 strip_slash(partname);
377
378                                 if (!slices[j].size || !dm_map_present(partname))
379                                         continue;
380
381                                 if (!dm_simplecmd(DM_DEVICE_REMOVE, partname))
382                                         continue;
383
384                                 if (verbose)
385                                         printf("del devmap : %s\n", partname);
386                         }
387
388                         if (S_ISREG (buf.st_mode)) {
389                                 if (del_loop(device)) {
390                                         if (verbose)
391                                                 printf("can't del loop : %s\n",
392                                                         device);
393                                         exit(1);
394                                 }
395                                 printf("loop deleted : %s\n", device);
396                         }
397                         break;
398
399                 case ADD:
400                         for (j=0; j<n; j++) {
401                                 if (slices[j].size == 0)
402                                         continue;
403
404                                 if (safe_sprintf(partname, "%s%s%d",
405                                              device + off , delim, j+1)) {
406                                         fprintf(stderr, "partname too small\n");
407                                         exit(1);
408                                 }
409                                 strip_slash(partname);
410                                 
411                                 if (safe_sprintf(params, "%s %lu", device,
412                                              (unsigned long)slices[j].start)) {
413                                         fprintf(stderr, "params too small\n");
414                                         exit(1);
415                                 }
416
417                                 op = (dm_map_present(partname) ?
418                                         DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
419
420                                 dm_addmap(op, partname, DM_TARGET, params,
421                                           slices[j].size);
422
423                                 if (op == DM_DEVICE_RELOAD)
424                                         dm_simplecmd(DM_DEVICE_RESUME,
425                                                         partname);
426
427                                 if (verbose)
428                                         printf("add map %s : 0 %lu %s %s\n",
429                                                 partname, slices[j].size,
430                                                 DM_TARGET, params);
431                         }
432                         break;
433
434                 default:
435                         break;
436
437                 }
438                 if (n > 0)
439                         break;
440         }
441         dm_lib_release();
442         dm_lib_exit();
443
444         return 0;
445 }
446
447 void *
448 xmalloc (size_t size) {
449         void *t;
450
451         if (size == 0)
452                 return NULL;
453
454         t = malloc (size);
455
456         if (t == NULL) {
457                 fprintf(stderr, "Out of memory\n");
458                 exit(1);
459         }
460
461         return t;
462 }
463
464 /*
465  * sseek: seek to specified sector
466  */
467 #if !defined (__alpha__) && !defined (__ia64__) && !defined (__x86_64__) \
468         && !defined (__s390x__)
469 #include <linux/unistd.h>       /* _syscall */
470 static
471 _syscall5(int,  _llseek,  uint,  fd, ulong, hi, ulong, lo,
472           long long *, res, uint, wh);
473 #endif
474
475 static int
476 sseek(int fd, unsigned int secnr) {
477         long long in, out;
478         in = ((long long) secnr << 9);
479         out = 1;
480
481 #if !defined (__alpha__) && !defined (__ia64__) && !defined (__x86_64__) \
482         && !defined (__s390x__)
483         if (_llseek (fd, in>>32, in & 0xffffffff, &out, SEEK_SET) != 0
484             || out != in)
485 #else
486         if ((out = lseek(fd, in, SEEK_SET)) != in)
487 #endif
488         {
489                 fprintf(stderr, "llseek error\n");
490                 return -1;
491         }
492         return 0;
493 }
494
495 static
496 struct block {
497         unsigned int secnr;
498         char *block;
499         struct block *next;
500 } *blockhead;
501
502 char *
503 getblock (int fd, unsigned int secnr) {
504         struct block *bp;
505
506         for (bp = blockhead; bp; bp = bp->next)
507
508                 if (bp->secnr == secnr)
509                         return bp->block;
510
511         if (sseek(fd, secnr))
512                 return NULL;
513
514         bp = xmalloc(sizeof(struct block));
515         bp->secnr = secnr;
516         bp->next = blockhead;
517         blockhead = bp;
518         bp->block = (char *) xmalloc(READ_SIZE);
519         
520         if (read(fd, bp->block, READ_SIZE) != READ_SIZE) {
521                 fprintf(stderr, "read error, sector %d\n", secnr);
522                 bp->block = NULL;
523         }
524
525         return bp->block;
526 }