Coverage Report

Created: 2026-02-22 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libblkid/src/devno.c
Line
Count
Source
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
#ifdef HAVE_UNISTD_H
16
#include <unistd.h>
17
#endif
18
#include <stdlib.h>
19
#ifdef HAVE_SYS_TYPES_H
20
#include <sys/types.h>
21
#endif
22
#ifdef HAVE_SYS_STAT_H
23
#include <sys/stat.h>
24
#endif
25
#include <dirent.h>
26
#ifdef HAVE_ERRNO_H
27
#include <errno.h>
28
#endif
29
#ifdef 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 "sysfs.h"
38
#include "strutils.h"
39
40
static char *blkid_strconcat(const char *a, const char *b, const char *c)
41
0
{
42
0
  char *res, *p;
43
0
  size_t len, al, bl, cl;
44
45
0
  al = a ? strlen(a) : 0;
46
0
  bl = b ? strlen(b) : 0;
47
0
  cl = c ? strlen(c) : 0;
48
49
0
  len = al + bl + cl;
50
0
  if (!len)
51
0
    return NULL;
52
0
  p = res = malloc(len + 1);
53
0
  if (!res)
54
0
    return NULL;
55
0
  if (al)
56
0
    p = mempcpy(p, a, al);
57
0
  if (bl)
58
0
    p = mempcpy(p, b, bl);
59
0
  if (cl)
60
0
    p = mempcpy(p, c, cl);
61
0
  *p = '\0';
62
0
  return res;
63
0
}
64
65
/*
66
 * This function adds an entry to the directory list
67
 */
68
static void add_to_dirlist(const char *dir, const char *subdir,
69
        struct dir_list **list)
70
0
{
71
0
  struct dir_list *dp;
72
73
0
  dp = malloc(sizeof(struct dir_list));
74
0
  if (!dp)
75
0
    return;
76
0
  dp->name = subdir ? blkid_strconcat(dir, "/", subdir) :
77
0
       dir ? strdup(dir) : NULL;
78
79
0
  if (!dp->name) {
80
0
    free(dp);
81
0
    return;
82
0
  }
83
0
  dp->next = *list;
84
0
  *list = dp;
85
0
}
86
87
/*
88
 * This function frees a directory list
89
 */
90
static void free_dirlist(struct dir_list **list)
91
0
{
92
0
  struct dir_list *dp, *next;
93
94
0
  for (dp = *list; dp; dp = next) {
95
0
    next = dp->next;
96
0
    free(dp->name);
97
0
    free(dp);
98
0
  }
99
0
  *list = NULL;
100
0
}
101
102
void blkid__scan_dir(char *dirname, dev_t devno, struct dir_list **list,
103
         char **devname)
