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