~ubuntu-branches/ubuntu/trusty/util-linux/trusty-proposed

« back to all changes in this revision

Viewing changes to libblkid/src/devno.c

  • Committer: Package Import Robot
  • Author(s): LaMont Jones
  • Date: 2011-11-03 15:38:23 UTC
  • mto: (4.5.5 sid) (1.6.4)
  • mto: This revision was merged to the branch mainline in revision 85.
  • Revision ID: package-import@ubuntu.com-20111103153823-10sx16jprzxlhkqf
ImportĀ upstreamĀ versionĀ 2.20.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * devno.c - find a particular device by its device number (major/minor)
 
3
 *
 
4
 * Copyright (C) 2000, 2001, 2003 Theodore Ts'o
 
5
 * Copyright (C) 2001 Andreas Dilger
 
6
 *
 
7
 * %Begin-Header%
 
8
 * This file may be redistributed under the terms of the
 
9
 * GNU Lesser General Public License.
 
10
 * %End-Header%
 
11
 */
 
12
 
 
13
#include <stdio.h>
 
14
#include <string.h>
 
15
#if HAVE_UNISTD_H
 
16
#include <unistd.h>
 
17
#endif
 
18
#include <stdlib.h>
 
19
#if HAVE_SYS_TYPES_H
 
20
#include <sys/types.h>
 
21
#endif
 
22
#if HAVE_SYS_STAT_H
 
23
#include <sys/stat.h>
 
24
#endif
 
25
#include <dirent.h>
 
26
#if HAVE_ERRNO_H
 
27
#include <errno.h>
 
28
#endif
 
29
#if HAVE_SYS_MKDEV_H
 
30
#include <sys/mkdev.h>
 
31
#endif
 
32
#include <fcntl.h>
 
33
#include <inttypes.h>
 
34
 
 
35
#include "blkidP.h"
 
36
#include "pathnames.h"
 
37
#include "at.h"
 
38
#include "sysfs.h"
 
39
 
 
40
char *blkid_strndup(const char *s, int length)
 
41
{
 
42
        char *ret;
 
43
 
 
44
        if (!s)
 
45
                return NULL;
 
46
 
 
47
        if (!length)
 
48
                length = strlen(s);
 
49
 
 
50
        ret = malloc(length + 1);
 
51
        if (ret) {
 
52
                strncpy(ret, s, length);
 
53
                ret[length] = '\0';
 
54
        }
 
55
        return ret;
 
56
}
 
57
 
 
58
char *blkid_strdup(const char *s)
 
59
{
 
60
        return blkid_strndup(s, 0);
 
61
}
 
62
 
 
63
char *blkid_strconcat(const char *a, const char *b, const char *c)
 
64
{
 
65
        char *res, *p;
 
66
        size_t len, al, bl, cl;
 
67
 
 
68
        al = a ? strlen(a) : 0;
 
69
        bl = b ? strlen(b) : 0;
 
70
        cl = c ? strlen(c) : 0;
 
71
 
 
72
        len = al + bl + cl;
 
73
        if (!len)
 
74
                return NULL;
 
75
        p = res = malloc(len + 1);
 
76
        if (!res)
 
77
                return NULL;
 
78
        if (al) {
 
79
                memcpy(p, a, al);
 
80
                p += al;
 
81
        }
 
82
        if (bl) {
 
83
                memcpy(p, b, bl);
 
84
                p += bl;
 
85
        }
 
86
        if (cl) {
 
87
                memcpy(p, c, cl);
 
88
                p += cl;
 
89
        }
 
90
        *p = '\0';
 
91
        return res;
 
92
}
 
93
 
 
94
/*
 
95
 * This function adds an entry to the directory list
 
96
 */
 
97
static void add_to_dirlist(const char *dir, const char *subdir,
 
98
                                struct dir_list **list)
 
99
{
 
100
        struct dir_list *dp;
 
101
 
 
102
        dp = malloc(sizeof(struct dir_list));
 
103
        if (!dp)
 
104
                return;
 
105
        dp->name = subdir ? blkid_strconcat(dir, "/", subdir) :
 
106
                            blkid_strdup(dir);
 
107
        if (!dp->name) {
 
108
                free(dp);
 
109
                return;
 
110
        }
 
111
        dp->next = *list;
 
112
        *list = dp;
 
113
}
 