104
0
{
105
0
  DIR *dir;
106
0
  struct dirent *dp;
107
0
  struct stat st;
108
109
0
  if ((dir = opendir(dirname)) == NULL)
110
0
    return;
111
112
0
  while ((dp = readdir(dir)) != NULL) {
113
0
#ifdef _DIRENT_HAVE_D_TYPE
114
0
    if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_BLK &&
115
0
        dp->d_type != DT_LNK && dp->d_type != DT_DIR)
116
0
      continue;
117
0
#endif
118
0
    if (dp->d_name[0] == '.' &&
119
0
        ((dp->d_name[1] == 0) ||
120
0
         ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
121
0
      continue;
122
123
0
    if (fstatat(dirfd(dir), dp->d_name, &st, 0))
124
0
      continue;
125
126
0
    if (S_ISBLK(st.st_mode) && st.st_rdev == devno) {
127
0
      *devname = blkid_strconcat(dirname, "/", dp->d_name);
128
0
      DBG(DEVNO, ul_debug("found 0x%llx at %s", (long long)devno,
129
0
           *devname));
130
0
      break;
131
0
    }
132
133
0
    if (!list || !S_ISDIR(st.st_mode))
134
0
      continue;
135
136
    /* add subdirectory (but not symlink) to the list */
137
0
#ifdef _DIRENT_HAVE_D_TYPE
138
0
    if (dp->d_type == DT_LNK)
139
0
      continue;
140
0
    if (dp->d_type == DT_UNKNOWN)
141
0
#endif
142
0
    {
143
0
      if (fstatat(dirfd(dir), dp->d_name, &st, AT_SYMLINK_NOFOLLOW) ||
144
0
          !S_ISDIR(st.st_mode))
145
0
        continue; /* symlink or fstatat() failed */
146
0
    }
147
148
0
    if (*dp->d_name == '.' || (
149
0
#ifdef _DIRENT_HAVE_D_TYPE
150
0
        dp->d_type == DT_DIR &&
151
0
#endif
152
0
        strcmp(dp->d_name, "shm") == 0))
153
      /* ignore /dev/.{udev,mount,mdadm} and /dev/shm */
154
0
      continue;
155
156
0
    add_to_dirlist(dirname, dp->d_name, list);
157
0
  }
158
0
  closedir(dir);
159
0
}
160
161
/* Directories where we will try to search for device numbers */
162
static const char *const devdirs[] = { "/devices", "/devfs", "/dev", NULL };
163
164
/**
165
 * SECTION: misc
166
 * @title: Miscellaneous utils
167
 * @short_description: mix of various utils for low-level and high-level API
168
 */
169
170
171
172
static char *scandev_devno_to_devpath(dev_t devno)
173
0
{
174
0
  struct dir_list *list = NULL, *new_list = NULL;
175
0
  char *devname = NULL;
176
0
  const char *const*dir;
177
178
  /*
179
   * Add the starting directories to search in reverse order of
180
   * importance, since we are using a stack...
181
   */
182
0
  for (dir = devdirs; *dir; dir++)
183
0
    add_to_dirlist(*dir, NULL, &list);
184
185
0
  while (list) {
186
0
    struct dir_list *current = list;
187
188
0
    list = list->next;
189
0
    DBG(DEVNO, ul_debug("directory %s", current->name));
190
0
    blkid__scan_dir(current->name, devno, &new_list, &devname);
191
0
    free(current->name);
192
0
    free(current);
193
0
    if (devname)
194
0
      break;
195
    /*
196
     * If we're done checking at this level, descend to
197
     * the next level of subdirectories. (breadth-first)
198
     */
199
0
    if (list == NULL) {
200
0
      list = new_list;
201
0
      new_list = NULL;
202
0
    }
203
0
  }
204
0
  free_dirlist(&list);
205
0
  free_dirlist(&new_list);
206
207
0
  return devname;
208
0
}
209
210
/**
211
 * blkid_devno_to_devname:
212
 * @devno: device number
213
 *
214
 * This function finds the pathname to a block device with a given
215
 * device number.
216
 *
217
 * Returns: a pointer to allocated memory to the pathname on success,
218
 * and NULL on failure.
219
 */
220
char *blkid_devno_to_devname(dev_t devno)
221
0
{
222
0
  char *path;
223
0
  char buf[PATH_MAX];
224
225
0
  path = sysfs_devno_to_devpath(devno, buf, sizeof(buf));
226
0
  if (path)
227
0
    path = strdup(path);
228
0
  if (!path)
229
0
    path = scandev_devno_to_devpath(devno);
230
231
0
  if (!path) {
232
0
    DBG(DEVNO, ul_debug("blkid: couldn't find devno 0x%04lx",
233
0
         (unsigned long) devno));
234
0
  } else {
235
0
    DBG(DEVNO, ul_debug("found devno 0x%04llx as %s", (long long)devno, path));
236
0
  }
237
238
0
  return path;
239
0
}
240
241
242
/**
243
 * blkid_devno_to_wholedisk:
244
 * @dev: device number
245
 * @diskname: buffer to return diskname (or NULL)
246
 * @len: diskname buffer size (or 0)
247
 * @diskdevno: pointer to returns devno of entire disk (or NULL)
248
 *
249
 * This function uses sysfs to convert the @devno device number to the *name*
250
 * of the whole disk. The function DOES NOT return full device name. The @dev
251
 * argument could be partition or whole disk -- both is converted.
252
 *
253
 * For example: sda1, 0x0801 --> sda, 0x0800
254
 *
255
 * For conversion to the full disk *path* use blkid_devno_to_devname(), for
256
 * example:
257
 *
258
 * <informalexample>
259
 *  <programlisting>
260
 *
261
 *  dev_t dev = 0x0801, disk;   // sda1 = 8:1
262
 *  char *diskpath, diskname[32];
263
 *
264
 *  blkid_devno_to_wholedisk(dev, diskname, sizeof(diskname), &disk);
265
 *  diskpath = blkid_devno_to_devname(disk);
266
 *
267
 *  // print "0x0801: sda, /dev/sda, 8:0
268
 *  printf("0x%x: %s, %s, %d:%d\n",
269
 *    dev, diskname, diskpath, major(disk), minor(disk));
270
 *
271
 *  free(diskpath);
272
 *
273
 *  </programlisting>
274
 * </informalexample>
275
 *
276
 * Returns: 0 on success or -1 in case of error.
277
 */
278
int blkid_devno_to_wholedisk(dev_t dev, char *diskname,
279
      size_t len, dev_t *diskdevno)
280
0
{
281
0
  return sysfs_devno_to_wholedisk( dev, diskname, len, diskdevno);
282
0
}
283
284
/*
285
 * Returns 1 if the @major number is associated with @drvname.
286
 */
287
int blkid_driver_has_major(const char *drvname, int drvmaj)
288
0
{
289
0
  FILE *f;
290
0
  char buf[128];
291
0
  int match = 0;
292
293
0
  f = fopen(_PATH_PROC_DEVICES, "r" UL_CLOEXECSTR);
294
0
  if (!f)
295
0
    return 0;
296
297
0
  while (fgets(buf, sizeof(buf), f)) { /* skip to block dev section */
298
0
    if (strncmp("Block devices:\n", buf, sizeof(buf)) == 0)
299
0
      break;
300
0
  }
301
302
0
  while (fgets(buf, sizeof(buf), f)) {
303
0
    int maj;
304
0
    char name[64 + 1];
305
306
0
    if (sscanf(buf, "%d %64[^\n ]", &maj, name) != 2)
307
0
      continue;
308
309
0
    if (maj == drvmaj && strcmp(name, drvname) == 0) {
310
0
      match = 1;
311
0
      break;
312
0
    }
313
0
  }
314
315
0
  fclose(f);
316
317
  DBG(DEVNO, ul_debug("major %d %s associated with '%s' driver",
318
0
      drvmaj, match ? "is" : "is NOT", drvname));
319
0
  return match;
320
0
}
321
322
#ifdef TEST_PROGRAM
323
int main(int argc, char** argv)
324
{
325
  char  *devname, *tmp;
326
  char  diskname[PATH_MAX];
327
  int devmaj, devmin;
328
  dev_t devno, disk_devno;
329
  const char *errmsg = "Couldn't parse %s: %s\n";
330
331
  blkid_init_debug(BLKID_DEBUG_ALL);
332
  if ((argc != 2) && (argc != 3)) {
333
    fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n"
334
      "Resolve a device number to a device name\n",
335
      argv[0], argv[0]);
336
    exit(1);
337
  }
338
  if (argc == 2) {
339
    devno = strtoul(argv[1], &tmp, 0);
340
    if (*tmp) {
341
      fprintf(stderr, errmsg, "device number", argv[1]);
342
      exit(1);
343
    }
344
  } else {
345
    devmaj = strtoul(argv[1], &tmp, 0);
346
    if (*tmp) {
347
      fprintf(stderr, errmsg, "major number", argv[1]);
348
      exit(1);
349
    }
350
    devmin = strtoul(argv[2], &tmp, 0);
351
    if (*tmp) {
352
      fprintf(stderr, errmsg, "minor number", argv[2]);
353
      exit(1);
354
    }
355
    devno = makedev(devmaj, devmin);
356
  }
357
  printf("Looking for device 0x%04llx\n", (long long)devno);
358
  devname = blkid_devno_to_devname(devno);
359
  free(devname);
360
361
  printf("Looking for whole-device for 0x%04llx\n", (long long)devno);
362
  if (blkid_devno_to_wholedisk(devno, diskname,
363
        sizeof(diskname), &disk_devno) == 0)
364
    printf("found devno 0x%04llx as /dev/%s\n", (long long) disk_devno, diskname);
365
366
  return 0;
367
}
368
#endif