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