put small subset of e2fsprogs back in the tree:
[oweals/busybox.git] / 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         return;
135 }
136
137 #define PROC_PARTITIONS "/proc/partitions"
138 #define VG_DIR          "/proc/lvm/VGs"
139
140 /*
141  * This function initializes the UUID cache with devices from the LVM
142  * proc hierarchy.  We currently depend on the names of the LVM
143  * hierarchy giving us the device structure in /dev.  (XXX is this a
144  * safe thing to do?)
145  */
146 #ifdef VG_DIR
147 #include <dirent.h>
148 static dev_t lvm_get_devno(const char *lvm_device)
149 {
150         FILE *lvf;
151         char buf[1024];
152         int ma, mi;
153         dev_t ret = 0;
154
155         DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device));
156         if ((lvf = fopen(lvm_device, "r")) == NULL) {
157                 DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno,
158                                           strerror(errno)));
159                 return 0;
160         }
161
162         while (fgets(buf, sizeof(buf), lvf)) {
163                 if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) {
164                         ret = makedev(ma, mi);
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         dev_t           dev;
179
180         if ((vg_list = opendir(VG_DIR)) == NULL)
181                 return;
182
183         DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR));
184
185         while ((vg_iter = readdir(vg_list)) != NULL) {
186                 DIR             *lv_list;
187                 char            *vdirname;
188                 char            *vg_name;
189                 struct dirent   *lv_iter;
190
191                 vg_name = vg_iter->d_name;
192                 if (LONE_CHAR(vg_name, '.') || !strcmp(vg_name, ".."))
193                         continue;
194                 vdirname = xmalloc(vg_len + strlen(vg_name) + 8);
195                 sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name);
196
197                 lv_list = opendir(vdirname);
198                 free(vdirname);
199                 if (lv_list == NULL)
200                         continue;
201
202                 while ((lv_iter = readdir(lv_list)) != NULL) {
203                         char            *lv_name, *lvm_device;
204
205                         lv_name = lv_iter->d_name;
206                         if (LONE_CHAR(lv_name, '.') || !strcmp(lv_name, ".."))
207                                 continue;
208
209                         lvm_device = xmalloc(vg_len + strlen(vg_name) +
210                                             strlen(lv_name) + 8);
211                         sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
212                                 lv_name);
213                         dev = lvm_get_devno(lvm_device);
214                         sprintf(lvm_device, "%s/%s", vg_name, lv_name);
215                         DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n",
216                                                   lvm_device,
217                                                   (unsigned int) dev));
218                         probe_one(cache, lvm_device, dev, BLKID_PRI_LVM);
219                         free(lvm_device);
220                 }
221                 closedir(lv_list);
222         }
223         closedir(vg_list);
224 }
225 #endif
226
227 #define PROC_EVMS_VOLUMES "/proc/evms/volumes"
228
229 static int
230 evms_probe_all(blkid_cache cache)
231 {
232         char line[100];
233         int ma, mi, sz, num = 0;
234         FILE *procpt;
235         char device[110];
236
237         procpt = fopen(PROC_EVMS_VOLUMES, "r");
238         if (!procpt)
239                 return 0;
240         while (fgets(line, sizeof(line), procpt)) {
241                 if (sscanf (line, " %d %d %d %*s %*s %[^\n ]",
242                             &ma, &mi, &sz, device) != 4)
243                         continue;
244
245                 DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n",
246                                           device, ma, mi));
247
248                 probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS);
249                 num++;
250         }
251         fclose(procpt);
252         return num;
253 }
254
255 /*
256  * Read the device data for all available block devices in the system.
257  */
258 int blkid_probe_all(blkid_cache cache)
259 {
260         FILE *proc;
261         char line[1024];
262         char ptname0[128], ptname1[128], *ptname = 0;
263         char *ptnames[2];
264         dev_t devs[2];
265         int ma, mi;
266         unsigned long long sz;
267         int lens[2] = { 0, 0 };
268         int which = 0, last = 0;
269
270         ptnames[0] = ptname0;
271         ptnames[1] = ptname1;
272
273         if (!cache)
274                 return -BLKID_ERR_PARAM;
275
276         if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
277             time(0) - cache->bic_time < BLKID_PROBE_INTERVAL)
278                 return 0;
279
280         blkid_read_cache(cache);
281         evms_probe_all(cache);
282 #ifdef VG_DIR
283         lvm_probe_all(cache);
284 #endif
285
286         proc = fopen(PROC_PARTITIONS, "r");
287         if (!proc)
288                 return -BLKID_ERR_PROC;
289
290         while (fgets(line, sizeof(line), proc)) {
291                 last = which;
292                 which ^= 1;
293                 ptname = ptnames[which];
294
295                 if (sscanf(line, " %d %d %llu %128[^\n ]",
296                            &ma, &mi, &sz, ptname) != 4)
297                         continue;
298                 devs[which] = makedev(ma, mi);
299
300                 DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname));
301
302                 /* Skip whole disk devs unless they have no partitions
303                  * If we don't have a partition on this dev, also
304                  * check previous dev to see if it didn't have a partn.
305                  * heuristic: partition name ends in a digit.
306                  *
307                  * Skip extended partitions.
308                  * heuristic: size is 1
309                  *
310                  * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
311                  */
312
313                 lens[which] = strlen(ptname);
314                 if (isdigit(ptname[lens[which] - 1])) {
315                         DBG(DEBUG_DEVNAME,
316                             printf("partition dev %s, devno 0x%04X\n",
317                                    ptname, (unsigned int) devs[which]));
318
319                         if (sz > 1)
320                                 probe_one(cache, ptname, devs[which], 0);
321                         lens[which] = 0;
322                         lens[last] = 0;
323                 } else if (lens[last] && strncmp(ptnames[last], ptname,
324                                                  lens[last])) {
325                         DBG(DEBUG_DEVNAME,
326                             printf("whole dev %s, devno 0x%04X\n",
327                                    ptnames[last], (unsigned int) devs[last]));
328                         probe_one(cache, ptnames[last], devs[last], 0);
329                         lens[last] = 0;
330                 }
331         }
332
333         /* Handle the last device if it wasn't partitioned */
334         if (lens[which])
335                 probe_one(cache, ptname, devs[which], 0);
336
337         fclose(proc);
338
339         cache->bic_time = time(0);
340         cache->bic_flags |= BLKID_BIC_FL_PROBED;
341         blkid_flush_cache(cache);
342         return 0;
343 }
344
345 #ifdef TEST_PROGRAM
346 int main(int argc, char **argv)
347 {
348         blkid_cache cache = NULL;
349         int ret;
350
351         blkid_debug_mask = DEBUG_ALL;
352         if (argc != 1) {
353                 fprintf(stderr, "Usage: %s\n"
354                         "Probe all devices and exit\n", argv[0]);
355                 exit(1);
356         }
357         if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
358                 fprintf(stderr, "%s: error creating cache (%d)\n",
359                         argv[0], ret);
360                 exit(1);
361         }
362         if (blkid_probe_all(cache) < 0)
363                 printf("%s: error probing devices\n", argv[0]);
364
365         blkid_put_cache(cache);
366         return 0;
367 }
368 #endif