avoid signed<->unsigned warning
[oweals/busybox.git] / e2fsprogs / 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 "blkidP.h"
37
38 /*
39  * Find a dev struct in the cache by device name, if available.
40  *
41  * If there is no entry with the specified device name, and the create
42  * flag is set, then create an empty device entry.
43  */
44 blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
45 {
46         blkid_dev dev = NULL, tmp;
47         struct list_head *p;
48
49         if (!cache || !devname)
50                 return NULL;
51
52         list_for_each(p, &cache->bic_devs) {
53                 tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
54                 if (strcmp(tmp->bid_name, devname))
55                         continue;
56
57                 DBG(DEBUG_DEVNAME,
58                     printf("found devname %s in cache\n", tmp->bid_name));
59                 dev = tmp;
60                 break;
61         }
62
63         if (!dev && (flags & BLKID_DEV_CREATE)) {
64                 dev = blkid_new_dev();
65                 if (!dev)
66                         return NULL;
67                 dev->bid_name = blkid_strdup(devname);
68                 dev->bid_cache = cache;
69                 list_add_tail(&dev->bid_devs, &cache->bic_devs);
70                 cache->bic_flags |= BLKID_BIC_FL_CHANGED;
71         }
72
73         if (flags & BLKID_DEV_VERIFY)
74                 dev = blkid_verify(cache, dev);
75         return dev;
76 }
77
78 /*
79  * Probe a single block device to add to the device cache.
80  */
81 static void probe_one(blkid_cache cache, const char *ptname,
82                       dev_t devno, int pri)
83 {
84         blkid_dev dev = NULL;
85         struct list_head *p;
86         const char **dir;
87         char *devname = NULL;
88
89         /* See if we already have this device number in the cache. */
90         list_for_each(p, &cache->bic_devs) {
91                 blkid_dev tmp = list_entry(p, struct blkid_struct_dev,
92                                            bid_devs);
93                 if (tmp->bid_devno == devno) {
94                         dev = blkid_verify(cache, tmp);
95                         break;
96                 }
97         }
98         if (dev && dev->bid_devno == devno)
99                 goto set_pri;
100
101         /*
102          * Take a quick look at /dev/ptname for the device number.  We check
103          * all of the likely device directories.  If we don't find it, or if
104          * the stat information doesn't check out, use blkid_devno_to_devname()
105          * to find it via an exhaustive search for the device major/minor.
106          */
107         for (dir = blkid_devdirs; *dir; dir++) {
108                 struct stat st;
109                 char device[256];
110
111                 sprintf(device, "%s/%s", *dir, ptname);
112                 if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) &&
113                     dev->bid_devno == devno)
114                         goto set_pri;
115
116                 if (stat(device, &st) == 0 && S_ISBLK(st.st_mode) &&
117                     st.st_rdev == devno) {
118                         devname = blkid_strdup(device);
119                         break;
120                 }
121         }
122         if (!devname) {
123                 devname = blkid_devno_to_devname(devno);
124                 if (!devname)
125                         return;
126         }
127         dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL);
128         free(devname);
129
130 set_pri:
131         if (!pri && !strncmp(ptname, "md", 2))
132                 pri = BLKID_PRI_MD;
133         if (dev)
134                 dev->bid_pri = pri;
135         return;
136 }
137
138 #define PROC_PARTITIONS "/proc/partitions"
139 #define VG_DIR          "/proc/lvm/VGs"
140
141 /*
142  * This function initializes the UUID cache with devices from the LVM
143  * proc hierarchy.  We currently depend on the names of the LVM
144  * hierarchy giving us the device structure in /dev.  (XXX is this a
145  * safe thing to do?)
146  */
147 #ifdef VG_DIR
148 #include <dirent.h>
149 static dev_t lvm_get_devno(const char *lvm_device)
150 {
151         FILE *lvf;
152         char buf[1024];
153         int ma, mi;
154         dev_t ret = 0;
155
156         DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device));
157         if ((lvf = fopen(lvm_device, "r")) == NULL) {
158                 DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno,
159                                           strerror(errno)));
160                 return 0;
161         }
162
163         while (fgets(buf, sizeof(buf), lvf)) {
164                 if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) {
165                         ret = makedev(ma, mi);
166                         break;
167                 }
168         }
169         fclose(lvf);
170
171         return ret;
172 }
173
174 static void lvm_probe_all(blkid_cache cache)
175 {
176         DIR             *vg_list;
177         struct dirent   *vg_iter;
178         int             vg_len = strlen(VG_DIR);
179         dev_t           dev;
180
181         if ((vg_list = opendir(VG_DIR)) == NULL)
182                 return;
183
184         DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR));
185
186         while ((vg_iter = readdir(vg_list)) != NULL) {
187                 DIR             *lv_list;
188                 char            *vdirname;
189                 char            *vg_name;
190                 struct dirent   *lv_iter;
191
192                 vg_name = vg_iter->d_name;
193                 if (!strcmp(vg_name, ".") || !strcmp(vg_name, ".."))
194                         continue;
195                 vdirname = xmalloc(vg_len + strlen(vg_name) + 8);
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
206                         lv_name = lv_iter->d_name;
207                         if (!strcmp(lv_name, ".") || !strcmp(lv_name, ".."))
208                                 continue;
209
210                         lvm_device = xmalloc(vg_len + strlen(vg_name) +
211                                             strlen(lv_name) + 8);
212                         sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
213                                 lv_name);
214                         dev = lvm_get_devno(lvm_device);
215                         sprintf(lvm_device, "%s/%s", vg_name, lv_name);
216                         DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n",
217                                                   lvm_device,
218                                                   (unsigned int) dev));
219                         probe_one(cache, lvm_device, dev, BLKID_PRI_LVM);
220                         free(lvm_device);
221                 }
222                 closedir(lv_list);
223         }
224         closedir(vg_list);
225 }
226 #endif
227
228 #define PROC_EVMS_VOLUMES "/proc/evms/volumes"
229
230 static int
231 evms_probe_all(blkid_cache cache)
232 {
233         char line[100];
234         int ma, mi, sz, num = 0;
235         FILE *procpt;
236         char device[110];
237
238         procpt = fopen(PROC_EVMS_VOLUMES, "r");
239         if (!procpt)
240                 return 0;
241         while (fgets(line, sizeof(line), procpt)) {
242                 if (sscanf (line, " %d %d %d %*s %*s %[^\n ]",
243                             &ma, &mi, &sz, device) != 4)
244                         continue;
245
246                 DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n",
247                                           device, ma, mi));
248
249                 probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS);
250                 num++;
251         }
252         fclose(procpt);
253         return num;
254 }
255
256 /*
257  * Read the device data for all available block devices in the system.
258  */
259 int blkid_probe_all(blkid_cache cache)
260 {
261         FILE *proc;
262         char line[1024];
263         char ptname0[128], ptname1[128], *ptname = 0;
264         char *ptnames[2];
265         dev_t devs[2];
266         int ma, mi;
267         unsigned long long sz;
268         int lens[2] = { 0, 0 };
269         int which = 0, last = 0;
270
271         ptnames[0] = ptname0;
272         ptnames[1] = ptname1;
273
274         if (!cache)
275                 return -BLKID_ERR_PARAM;
276
277         if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
278             time(0) - cache->bic_time < BLKID_PROBE_INTERVAL)
279                 return 0;
280
281         blkid_read_cache(cache);
282         evms_probe_all(cache);
283 #ifdef VG_DIR
284         lvm_probe_all(cache);
285 #endif
286
287         proc = fopen(PROC_PARTITIONS, "r");
288         if (!proc)
289                 return -BLKID_ERR_PROC;
290
291         while (fgets(line, sizeof(line), proc)) {
292                 last = which;
293                 which ^= 1;
294                 ptname = ptnames[which];
295
296                 if (sscanf(line, " %d %d %llu %128[^\n ]",
297                            &ma, &mi, &sz, ptname) != 4)
298                         continue;
299                 devs[which] = makedev(ma, mi);
300
301                 DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname));
302
303                 /* Skip whole disk devs unless they have no partitions
304                  * If we don't have a partition on this dev, also
305                  * check previous dev to see if it didn't have a partn.
306                  * heuristic: partition name ends in a digit.
307                  *
308                  * Skip extended partitions.
309                  * heuristic: size is 1
310                  *
311                  * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
312                  */
313
314                 lens[which] = strlen(ptname);
315                 if (isdigit(ptname[lens[which] - 1])) {
316                         DBG(DEBUG_DEVNAME,
317                             printf("partition dev %s, devno 0x%04X\n",
318                                    ptname, (unsigned int) devs[which]));
319
320                         if (sz > 1)
321                                 probe_one(cache, ptname, devs[which], 0);
322                         lens[which] = 0;
323                         lens[last] = 0;
324                 } else if (lens[last] && strncmp(ptnames[last], ptname,
325                                                  lens[last])) {
326                         DBG(DEBUG_DEVNAME,
327                             printf("whole dev %s, devno 0x%04X\n",
328                                    ptnames[last], (unsigned int) devs[last]));
329                         probe_one(cache, ptnames[last], devs[last], 0);
330                         lens[last] = 0;
331                 }
332         }
333
334         /* Handle the last device if it wasn't partitioned */
335         if (lens[which])
336                 probe_one(cache, ptname, devs[which], 0);
337
338         fclose(proc);
339
340         cache->bic_time = time(0);
341         cache->bic_flags |= BLKID_BIC_FL_PROBED;
342         blkid_flush_cache(cache);
343         return 0;
344 }
345
346 #ifdef TEST_PROGRAM
347 int main(int argc, char **argv)
348 {
349         blkid_cache cache = NULL;
350         int ret;
351
352         blkid_debug_mask = DEBUG_ALL;
353         if (argc != 1) {
354                 fprintf(stderr, "Usage: %s\n"
355                         "Probe all devices and exit\n", argv[0]);
356                 exit(1);
357         }
358         if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
359                 fprintf(stderr, "%s: error creating cache (%d)\n",
360                         argv[0], ret);
361                 exit(1);
362         }
363         if (blkid_probe_all(cache) < 0)
364                 printf("%s: error probing devices\n", argv[0]);
365
366         blkid_put_cache(cache);
367         return (0);
368 }
369 #endif