Imported Upstream version 0.4b37
[debian/dump] / compat / lib / bylabel.c
1 /*
2  * mount_by_label.c - aeb
3  *
4  * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
5  * - added Native Language Support
6  * 2000-01-20 James Antill <james@and.org>
7  * - Added error message if /proc/partitions cannot be opened
8  * 2000-05-09 Erik Troan <ewt@redhat.com>
9  * - Added cache for UUID and disk labels
10  * Wed Aug 16 2000 Erik Troan <ewt@redhat.com>
11  * - Ported to dump/restore
12  */
13
14 #include <config.h>
15 #include <compatlfs.h>
16 #include <sys/types.h>
17 #include <stdio.h>
18 #include <sys/param.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <malloc.h>
24 #include <sys/cdefs.h>
25 #include "bylabel.h"
26
27 #ifndef HAVE_BLKID
28
29 #define PROC_PARTITIONS "/proc/partitions"
30 #define DEVLABELDIR     "/dev"
31
32 #define EXT2_SUPER_OFFSET       1024
33 #define EXT2_SUPER_SIZE         sizeof(struct ext2_super_block)
34 #define EXT2_SUPER_MAGIC        0xEF53
35
36 #define VOLNAMSZ        16
37
38 struct ext2_super_block {
39         unsigned char   s_dummy1[56];
40         unsigned char   s_magic[2];
41         unsigned char   s_dummy2[46];
42         unsigned char   s_uuid[16];
43         unsigned char   s_volume_name[VOLNAMSZ];
44 };
45 #define ext2magic(s)    ((unsigned int) s.s_magic[0] + (((unsigned int) s.s_magic[1]) << 8))
46
47 void msg __P((const char *fmt, ...));
48
49 static struct uuidCache_s {
50         struct uuidCache_s *next;
51         char uuid[16];
52         char *label;
53         char *device;
54 } *uuidCache = NULL;
55
56 /* for now, only ext2 is supported */
57 static int
58 get_label_uuid(const char *device, char **label, char *uuid) {
59
60         /* start with a test for ext2, taken from mount_guess_fstype */
61         /* should merge these later */
62         int fd;
63         struct ext2_super_block e2sb;
64
65         fd = OPEN(device, O_RDONLY);
66         if (fd < 0)
67                 return 1;
68
69         if (LSEEK(fd, EXT2_SUPER_OFFSET, SEEK_SET) != EXT2_SUPER_OFFSET ||
70             read(fd, (char *) &e2sb, EXT2_SUPER_SIZE) != EXT2_SUPER_SIZE ||
71             ext2magic(e2sb) != EXT2_SUPER_MAGIC) {
72                 close(fd);
73                 return 1;
74         }
75
76         close(fd);
77
78         /* superblock is ext2 - now what is its label? */
79         memcpy(uuid, e2sb.s_uuid, sizeof(e2sb.s_uuid));
80         *label = malloc(VOLNAMSZ + 1);
81         strncpy(*label, e2sb.s_volume_name, VOLNAMSZ);
82         (*label)[VOLNAMSZ] = 0;
83
84         return 0;
85 }
86
87 static void
88 uuidcache_addentry(char *device, char *label, char *uuid) {
89         struct uuidCache_s *last;
90
91         if (!uuidCache) {
92                 last = uuidCache = (struct uuidCache_s *)malloc(sizeof(*uuidCache));
93         } else {
94                 for (last = uuidCache; last->next; last = last->next) ;
95                 last->next = (struct uuidCache_s *)malloc(sizeof(*uuidCache));
96                 last = last->next;
97         }
98         last->next = NULL;
99         last->device = device;
100         last->label = label;
101         memcpy(last->uuid, uuid, sizeof(last->uuid));
102 }
103
104 static void
105 uuidcache_init(void) {
106         char line[100];
107         char *s;
108         int ma, mi, sz;
109         static char ptname[100];
110         FILE *procpt;
111         char uuid[16], *label;
112         char device[110];
113         int firstPass;
114         int handleOnFirst;
115
116         if (uuidCache)
117                 return;
118
119         procpt = fopen(PROC_PARTITIONS, "r");
120         if (!procpt)
121                 return;
122
123         for (firstPass = 1; firstPass >= 0; firstPass--) {
124             fseek(procpt, 0, SEEK_SET);
125
126             while (fgets(line, sizeof(line), procpt)) {
127                 if (sscanf (line, " %d %d %d %[^\n ]",
128                             &ma, &mi, &sz, ptname) != 4)
129                         continue;
130
131                 /* skip extended partitions (heuristic: size 1) */
132                 if (sz == 1)
133                         continue;
134
135                 /* look only at md devices on first pass */
136                 handleOnFirst = !strncmp(ptname, "md", 2);
137                 if (firstPass != handleOnFirst)
138                         continue;
139
140                 /* skip entire disk (minor 0, 64, ... on ide;
141                    0, 16, ... on sd) */
142                 /* heuristic: partition name ends in a digit */
143
144                 for(s = ptname; *s; s++);
145                 if (isdigit(s[-1])) {
146                 /*
147                  * Note: this is a heuristic only - there is no reason
148                  * why these devices should live in /dev.
149                  * Perhaps this directory should be specifiable by option.
150                  * One might for example have /devlabel with links to /dev
151                  * for the devices that may be accessed in this way.
152                  * (This is useful, if the cdrom on /dev/hdc must not
153                  * be accessed.)
154                  */
155                         sprintf(device, "%s/%s", DEVLABELDIR, ptname);
156                         if (!get_label_uuid(device, &label, uuid))
157                                 uuidcache_addentry(strdup(device), label, uuid);
158                 }
159             }
160         }
161
162         fclose(procpt);
163 }
164
165 #define UUID   1
166 #define VOL    2
167
168 static char *
169 get_spec_by_x(int n, const char *t) {
170         struct uuidCache_s *uc;
171
172         uuidcache_init();
173         uc = uuidCache;
174
175         while(uc) {
176                 switch (n) {
177                 case UUID:
178                         if (!memcmp(t, uc->uuid, sizeof(uc->uuid)))
179                                 return strdup(uc->device);
180                         break;
181                 case VOL:
182                         if (!strcmp(t, uc->label))
183                                 return strdup(uc->device);
184                         break;
185                 }
186                 uc = uc->next;
187         }
188         return NULL;
189 }
190
191 static u_char
192 fromhex(char c) {
193         if (isdigit(c))
194                 return (c - '0');
195         else if (islower(c))
196                 return (c - 'a' + 10);
197         else
198                 return (c - 'A' + 10);
199 }
200
201 static char *
202 get_spec_by_uuid(const char *s) {
203         u_char uuid[16];
204         int i;
205
206         if (strlen(s) != 36 ||
207             s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
208                 goto bad_uuid;
209         for (i=0; i<16; i++) {
210             if (*s == '-') s++;
211             if (!isxdigit(s[0]) || !isxdigit(s[1]))
212                     goto bad_uuid;
213             uuid[i] = ((fromhex(s[0])<<4) | fromhex(s[1]));
214             s += 2;
215         }
216         return get_spec_by_x(UUID, uuid);
217
218  bad_uuid:
219         msg("mount: bad UUID\n");
220         return NULL;            /* just for gcc */
221 }
222
223 static char *
224 get_spec_by_volume_label(const char *s) {
225         return get_spec_by_x(VOL, s);
226 }
227
228 const char * 
229 get_device_name(const char * item) {
230         const char * rc;
231
232         if (!strncmp(item, "UUID=", 5)) {
233                 rc = get_spec_by_uuid(item+5);
234         } 
235         else
236                 if (!strncmp(item, "LABEL=", 6)) {
237                         rc = get_spec_by_volume_label(item+6);
238                 }
239                 else {
240                         rc = item;
241                 }
242
243         return rc;
244 }
245
246 const char *
247 get_device_label(const char * spec) {
248         struct uuidCache_s *uc;
249
250         uuidcache_init();
251         uc = uuidCache;
252
253         while(uc) {
254                 if (!strcmp(spec, uc->device))
255                         return uc->label[0] == '\0' ? NULL : strdup(uc->label);
256                 uc = uc->next;
257         }
258         return NULL;
259 }
260
261 #endif /* !HAVE_BLKID */