Update to 4.01.
[profile/ivi/quota.git] / bylabel.c
1 /*
2  * Derived from the util-linux/mount/mount_by_label.c source,
3  * currently maintained by Andries Brouwer <aeb@cwi.nl>.
4  *
5  * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
6  * - added Native Language Support
7  * 2000-01-20 James Antill <james@and.org>
8  * - Added error message if /proc/partitions cannot be opened
9  * 2000-05-09 Erik Troan <ewt@redhat.com>
10  * - Added cache for UUID and disk labels
11  * 2000-11-07 Nathan Scott <nathans@sgi.com>
12  * - Added XFS support
13  */
14
15 #include "config.h"
16
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
24 #include "bylabel.h"
25 #include "common.h"
26 #include "pot.h"
27
28 #define PROC_PARTITIONS "/proc/partitions"
29 #define DEVLABELDIR     "/dev"
30
31 static struct uuidCache_s {
32         struct uuidCache_s *next;
33         char uuid[16];
34         char *label;
35         char *device;
36 } *uuidCache = NULL;
37
38 #define EXT2_SUPER_MAGIC        0xEF53
39 struct ext2_super_block {
40         u_char s_dummy1[56];
41         u_char s_magic[2];
42         u_char s_dummy2[46];
43         u_char s_uuid[16];
44         u_char s_volume_name[16];
45 };
46
47 #define ext2magic(s)    ((uint) s.s_magic[0] + (((uint) s.s_magic[1]) << 8))
48
49 #define XFS_SUPER_MAGIC "XFSB"
50 #define XFS_SUPER_MAGIC2 "BSFX"
51 struct xfs_super_block {
52         u_char s_magic[4];
53         u_char s_dummy[28];
54         u_char s_uuid[16];
55         u_char s_dummy2[60];
56         u_char s_fsname[12];
57 };
58
59 #define REISER_SUPER_MAGIC      "ReIsEr2Fs"
60 struct reiserfs_super_block {
61         u_char s_dummy1[52];
62         u_char s_magic[10];
63         u_char s_dummy2[22];
64         u_char s_uuid[16];
65         u_char s_volume_name[16];
66 };
67
68 static inline unsigned short swapped(unsigned short a)
69 {
70         return (a >> 8) | (a << 8);
71 }
72
73 /* for now, only ext2 and xfs are supported */
74 static int get_label_uuid(const char *device, char **label, char *uuid)
75 {
76
77         /* start with ext2 and xfs tests, taken from mount_guess_fstype */
78         /* should merge these later */
79         int fd, rv = 1;
80         size_t namesize;
81         struct ext2_super_block e2sb;
82         struct xfs_super_block xfsb;
83         struct reiserfs_super_block reisersb;
84
85         fd = open(device, O_RDONLY);
86         if (fd < 0)
87                 return rv;
88
89         if (lseek(fd, 1024, SEEK_SET) == 1024
90             && read(fd, (char *)&e2sb, sizeof(e2sb)) == sizeof(e2sb)
91             && ext2magic(e2sb) == EXT2_SUPER_MAGIC) {
92                 memcpy(uuid, e2sb.s_uuid, sizeof(e2sb.s_uuid));
93                 namesize = sizeof(e2sb.s_volume_name);
94                 *label = smalloc(namesize + 1);
95                 sstrncpy(*label, (char *)e2sb.s_volume_name, namesize);
96                 rv = 0;
97         }
98         else if (lseek(fd, 0, SEEK_SET) == 0
99                  && read(fd, (char *)&xfsb, sizeof(xfsb)) == sizeof(xfsb)
100                  && (strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC, 4) == 0 ||
101                      strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC2, 4) == 0)) {
102                 memcpy(uuid, xfsb.s_uuid, sizeof(xfsb.s_uuid));
103                 namesize = sizeof(xfsb.s_fsname);
104                 *label = smalloc(namesize + 1);
105                 sstrncpy(*label, (char *)xfsb.s_fsname, namesize);
106                 rv = 0;
107         }
108         else if (lseek(fd, 65536, SEEK_SET) == 65536
109                 && read(fd, (char *)&reisersb, sizeof(reisersb)) == sizeof(reisersb)
110                 && !strncmp((char *)&reisersb.s_magic, REISER_SUPER_MAGIC, 9)) {
111                 memcpy(uuid, reisersb.s_uuid, sizeof(reisersb.s_uuid));
112                 namesize = sizeof(reisersb.s_volume_name);
113                 *label = smalloc(namesize + 1);
114                 sstrncpy(*label, (char *)reisersb.s_volume_name, namesize);
115                 rv = 0;
116         }
117         close(fd);
118         return rv;
119 }
120
121 static void uuidcache_addentry(char *device, char *label, char *uuid)
122 {
123         struct uuidCache_s *last;
124
125         if (!uuidCache) {
126                 last = uuidCache = smalloc(sizeof(*uuidCache));
127         }
128         else {
129                 for (last = uuidCache; last->next; last = last->next);
130                 last->next = smalloc(sizeof(*uuidCache));
131                 last = last->next;
132         }
133         last->next = NULL;
134         last->device = device;
135         last->label = label;
136         memcpy(last->uuid, uuid, sizeof(last->uuid));
137 }
138
139 static void uuidcache_init(void)
140 {
141         char line[100];
142         char *s;
143         int ma, mi, sz;
144         static char ptname[100];
145         FILE *procpt;
146         char uuid[16], *label;
147         char device[110];
148         int firstPass;
149         int handleOnFirst;
150
151         if (uuidCache)
152                 return;
153
154         procpt = fopen(PROC_PARTITIONS, "r");
155         if (!procpt)
156                 return;
157
158         for (firstPass = 1; firstPass >= 0; firstPass--) {
159                 fseek(procpt, 0, SEEK_SET);
160
161                 while (fgets(line, sizeof(line), procpt)) {
162                         if (sscanf(line, " %d %d %d %[^\n ]", &ma, &mi, &sz, ptname) != 4)
163                                 continue;
164
165                         /* skip extended partitions (heuristic: size 1) */
166                         if (sz == 1)
167                                 continue;
168
169                         /* look only at md devices on first pass */
170                         handleOnFirst = !strncmp(ptname, "md", 2);
171                         if (firstPass != handleOnFirst)
172                                 continue;
173
174                         /* skip entire disk (minor 0, 64, ... on ide;
175                            0, 16, ... on sd) */
176                         /* heuristic: partition name ends in a digit */
177
178                         for (s = ptname; *s; s++);
179                         if (isdigit(s[-1])) {
180                                 /*
181                                  * Note: this is a heuristic only - there is no reason
182                                  * why these devices should live in /dev.
183                                  * Perhaps this directory should be specifiable by option.
184                                  * One might for example have /devlabel with links to /dev
185                                  * for the devices that may be accessed in this way.
186                                  * (This is useful, if the cdrom on /dev/hdc must not
187                                  * be accessed.)
188                                  */
189                                 snprintf(device, sizeof(device), "%s/%s", DEVLABELDIR, ptname);
190                                 if (!get_label_uuid(device, &label, uuid))
191                                         uuidcache_addentry(sstrdup(device), label, uuid);
192                         }
193                 }
194         }
195
196         fclose(procpt);
197 }
198
199 #define UUID   1
200 #define VOL    2
201
202 static char *get_spec_by_x(int n, const char *t)
203 {
204         struct uuidCache_s *uc;
205
206         uuidcache_init();
207         uc = uuidCache;
208
209         while (uc) {
210                 switch (n) {
211                   case UUID:
212                           if (!memcmp(t, uc->uuid, sizeof(uc->uuid)))
213                                   return sstrdup(uc->device);
214                           break;
215                   case VOL:
216                           if (!strcmp(t, uc->label))
217                                   return sstrdup(uc->device);
218                           break;
219                 }
220                 uc = uc->next;
221         }
222         return NULL;
223 }
224
225 static u_char fromhex(char c)
226 {
227         if (isdigit(c))
228                 return (c - '0');
229         else if (islower(c))
230                 return (c - 'a' + 10);
231         else
232                 return (c - 'A' + 10);
233 }
234
235 static char *get_spec_by_uuid(const char *s)
236 {
237         u_char uuid[16];
238         int i;
239
240         if (strlen(s) != 36 || s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
241                 goto bad_uuid;
242         for (i = 0; i < 16; i++) {
243                 if (*s == '-')
244                         s++;
245                 if (!isxdigit(s[0]) || !isxdigit(s[1]))
246                         goto bad_uuid;
247                 uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1]));
248                 s += 2;
249         }
250         return get_spec_by_x(UUID, (char *)uuid);
251
252       bad_uuid:
253         errstr(_("Found an invalid UUID: %s\n"), s);
254         return NULL;
255 }
256
257 static char *get_spec_by_volume_label(const char *s)
258 {
259         return get_spec_by_x(VOL, s);
260 }
261
262 const char *get_device_name(const char *item)
263 {
264         const char *rc;
265
266         if (!strncmp(item, "UUID=", 5))
267                 rc = get_spec_by_uuid(item + 5);
268         else if (!strncmp(item, "LABEL=", 6))
269                 rc = get_spec_by_volume_label(item + 6);
270         else
271                 rc = sstrdup(item);
272         if (!rc)
273                 errstr(_("Error checking device name: %s\n"), item);
274         return rc;
275 }