/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 |