114
 
 
115
/*
 
116
 * This function frees a directory list
 
117
 */
 
118
static void free_dirlist(struct dir_list **list)
 
119
{
 
120
        struct dir_list *dp, *next;
 
121
 
 
122
        for (dp = *list; dp; dp = next) {
 
123
                next = dp->next;
 
124
                free(dp->name);
 
125
                free(dp);
 
126
        }
 
127
        *list = NULL;
 
128
}
 
129
 
 
130
void blkid__scan_dir(char *dirname, dev_t devno, struct dir_list **list,
 
131
                     char **devname)
 
132
{
 
133
        DIR     *dir;
 
134
        struct dirent *dp;
 
135
        struct stat st;
 
136
 
 
137
        if ((dir = opendir(dirname)) == NULL)
 
138
                return;
 
139
 
 
140
        while ((dp = readdir(dir)) != 0) {
 
141
#ifdef _DIRENT_HAVE_D_TYPE
 
142
                if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_BLK &&
 
143
                    dp->d_type != DT_LNK && dp->d_type != DT_DIR)
 
144
                        continue;
 
145
#endif
 
146
                if (dp->d_name[0] == '.' &&
 
147
                    ((dp->d_name[1] == 0) ||
 
148
                     ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
 
149
                        continue;
 
150
 
 
151
                if (fstat_at(dirfd(dir), dirname, dp->d_name, &st, 0))
 
152
                        continue;
 
153
 
 
154
                if (S_ISBLK(st.st_mode) && st.st_rdev == devno) {
 
155
                        *devname = blkid_strconcat(dirname, "/", dp->d_name);
 
156
                        DBG(DEBUG_DEVNO,
 
157
                            printf("found 0x%llx at %s\n", (long long)devno,
 
158
                                   *devname));
 
159
                        break;
 
160
                }
 
161
 
 
162
                if (!list || !S_ISDIR(st.st_mode))
 
163
                        continue;
 
164
 
 
165
                /* add subdirectory (but not symlink) to the list */
 
166
#ifdef _DIRENT_HAVE_D_TYPE
 
167
                if (dp->d_type == DT_LNK)
 
168
                        continue;
 
169
                if (dp->d_type == DT_UNKNOWN)
 
170
#endif
 
171
                {
 
172
                        if (fstat_at(dirfd(dir), dirname, dp->d_name, &st, 1) ||
 
173
                            !S_ISDIR(st.st_mode))
 
174
                                continue;       /* symlink or lstat() failed */
 
175
                }
 
176
 
 
177
                if (*dp->d_name == '.' || (
 
178
#ifdef _DIRENT_HAVE_D_TYPE
 
179
                    dp->d_type == DT_DIR &&
 
180
#endif
 
181
                    strcmp(dp->d_name, "shm") == 0))
 
182
                        /* ignore /dev/.{udev,mount,mdadm} and /dev/shm */
 
183
                        continue;
 
184
 
 
185
                add_to_dirlist(dirname, dp->d_name, list);
 
186
        }
 
187
        closedir(dir);
 
188
        return;
 
189
}
 
190
 
 
191
/* Directories where we will try to search for device numbers */
 
192
static const char *devdirs[] = { "/devices", "/devfs", "/dev", NULL };
 
193
 
 
194
/**
 
195
 * SECTION: misc
 
196
 * @title: Miscellaneous utils
 
197
 * @short_description: mix of various utils for low-level and high-level API
 
198
 */
 
199
 
 
200
/* returns basename and keeps dirname in the @path */
 
201
static char *stripoff_last_component(char *path)
 
202
{
 
203
        char *p = strrchr(path, '/');
 
204
 
 
205
        if (!p)
 
206
                return NULL;
 
207
        *p = '\0';
 
208
        return ++p;
 
209
}
 
210
 
 
211
static char *scandev_devno_to_devpath(dev_t devno)
 
212
{
 
213
        struct dir_list *list = NULL, *new_list = NULL;
 
214
        char *devname = NULL;
 
215
        const char **dir;
 
216
 
 
217
        /*
 
218
         * Add the starting directories to search in reverse order of
 
219
         * importance, since we are using a stack...
 
220
         */
 
221
        for (dir = devdirs; *dir; dir++)
 
222
                add_to_dirlist(*dir, NULL, &list);
 
223
 
 
224
        while (list) {
 
225
                struct dir_list *current = list;
 
226
 
 
227
                list = list->next;
 
228
                DBG(DEBUG_DEVNO, printf("directory %s\n", current->name));
 
229
                blkid__scan_dir(current->name, devno, &new_list, &devname);
 
230
                free(current->name);
 
231
                free(current);
 
232
                if (devname)
 
233
                        break;
 
234
                /*
 
235
                 * If we're done checking at this level, descend to
 
236
                 * the next level of subdirectories. (breadth-first)
 
237
                 */
 
238
                if (list == NULL) {
 
239
                        list = new_list;
 
240
                        new_list = NULL;
 
241
                }
 
242
        }
 
243
        free_dirlist(&list);
 
244
        free_dirlist(&new_list);
 
245
 
 
246
        return devname;
 
247
}
 
248
 
 
249
/**
 
250
 * blkid_devno_to_devname:
 
251
 * @devno: device number
 
252
 *
 
253
 * This function finds the pathname to a block device with a given
 
254
 * device number.
 
255
 *
 
256
 * Returns: a pointer to allocated memory to the pathname on success,
 
257
 * and NULL on failure.
 
258
 */
 
259
char *blkid_devno_to_devname(dev_t devno)
 
260
{
 
261
        char *path = NULL;
 
262
        char buf[PATH_MAX];
 
263
 
 
264
        path = sysfs_devno_to_devpath(devno, buf, sizeof(buf));
 
265
        if (path)
 
266
                path = strdup(path);
 
267
        if (!path)
 
268
                path = scandev_devno_to_devpath(devno);
 
269
 
 
270
        if (!path) {
 
271
                DBG(DEBUG_DEVNO,
 
272
                    printf("blkid: couldn't find devno 0x%04lx\n",
 
273
                           (unsigned long) devno));
 
274
        } else {
 
275
                DBG(DEBUG_DEVNO,
 
276
                    printf("found devno 0x%04llx as %s\n", (long long)devno, path));
 
277
        }
 
278
 
 
279
        return path;
 
280
}
 
281
 
 
282
static int get_dm_wholedisk(struct sysfs_cxt *cxt, char *diskname,
 
283
                            size_t len, dev_t *diskdevno)
 
284
{
 
285
        int rc = 0;
 
286
        char *name;
 
287
 
 
288
        /* Note, sysfs_get_slave() returns the first slave only,
 
289
         * if there is more slaves, then return NULL
 
290
         */
 
291
        name = sysfs_get_slave(cxt);
 
292
        if (!name)
 
293
                return -1;
 
294
 
 
295
        if (diskname && len) {
 
296
                strncpy(diskname, name, len);
 
297
                diskname[len - 1] = '\0';
 
298
        }
 
299
 
 
300
        if (diskdevno) {
 
301
                *diskdevno = sysfs_devname_to_devno(name, NULL);
 
302
                if (!*diskdevno)
 
303
                        rc = -1;
 
304
        }
 
305
 
 
306
        free(name);
 
307
        return rc;
 
308
}
 
309
 
 
310
/**
 
311
 * blkid_devno_to_wholedisk:
 
312
 * @dev: device number
 
313
 * @diskname: buffer to return diskname (or NULL)
 
314
 * @len: diskname buffer size (or 0)
 
315
 * @diskdevno: pointer to returns devno of entire disk (or NULL)
 
316
 *
 
317
 * This function uses sysfs to convert the @devno device number to the *name*
 
318
 * of the whole disk. The function DOES NOT return full device name. The @dev
 
319
 * argument could be partition or whole disk -- both is converted.
 
320
 *
 
321
 * For example: sda1, 0x0801 --> sda, 0x0800
 
322
 *
 
323
 * For conversion to the full disk *path* use blkid_devno_to_devname(), for
 
324
 * example:
 
325
 *
 
326
 * <informalexample>
 
327
 *  <programlisting>
 
328
 *
 
329
 *      dev_t dev = 0x0801, disk;               // sda1 = 8:1
 
330
 *      char *diskpath, diskname[32];
 
331
 *
 
332
 *      blkid_devno_to_wholedisk(dev, diskname, sizeof(diskname), &disk);
 
333
 *      diskpath = blkid_devno_to_devname(disk);
 
334
 *
 
335
 *      // print "0x0801: sda, /dev/sda, 8:0
 
336
 *      printf("0x%x: %s, %s, %d:%d\n",
 
337
 *              dev, diskname, diskpath, major(disk), minor(disk));
 
338
 *
 
339
 *      free(diskpath);
 
340
 *
 
341
 *  </programlisting>
 
342
 * </informalexample>
 
343
 *
 
344
 * Returns: 0 on success or -1 in case of error.
 
345
 */
 
346
int blkid_devno_to_wholedisk(dev_t dev, char *diskname,
 
347
                        size_t len, dev_t *diskdevno)
 
348
{
 
349
        struct sysfs_cxt cxt;
 
350
        int is_part = 0;
 
351
 
 
352
        if (!dev || sysfs_init(&cxt, dev, NULL) != 0)
 
353
                return -1;
 
354
 
 
355
        is_part = sysfs_has_attribute(&cxt, "partition");
 
356
        if (!is_part) {
 
357
                /*
 
358
                 * Extra case for partitions mapped by device-mapper.
 
359
                 *
 
360
                 * All regualar partitions (added by BLKPG ioctl or kernel PT
 
361
                 * parser) have the /sys/.../partition file. The partitions
 
362
                 * mapped by DM don't have such file, but they have "part"
 
363
                 * prefix in DM UUID.
 
364
                 */
 
365
                char *uuid = sysfs_strdup(&cxt, "dm/uuid");
 
366
                char *tmp = uuid;
 
367
                char *prefix = uuid ? strsep(&tmp, "-") : NULL;
 
368
 
 
369
                if (prefix && strncasecmp(prefix, "part", 4) == 0)
 
370
                        is_part = 1;
 
371
                free(uuid);
 
372
 
 
373
                if (is_part &&
 
374
                    get_dm_wholedisk(&cxt, diskname, len, diskdevno) == 0)
 
375
                        /*
 
376
                         * partitioned device, mapped by DM
 
377
                         */
 
378
                        goto done;
 
379
 
 
380
                is_part = 0;
 
381
        }
 
382
 
 
383
        if (!is_part) {
 
384
                /*
 
385
                 * unpartitioned device
 
386
                 */
 
387
                if (diskname && len) {
 
388
                        if (!sysfs_get_devname(&cxt, diskname, len))
 
389
                                goto err;
 
390
                }
 
391
                if (diskdevno)
 
392
                        *diskdevno = dev;
 
393
 
 
394
        } else {
 
395
                /*
 
396
                 * partitioned device
 
397
                 *      - readlink /sys/dev/block/8:1   = ../../block/sda/sda1
 
398
                 *      - dirname  ../../block/sda/sda1 = ../../block/sda
 
399
                 *      - basename ../../block/sda      = sda
 
400
                 */
 
401
                char linkpath[PATH_MAX];
 
402
                char *name;
 
403
                int linklen;
 
404
 
 
405
                linklen = sysfs_readlink(&cxt, NULL,
 
406
                                linkpath, sizeof(linkpath) - 1);
 
407
                if (linklen < 0)
 
408
                        goto err;
 
409
                linkpath[linklen] = '\0';
 
410
 
 
411
                stripoff_last_component(linkpath);              /* dirname */
 
412
                name = stripoff_last_component(linkpath);       /* basename */
 
413
                if (!name)
 
414
                        goto err;
 
415
 
 
416
                if (diskname && len) {
 
417
                        strncpy(diskname, name, len);
 
418
                        diskname[len - 1] = '\0';
 
419
                }
 
420
 
 
421
                if (diskdevno) {
 
422
                        *diskdevno = sysfs_devname_to_devno(name, NULL);
 
423
                        if (!*diskdevno)
 
424
                                goto err;
 
425
                }
 
426
        }
 
427
 
 
428
done:
 
429
        sysfs_deinit(&cxt);
 
430
 
 
431
        DBG(DEBUG_DEVNO,
 
432
            printf("found entire diskname for devno 0x%04llx %s\n",
 
433
            (long long) dev, diskname ? diskname : ""));
 
434
        return 0;
 
435
err:
 
436
        sysfs_deinit(&cxt);
 
437
 
 
438
        DBG(DEBUG_DEVNO,
 
439
            printf("failed to convert 0x%04llx to wholedisk name, errno=%d\n",
 
440
            (long long) dev, errno));
 
441
        return -1;
 
442
}
 
443
 
 
444
/*
 
445
 * Returns 1 if the @major number is associated with @drvname.
 
446
 */
 
447
int blkid_driver_has_major(const char *drvname, int major)
 
448
{
 
449
        FILE *f;
 
450
        char buf[128];
 
451
        int match = 0;
 
452
 
 
453
        f = fopen(_PATH_PROC_DEVICES, "r");
 
454
        if (!f)
 
455
                return 0;
 
456
 
 
457
        while (fgets(buf, sizeof(buf), f)) {    /* skip to block dev section */
 
458
                if (strncmp("Block devices:\n", buf, sizeof(buf)) == 0)
 
459
                        break;
 
460
        }
 
461
 
 
462
        while (fgets(buf, sizeof(buf), f)) {
 
463
                int maj;
 
464
                char name[64];
 
465
 
 
466
                if (sscanf(buf, "%d %64[^\n ]", &maj, name) != 2)
 
467
                        continue;
 
468
 
 
469
                if (maj == major && strcmp(name, drvname) == 0) {
 
470
                        match = 1;
 
471
                        break;
 
472
                }
 
473
        }
 
474
 
 
475
        fclose(f);
 
476
 
 
477
        DBG(DEBUG_DEVNO, printf("major %d %s associated with '%s' driver\n",
 
478
                        major, match ? "is" : "is NOT", drvname));
 
479
        return match;
 
480
}
 
481
 
 
482
 
 
483
#ifdef TEST_PROGRAM
 
484
int main(int argc, char** argv)
 
485
{
 
486
        char    *devname, *tmp;
 
487
        char    diskname[PATH_MAX];
 
488
        int     major, minor;
 
489
        dev_t   devno, disk_devno;
 
490
        const char *errmsg = "Couldn't parse %s: %s\n";
 
491
 
 
492
        blkid_init_debug(DEBUG_ALL);
 
493
        if ((argc != 2) && (argc != 3)) {
 
494
                fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n"
 
495
                        "Resolve a device number to a device name\n",
 
496
                        argv[0], argv[0]);
 
497
                exit(1);
 
498
        }
 
499
        if (argc == 2) {
 
500
                devno = strtoul(argv[1], &tmp, 0);
 
501
                if (*tmp) {
 
502
                        fprintf(stderr, errmsg, "device number", argv[1]);
 
503
                        exit(1);
 
504
                }
 
505
        } else {
 
506
                major = strtoul(argv[1], &tmp, 0);
 
507
                if (*tmp) {
 
508
                        fprintf(stderr, errmsg, "major number", argv[1]);
 
509
                        exit(1);
 
510
                }
 
511
                minor = strtoul(argv[2], &tmp, 0);
 
512
                if (*tmp) {
 
513
                        fprintf(stderr, errmsg, "minor number", argv[2]);
 
514
                        exit(1);
 
515
                }
 
516
                devno = makedev(major, minor);
 
517
        }
 
518
        printf("Looking for device 0x%04llx\n", (long long)devno);
 
519
        devname = blkid_devno_to_devname(devno);
 
520
        free(devname);
 
521
 
 
522
        printf("Looking for whole-device for 0x%04llx\n", (long long)devno);
 
523
        blkid_devno_to_wholedisk(devno, diskname, sizeof(diskname), &disk_devno);
 
524
 
 
525
        return 0;
 
526
}
 
527
#endif