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