chiark / gitweb /
dcfb0bb92f8bdfa7c27421378ab5f87b0d1a5bd0
[elogind.git] / extras / volume_id / vol_id.c
1 /*
2  * vol_id - read filesystem label and uuid
3  *
4  * Copyright (C) 2005-2006 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  *      This program is free software; you can redistribute it and/or modify it
7  *      under the terms of the GNU General Public License as published by the
8  *      Free Software Foundation version 2 of the License.
9  * 
10  *      This program is distributed in the hope that it will be useful, but
11  *      WITHOUT ANY WARRANTY; without even the implied warranty of
12  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *      General Public License for more details.
14  * 
15  *      You should have received a copy of the GNU General Public License along
16  *      with this program; if not, write to the Free Software Foundation, Inc.,
17  *      675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  */
20
21 #ifndef _GNU_SOURCE
22 #define _GNU_SOURCE 1
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <pwd.h>
32 #include <grp.h>
33 #include <getopt.h>
34 #include <fcntl.h>
35 #include <sys/ioctl.h>
36
37 #include "../../udev/udev.h"
38 #include "lib/libvolume_id.h"
39
40 #define BLKGETSIZE64 _IOR(0x12,114,size_t)
41
42 static void log_fn(struct udev *udev, int priority,
43                    const char *file, int line, const char *fn,
44                    const char *format, va_list args)
45 {
46         vsyslog(priority, format, args);
47 }
48
49 static void vid_log(int priority, const char *file, int line, const char *format, ...)
50 {
51         va_list args;
52
53         va_start(args, format);
54         log_fn(NULL, priority, file, line, NULL, format, args);
55         va_end(args);
56         return;
57 }
58
59 static void set_str(char *to, const char *from, size_t count)
60 {
61         size_t i, j, len;
62
63         /* strip trailing whitespace */
64         len = strnlen(from, count);
65         while (len && isspace(from[len-1]))
66                 len--;
67
68         /* strip leading whitespace */
69         i = 0;
70         while (isspace(from[i]) && (i < len))
71                 i++;
72
73         j = 0;
74         while (i < len) {
75                 /* substitute multiple whitespace */
76                 if (isspace(from[i])) {
77                         while (isspace(from[i]))
78                                 i++;
79                         to[j++] = '_';
80                 }
81                 /* skip chars */
82                 if (from[i] == '/') {
83                         i++;
84                         continue;
85                 }
86                 to[j++] = from[i++];
87         }
88         to[j] = '\0';
89 }
90
91 static int all_probers(volume_id_probe_fn_t probe_fn,
92                        struct volume_id *id, uint64_t off, uint64_t size,
93                        void *data)
94 {
95         const char *type;
96
97         if (probe_fn(id, off, size) == 0)
98                 if (volume_id_get_type(id, &type))
99                         printf("%s\n", type);
100
101         return 0;
102 }
103
104 int main(int argc, char *argv[])
105 {
106         struct udev *udev;
107         static const struct option options[] = {
108                 { "label", 0, NULL, 'l' },
109                 { "label-raw", 0, NULL, 'L' },
110                 { "uuid", 0, NULL, 'u' },
111                 { "type", 0, NULL, 't' },
112                 { "export", 0, NULL, 'x' },
113                 { "skip-raid", 0, NULL, 's' },
114                 { "probe-all", 0, NULL, 'a' },
115                 { "offset", 2, NULL, 'o' },
116                 { "help", 0, NULL, 'h' },
117                 {}
118         };
119
120         enum print_type {
121                 PRINT_EXPORT,
122                 PRINT_TYPE,
123                 PRINT_LABEL,
124                 PRINT_UUID,
125                 PRINT_LABEL_RAW,
126         } print = PRINT_EXPORT;
127
128         struct volume_id *vid = NULL;
129         char label_safe[256];
130         char label_enc[256];
131         char uuid_enc[256];
132         uint64_t size;
133         int skip_raid = 0;
134         int probe_all = 0;
135         uint64_t offset = 0;
136         const char *node;
137         int fd;
138         const char *label, *uuid, *type, *type_version, *usage;
139         int retval;
140         int rc = 0;
141
142         udev = udev_new();
143         if (udev == NULL)
144                 goto exit;
145         logging_init("vol_id");
146         udev_set_log_fn(udev, log_fn);
147
148         /* hook in our debug into libvolume_id */
149         volume_id_log_fn = vid_log;
150
151         while (1) {
152                 int option;
153
154                 option = getopt_long(argc, argv, "lLutxsaoh", options, NULL);
155                 if (option == -1)
156                         break;
157
158                 switch (option) {
159                 case 'l':
160                         print = PRINT_LABEL;
161                         break;
162                 case 'L':
163                         print = PRINT_LABEL_RAW;
164                         break;
165                 case 'u':
166                         print = PRINT_UUID;
167                         break;
168                 case 't':
169                         print = PRINT_TYPE;
170                         break;
171                 case 'x':
172                         print = PRINT_EXPORT;
173                         break;
174                 case 's':
175                         skip_raid = 1;
176                         break;
177                 case 'a':
178                         probe_all = 1;
179                         break;
180                 case 'o':
181                         if (optarg[0] != '\0')
182                                 offset = strtoull(optarg, NULL, 0);
183                         break;
184                 case 'h':
185                         printf("Usage: vol_id [options] <device>\n"
186                             " --export        export key/value pairs\n"
187                             " --type          filesystem type\n"
188                             " --label         filesystem label\n"
189                             " --label-raw     raw label\n"
190                             " --uuid          filesystem uuid\n"
191                             " --skip-raid     don't probe for raid\n"
192                             " --probe-all     find possibly conflicting signatures\n"
193                             " --offset        skip given number of bytes of input\n"
194                             " --help\n\n");
195                         goto exit;
196                 default:
197                         retval = 1;
198                         goto exit;
199                 }
200         }
201
202         node = argv[optind];
203         if (!node) {
204                 err(udev, "no device\n");
205                 fprintf(stderr, "no device\n");
206                 rc = 1;
207                 goto exit;
208         }
209
210         fd = open(node, O_RDONLY);
211         if (fd < 0) {
212                 fprintf(stderr, "%s: error opening volume\n", node);
213                 rc = 2;
214                 goto exit;
215         }
216
217         vid = volume_id_open_fd(fd);
218         if (vid == NULL) {
219                 rc = 2;
220                 goto exit;
221         }
222
223         if (ioctl(fd, BLKGETSIZE64, &size) != 0)
224                 size = 0;
225         dbg(udev, "BLKGETSIZE64=%llu\n", (unsigned long long)size);
226
227         /* try to drop all privileges before reading disk content */
228         if (getuid() == 0) {
229                 struct passwd *pw;
230
231                 pw = getpwnam("nobody");
232                 if (pw != NULL && pw->pw_uid > 0 && pw->pw_gid > 0) {
233                         if (setgroups(0, NULL) != 0 ||
234                             setgid(pw->pw_gid) != 0 ||
235                             setuid(pw->pw_uid) != 0)
236                                 info(udev, "unable to drop privileges: %s\n\n", strerror(errno));
237                 }
238         }
239
240         if (probe_all) {
241                 volume_id_all_probers(all_probers, vid, offset, size, NULL);
242                 goto exit;
243         }
244
245         if (skip_raid)
246                 retval = volume_id_probe_filesystem(vid, offset, size);
247         else
248                 retval = volume_id_probe_all(vid, offset, size);
249         if (retval != 0) {
250                 fprintf(stderr, "%s: unknown volume type\n", node);
251                 rc = 4;
252                 goto exit;
253         }
254
255         if (!volume_id_get_label(vid, &label) ||
256             !volume_id_get_usage(vid, &usage) ||
257             !volume_id_get_type(vid, &type) ||
258             !volume_id_get_type_version(vid, &type_version) ||
259             !volume_id_get_uuid(vid, &uuid)) {
260                 rc = 4;
261                 goto exit;
262         }
263
264         set_str(label_safe, label, sizeof(label_safe));
265         replace_chars(label_safe, ALLOWED_CHARS_INPUT);
266
267         volume_id_encode_string(label, label_enc, sizeof(label_enc));
268         volume_id_encode_string(uuid, uuid_enc, sizeof(uuid_enc));
269
270         switch (print) {
271         case PRINT_EXPORT:
272                 printf("ID_FS_USAGE=%s\n", usage);
273                 printf("ID_FS_TYPE=%s\n", type);
274                 printf("ID_FS_VERSION=%s\n", type_version);
275                 printf("ID_FS_UUID=%s\n", uuid);
276                 printf("ID_FS_UUID_ENC=%s\n", uuid_enc);
277                 printf("ID_FS_LABEL=%s\n", label);
278                 printf("ID_FS_LABEL_ENC=%s\n", label_enc);
279                 printf("ID_FS_LABEL_SAFE=%s\n", label_safe);
280                 break;
281         case PRINT_TYPE:
282                 printf("%s\n", type);
283                 break;
284         case PRINT_LABEL:
285                 if (label_safe[0] == '\0' || strcmp(usage, "raid") == 0) {
286                         rc = 3;
287                         goto exit;
288                 }
289                 printf("%s\n", label_safe);
290                 break;
291         case PRINT_UUID:
292                 if (uuid_enc[0] == '\0' || strcmp(usage, "raid") == 0) {
293                         rc = 4;
294                         goto exit;
295                 }
296                 printf("%s\n", uuid_enc);
297                 break;
298         case PRINT_LABEL_RAW:
299                 if (label[0] == '\0' || strcmp(usage, "raid") == 0) {
300                         rc = 3;
301                         goto exit;
302                 }
303                 printf("%s\n", label);
304                 break;
305         }
306
307 exit:
308         if (vid != NULL)
309                 volume_id_close(vid);
310         udev_unref(udev);
311         logging_close();
312         return rc;
313 }