]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - lib/blkid/devname.c
devname.c: Add support for EVMS to the blkid library.
[thirdparty/e2fsprogs.git] / lib / blkid / devname.c
1 /*
2 * devname.c - get a dev by its device inode name
3 *
4 * Copyright (C) Andries Brouwer
5 * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o
6 * Copyright (C) 2001 Andreas Dilger
7 *
8 * %Begin-Header%
9 * This file may be redistributed under the terms of the
10 * GNU Lesser General Public License.
11 * %End-Header%
12 */
13
14 #include <stdio.h>
15 #include <string.h>
16 #if HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
19 #include <stdlib.h>
20 #include <string.h>
21 #include <ctype.h>
22 #if HAVE_SYS_TYPES_H
23 #include <sys/types.h>
24 #endif
25 #if HAVE_SYS_STAT_H
26 #include <sys/stat.h>
27 #endif
28 #if HAVE_ERRNO_H
29 #include <errno.h>
30 #endif
31 #if HAVE_SYS_MKDEV_H
32 #include <sys/mkdev.h>
33 #endif
34 #include <time.h>
35
36 #include "blkid/blkid.h"
37
38 /* #define DEBUG_DEVNAME */
39 #ifdef DEBUG_DEVNAME
40 #define DBG(x) x
41 #else
42 #define DBG(x)
43 #endif
44
45 /*
46 * Find a dev struct in the cache by device name, if available.
47 */
48 blkid_dev *blkid_find_devname(blkid_cache *cache, const char *devname)
49 {
50 blkid_dev *dev = NULL;
51 struct list_head *p;
52
53 if (!cache || !devname)
54 return NULL;
55
56 list_for_each(p, &cache->bic_devs) {
57 blkid_dev *tmp = list_entry(p, blkid_dev, bid_devs);
58
59 if (strcmp(tmp->bid_name, devname))
60 continue;
61
62 DBG(printf("found devname %s in cache\n", tmp->bid_name));
63 dev = blkid_verify_devname(cache, tmp);
64 break;
65 }
66
67 return dev;
68 }
69
70 /*
71 * Return a pointer to an dev struct, either from cache or by probing.
72 */
73 blkid_dev *blkid_get_devname(blkid_cache *cache, const char *devname)
74 {
75 blkid_dev *dev;
76
77 if ((dev = blkid_find_devname(cache, devname)))
78 return dev;
79
80 dev = blkid_devname_to_dev(devname, 0);
81 return blkid_add_dev_to_cache(cache, dev);
82 }
83
84 /*
85 * Probe a single block device to add to the device cache.
86 * If the size is not specified, it will be found in blkid_devname_to_dev().
87 */
88 static blkid_dev *probe_one(blkid_cache *cache, const char *ptname,
89 int major, int minor, unsigned long long size)
90 {
91 dev_t devno = makedev(major, minor);
92 blkid_dev *dev;
93 const char **dir;
94 char *devname = NULL;
95
96 /* See if we already have this device number in the cache. */
97 if ((dev = blkid_find_devno(cache, devno)))
98 return dev;
99
100 /*
101 * Take a quick look at /dev/ptname for the device number. We check
102 * all of the likely device directories. If we don't find it, or if
103 * the stat information doesn't check out, use blkid_devno_to_devname()
104 * to find it via an exhaustive search for the device major/minor.
105 */
106 for (dir = devdirs; *dir; dir++) {
107 struct stat st;
108 char device[256];
109
110 sprintf(device, "%s/%s", *dir, ptname);
111 if ((dev = blkid_find_devname(cache, device)) &&
112 dev->bid_devno == devno)
113 return dev;
114
115 if (stat(device, &st) == 0 && st.st_rdev == devno) {
116 devname = string_copy(device);
117 break;
118 }
119 }
120 if (!devname) {
121 devname = blkid_devno_to_devname(devno);
122 if (!devname)
123 return NULL;
124 }
125 dev = blkid_devname_to_dev(devname, size);
126 string_free(devname);
127
128 return blkid_add_dev_to_cache(cache, dev);
129 }
130
131 #define PROC_PARTITIONS "/proc/partitions"
132 #define VG_DIR "/proc/lvm/VGs"
133
134 /*
135 * This function initializes the UUID cache with devices from the LVM
136 * proc hierarchy. We currently depend on the names of the LVM
137 * hierarchy giving us the device structure in /dev. (XXX is this a
138 * safe thing to do?)
139 */
140 #ifdef VG_DIR
141 #include <dirent.h>
142 static int lvm_get_devno(const char *lvm_device, int *major, int *minor,
143 blkid_loff_t *size)
144 {
145 FILE *lvf;
146 char buf[1024];
147 int ret = 1;
148
149 *major = *minor = 0;
150 *size = 0;
151
152 DBG(printf("opening %s\n", lvm_device));
153 if ((lvf = fopen(lvm_device, "r")) == NULL) {
154 ret = errno;
155 DBG(printf("%s: (%d) %s\n", lvm_device, ret, strerror(ret)));
156 return -ret;
157 }
158
159 while (fgets(buf, sizeof(buf), lvf)) {
160 if (sscanf(buf, "size: %llu", size) == 1) { /* sectors */
161 *size <<= 9;
162 }
163 if (sscanf(buf, "device: %d:%d", major, minor) == 2) {
164 ret = 0;
165 break;
166 }
167 }
168 fclose(lvf);
169
170 return ret;
171 }
172
173 static void lvm_probe_all(blkid_cache **cache)
174 {
175 DIR *vg_list;
176 struct dirent *vg_iter;
177 int vg_len = strlen(VG_DIR);
178
179 if ((vg_list = opendir(VG_DIR)) == NULL)
180 return;
181
182 DBG(printf("probing LVM devices under %s\n", VG_DIR));
183
184 while ((vg_iter = readdir(vg_list)) != NULL) {
185 DIR *lv_list;
186 char *vdirname;
187 char *vg_name;
188 struct dirent *lv_iter;
189
190 vg_name = vg_iter->d_name;
191 if (!strcmp(vg_name, ".") || !strcmp(vg_name, ".."))
192 continue;
193 vdirname = malloc(vg_len + strlen(vg_name) + 8);
194 if (!vdirname)
195 goto exit;
196 sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name);
197
198 lv_list = opendir(vdirname);
199 free(vdirname);
200 if (lv_list == NULL)
201 continue;
202
203 while ((lv_iter = readdir(lv_list)) != NULL) {
204 char *lv_name, *lvm_device;
205 int major, minor;
206 blkid_loff_t size;
207
208 lv_name = lv_iter->d_name;
209 if (!strcmp(lv_name, ".") || !strcmp(lv_name, ".."))
210 continue;
211
212 lvm_device = malloc(vg_len + strlen(vg_name) +
213 strlen(lv_name) + 8);
214 if (!lvm_device) {
215 closedir(lv_list);
216 goto exit;
217 }
218 sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
219 lv_name);
220 if (lvm_get_devno(lvm_device, &major, &minor, &size)) {
221 free(lvm_device);
222 continue;
223 }
224 sprintf(lvm_device, "%s/%s", vg_name, lv_name);
225 DBG(printf("LVM dev %s: devno 0x%02X%02X, size %Ld\n",
226 lvm_device, major, minor, size));
227 probe_one(*cache, lvm_device, major, minor, size);
228 free(lvm_device);
229 }
230 closedir(lv_list);
231 }
232 exit:
233 closedir(vg_list);
234 }
235 #endif
236
237 #define PROC_EVMS_VOLUMES "/proc/evms/volumes"
238
239 static int
240 evms_probe_all(blkid_cache **cache)
241 {
242 char line[100];
243 int ma, mi, sz, num = 0;
244 FILE *procpt;
245 char device[110];
246
247 procpt = fopen(PROC_EVMS_VOLUMES, "r");
248 if (!procpt)
249 return 0;
250 while (fgets(line, sizeof(line), procpt)) {
251 if (sscanf (line, " %d %d %d %*s %*s %[^\n ]",
252 &ma, &mi, &sz, device) != 4)
253 continue;
254
255 DBG(printf("Checking partition %s (%d, %d)\n",
256 device, ma, mi));
257
258 probe_one(*cache, device, ma, mi, sz << 10);
259 num++;
260 }
261 fclose(procpt);
262 return num;
263 }
264
265 /*
266 * Read the device data for all available block devices in the system.
267 */
268 int blkid_probe_all(blkid_cache **cache)
269 {
270 FILE *proc;
271 int firstPass;
272
273 if (!cache)
274 return -BLKID_ERR_PARAM;
275
276 if (!*cache)
277 *cache = blkid_new_cache();
278
279 if (!*cache)
280 return -BLKID_ERR_MEM;
281
282 if ((*cache)->bic_flags & BLKID_BIC_FL_PROBED &&
283 time(0) - (*cache)->bic_time < BLKID_PROBE_INTERVAL)
284 return 0;
285
286 if (evms_probe_all(cache))
287 goto finish;
288
289 #ifdef VG_DIR
290 lvm_probe_all(cache);
291 #endif
292
293 proc = fopen(PROC_PARTITIONS, "r");
294 if (!proc)
295 return -BLKID_ERR_PROC;
296
297 for (firstPass = 1; firstPass >= 0; firstPass--) {
298 char line[1024];
299 char ptname0[128], ptname1[128];
300 char *ptnames[2] = { ptname0, ptname1 };
301 int majors[2], minors[2];
302 unsigned long long sizes[2];
303 int lens[2] = { 0, 0 };
304 int handleOnFirst;
305 int which = 0, last = 0;
306
307 fseek(proc, 0, SEEK_SET);
308
309 while (fgets(line, sizeof(line), proc)) {
310 last = which;
311 which ^= 1;
312
313 if (sscanf(line, " %d %d %lld %128[^\n ]",
314 &majors[which], &minors[which],
315 &sizes[which], ptnames[which]) != 4)
316 continue;
317
318 DBG(printf("read partition name %s\n", ptnames[which]));
319
320 /* look only at md devices on first pass */
321 handleOnFirst = !strncmp(ptnames[which], "md", 2);
322 if (firstPass != handleOnFirst)
323 continue;
324
325 /* Skip whole disk devs unless they have no partitions
326 * If we don't have a partition on this dev, also
327 * check previous dev to see if it didn't have a partn.
328 * heuristic: partition name ends in a digit.
329 *
330 * Skip extended partitions.
331 * heuristic: size is 1
332 *
333 * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
334 */
335
336 lens[which] = strlen(ptnames[which]);
337 if (isdigit(ptnames[which][lens[which] - 1])) {
338 DBG(printf("partition dev %s, devno 0x%02X%02X\n",
339 ptnames[which], majors[which],
340 minors[which]));
341
342 if (sizes[which] > 1)
343 probe_one(*cache, ptnames[which],
344 majors[which], minors[which],
345 sizes[which] << 10);
346 lens[which] = 0;
347 lens[last] = 0;
348 } else if (lens[last] &&
349 strncmp(ptnames[last], ptnames[which],
350 lens[last])) {
351 DBG(printf("whole dev %s, devno 0x%02X%02X\n",
352 ptnames[last], majors[last],
353 minors[last]));
354 probe_one(*cache, ptnames[last], majors[last],
355 minors[last], sizes[last] << 10);
356 lens[last] = 0;
357 }
358 }
359
360 /* Handle the last device if it wasn't partitioned */
361 if (lens[which])
362 probe_one(*cache, ptnames[which], majors[which],
363 minors[which], sizes[which] << 10);
364 }
365 fclose(proc);
366
367 finish:
368 (*cache)->bic_time = time(0);
369 (*cache)->bic_flags |= BLKID_BIC_FL_PROBED;
370
371 return 0;
372 }
373
374 #ifdef TEST_PROGRAM
375 int main(int argc, char **argv)
376 {
377 blkid_cache *cache = NULL;
378
379 if (argc != 1) {
380 fprintf(stderr, "Usage: %s\n"
381 "Probe all devices and exit\n", argv[0]);
382 exit(1);
383 }
384 if (blkid_probe_all(&cache) < 0)
385 printf("%s: error probing devices\n", argv[0]);
386
387 blkid_free_cache(cache);
388 return (0);
389 }
390 #endif