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.h" 38#include "lib/libvolume_id.h" 39 40#define BLKGETSIZE64 _IOR(0x12,114,size_t) 41 42#ifdef USE_LOG 43void log_message(int priority, const char *format, ...) 44{ 45 va_list args; 46 static int udev_log = -1; 47 48 if (udev_log == -1) { 49 const char *value; 50 51 value = getenv("UDEV_LOG"); 52 if (value) 53 udev_log = log_priority(value); 54 else 55 udev_log = LOG_ERR; 56 } 57 58 if (priority > udev_log) 59 return; 60 61 va_start(args, format); 62 vsyslog(priority, format, args); 63 va_end(args); 64} 65#endif 66 67static void vid_log(int priority, const char *file, int line, const char *format, ...) 68{ 69#ifdef USE_LOG 70 char log_str[1024]; 71 va_list args; 72 73 va_start(args, format); 74 vsnprintf(log_str, sizeof(log_str), format, args); 75 log_str[sizeof(log_str)-1] = '\0'; 76 log_message(priority, "%s:%i %s", file, line, log_str); 77 va_end(args); 78#endif 79 return; 80} 81 82static void set_str(char *to, const char *from, size_t count) 83{ 84 size_t i, j, len; 85 86 /* strip trailing whitespace */ 87 len = strnlen(from, count); 88 while (len && isspace(from[len-1])) 89 len--; 90 91 /* strip leading whitespace */ 92 i = 0; 93 while (isspace(from[i]) && (i < len)) 94 i++; 95 96 j = 0; 97 while (i < len) { 98 /* substitute multiple whitespace */ 99 if (isspace(from[i])) { 100 while (isspace(from[i])) 101 i++; 102 to[j++] = '_'; 103 } 104 /* skip chars */ 105 if (from[i] == '/') { 106 i++; 107 continue; 108 } 109 to[j++] = from[i++]; 110 } 111 to[j] = '\0'; 112} 113 114static int all_probers(volume_id_probe_fn_t probe_fn, 115 struct volume_id *id, uint64_t off, uint64_t size, 116 void *data) 117{ 118 const char *type; 119 120 if (probe_fn(id, off, size) == 0) 121 if (volume_id_get_type(id, &type)) 122 printf("%s\n", type); 123 124 return 0; 125} 126 127int main(int argc, char *argv[]) 128{ 129 static const struct option options[] = { 130 { "label", 0, NULL, 'l' }, 131 { "label-raw", 0, NULL, 'L' }, 132 { "uuid", 0, NULL, 'u' }, 133 { "type", 0, NULL, 't' }, 134 { "export", 0, NULL, 'x' }, 135 { "skip-raid", 0, NULL, 's' }, 136 { "probe-all", 0, NULL, 'a' }, 137 { "help", 0, NULL, 'h' }, 138 {} 139 }; 140 141 enum print_type { 142 PRINT_EXPORT, 143 PRINT_TYPE, 144 PRINT_LABEL, 145 PRINT_UUID, 146 PRINT_LABEL_RAW, 147 } print = PRINT_EXPORT; 148 149 struct volume_id *vid = NULL; 150 char label_safe[256]; 151 char label_enc[256]; 152 char uuid_enc[256]; 153 uint64_t size; 154 int skip_raid = 0; 155 int probe_all = 0; 156 const char *node; 157 int fd; 158 const char *label, *uuid, *type, *type_version, *usage; 159 int retval; 160 int rc = 0; 161 162 logging_init("vol_id"); 163 164 /* hook in our debug into libvolume_id */ 165 volume_id_log_fn = vid_log; 166 167 while (1) { 168 int option; 169 170 option = getopt_long(argc, argv, "lLutxsah", options, NULL); 171 if (option == -1) 172 break; 173 174 switch (option) { 175 case 'l': 176 print = PRINT_LABEL; 177 break; 178 case 'L': 179 print = PRINT_LABEL_RAW; 180 break; 181 case 'u': 182 print = PRINT_UUID; 183 break; 184 case 't': 185 print = PRINT_TYPE; 186 break; 187 case 'x': 188 print = PRINT_EXPORT; 189 break; 190 case 's': 191 skip_raid = 1; 192 break; 193 case 'a': 194 probe_all = 1; 195 break; 196 case 'h': 197 printf("Usage: vol_id [options] <device>\n" 198 " --export export key/value pairs\n" 199 " --type filesystem type\n" 200 " --label filesystem label\n" 201 " --label-raw raw label\n" 202 " --uuid filesystem uuid\n" 203 " --skip-raid don't probe for raid\n" 204 " --probe-all find possibly conflicting signatures\n" 205 " --help\n\n"); 206 goto exit; 207 default: 208 retval = 1; 209 goto exit; 210 } 211 } 212 213 node = argv[optind]; 214 if (!node) { 215 err("no device"); 216 fprintf(stderr, "no device\n"); 217 rc = 1; 218 goto exit; 219 } 220 221 fd = open(node, O_RDONLY); 222 if (fd < 0) { 223 fprintf(stderr, "%s: error opening volume\n", node); 224 rc = 2; 225 goto exit; 226 } 227 228 vid = volume_id_open_fd(fd); 229 if (vid == NULL) { 230 rc = 2; 231 goto exit; 232 } 233 234 if (ioctl(fd, BLKGETSIZE64, &size) != 0) 235 size = 0; 236 dbg("BLKGETSIZE64=%llu", (unsigned long long)size); 237 238 /* try to drop all privileges before reading disk content */ 239 if (getuid() == 0) { 240 struct passwd *pw; 241 242 pw = getpwnam("nobody"); 243 if (pw != NULL && pw->pw_uid > 0 && pw->pw_gid > 0) { 244 if (setgroups(0, NULL) != 0 || 245 setgid(pw->pw_gid) != 0 || 246 setuid(pw->pw_uid) != 0) 247 info("unable to drop privileges: %s\n", strerror(errno)); 248 } 249 } 250 251 if (probe_all) { 252 volume_id_all_probers(all_probers, vid, 0, size, NULL); 253 goto exit; 254 } 255 256 if (skip_raid) 257 retval = volume_id_probe_filesystem(vid, 0, size); 258 else 259 retval = volume_id_probe_all(vid, 0, size); 260 if (retval != 0) { 261 fprintf(stderr, "%s: unknown volume type\n", node); 262 rc = 4; 263 goto exit; 264 } 265 266 if (!volume_id_get_label(vid, &label) || 267 !volume_id_get_usage(vid, &usage) || 268 !volume_id_get_type(vid, &type) || 269 !volume_id_get_type_version(vid, &type_version) || 270 !volume_id_get_uuid(vid, &uuid)) { 271 rc = 4; 272 goto exit; 273 } 274 275 set_str(label_safe, label, sizeof(label_safe)); 276 replace_chars(label_safe, ALLOWED_CHARS_INPUT); 277 278 volume_id_encode_string(label, label_enc, sizeof(label_enc)); 279 volume_id_encode_string(uuid, uuid_enc, sizeof(uuid_enc)); 280 281 switch (print) { 282 case PRINT_EXPORT: 283 printf("ID_FS_USAGE=%s\n", usage); 284 printf("ID_FS_TYPE=%s\n", type); 285 printf("ID_FS_VERSION=%s\n", type_version); 286 printf("ID_FS_UUID=%s\n", uuid); 287 printf("ID_FS_UUID_ENC=%s\n", uuid_enc); 288 printf("ID_FS_LABEL=%s\n", label); 289 printf("ID_FS_LABEL_ENC=%s\n", label_enc); 290 printf("ID_FS_LABEL_SAFE=%s\n", label_safe); 291 break; 292 case PRINT_TYPE: 293 printf("%s\n", type); 294 break; 295 case PRINT_LABEL: 296 if (label_safe[0] == '\0' || strcmp(usage, "raid") == 0) { 297 rc = 3; 298 goto exit; 299 } 300 printf("%s\n", label_safe); 301 break; 302 case PRINT_UUID: 303 if (uuid_enc[0] == '\0' || strcmp(usage, "raid") == 0) { 304 rc = 4; 305 goto exit; 306 } 307 printf("%s\n", uuid_enc); 308 break; 309 case PRINT_LABEL_RAW: 310 if (label[0] == '\0' || strcmp(usage, "raid") == 0) { 311 rc = 3; 312 goto exit; 313 } 314 printf("%s\n", label); 315 break; 316 } 317 318exit: 319 if (vid != NULL) 320 volume_id_close(vid); 321 322 logging_close(); 323 return rc; 324} 325