1/* 2 * volume_id - reads filesystem label and uuid 3 * 4 * Copyright (C) 2005-2007 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 11#ifndef _GNU_SOURCE 12#define _GNU_SOURCE 1 13#endif 14 15#ifdef HAVE_CONFIG_H 16# include <config.h> 17#endif 18 19#include <stdio.h> 20#include <stdlib.h> 21#include <unistd.h> 22#include <string.h> 23#include <errno.h> 24#include <ctype.h> 25#include <fcntl.h> 26#include <sys/stat.h> 27 28#include "libvolume_id.h" 29#include "util.h" 30 31/* count of characters used to encode one unicode char */ 32static int utf8_encoded_expected_len(const char *str) 33{ 34 unsigned char c = (unsigned char)str[0]; 35 36 if (c < 0x80) 37 return 1; 38 if ((c & 0xe0) == 0xc0) 39 return 2; 40 if ((c & 0xf0) == 0xe0) 41 return 3; 42 if ((c & 0xf8) == 0xf0) 43 return 4; 44 if ((c & 0xfc) == 0xf8) 45 return 5; 46 if ((c & 0xfe) == 0xfc) 47 return 6; 48 return 0; 49} 50 51/* decode one unicode char */ 52static int utf8_encoded_to_unichar(const char *str) 53{ 54 int unichar; 55 int len; 56 int i; 57 58 len = utf8_encoded_expected_len(str); 59 switch (len) { 60 case 1: 61 return (int)str[0]; 62 case 2: 63 unichar = str[0] & 0x1f; 64 break; 65 case 3: 66 unichar = (int)str[0] & 0x0f; 67 break; 68 case 4: 69 unichar = (int)str[0] & 0x07; 70 break; 71 case 5: 72 unichar = (int)str[0] & 0x03; 73 break; 74 case 6: 75 unichar = (int)str[0] & 0x01; 76 break; 77 default: 78 return -1; 79 } 80 81 for (i = 1; i < len; i++) { 82 if (((int)str[i] & 0xc0) != 0x80) 83 return -1; 84 unichar <<= 6; 85 unichar |= (int)str[i] & 0x3f; 86 } 87 88 return unichar; 89} 90 91/* expected size used to encode one unicode char */ 92static int utf8_unichar_to_encoded_len(int unichar) 93{ 94 if (unichar < 0x80) 95 return 1; 96 if (unichar < 0x800) 97 return 2; 98 if (unichar < 0x10000) 99 return 3; 100 if (unichar < 0x200000) 101 return 4; 102 if (unichar < 0x4000000) 103 return 5; 104 return 6; 105} 106 107/* check if unicode char has a valid numeric range */ 108static int utf8_unichar_valid_range(int unichar) 109{ 110 if (unichar > 0x10ffff) 111 return 0; 112 if ((unichar & 0xfffff800) == 0xd800) 113 return 0; 114 if ((unichar > 0xfdcf) && (unichar < 0xfdf0)) 115 return 0; 116 if ((unichar & 0xffff) == 0xffff) 117 return 0; 118 return 1; 119} 120 121/* validate one encoded unicode char and return its length */ 122int volume_id_utf8_encoded_valid_unichar(const char *str) 123{ 124 int len; 125 int unichar; 126 int i; 127 128 len = utf8_encoded_expected_len(str); 129 if (len == 0) 130 return -1; 131 132 /* ascii is valid */ 133 if (len == 1) 134 return 1; 135 136 /* check if expected encoded chars are available */ 137 for (i = 0; i < len; i++) 138 if ((str[i] & 0x80) != 0x80) 139 return -1; 140 141 unichar = utf8_encoded_to_unichar(str); 142 143 /* check if encoded length matches encoded value */ 144 if (utf8_unichar_to_encoded_len(unichar) != len) 145 return -1; 146 147 /* check if value has valid range */ 148 if (!utf8_unichar_valid_range(unichar)) 149 return -1; 150 151 return len; 152} 153 154size_t volume_id_set_unicode16(uint8_t *str, size_t len, const uint8_t *buf, enum endian endianess, size_t count) 155{ 156 size_t i, j; 157 uint16_t c; 158 159 j = 0; 160 for (i = 0; i + 2 <= count; i += 2) { 161 if (endianess == LE) 162 c = (buf[i+1] << 8) | buf[i]; 163 else 164 c = (buf[i] << 8) | buf[i+1]; 165 if (c == 0) { 166 str[j] = '\0'; 167 break; 168 } else if (c < 0x80) { 169 if (j+1 >= len) 170 break; 171 str[j++] = (uint8_t) c; 172 } else if (c < 0x800) { 173 if (j+2 >= len) 174 break; 175 str[j++] = (uint8_t) (0xc0 | (c >> 6)); 176 str[j++] = (uint8_t) (0x80 | (c & 0x3f)); 177 } else { 178 if (j+3 >= len) 179 break; 180 str[j++] = (uint8_t) (0xe0 | (c >> 12)); 181 str[j++] = (uint8_t) (0x80 | ((c >> 6) & 0x3f)); 182 str[j++] = (uint8_t) (0x80 | (c & 0x3f)); 183 } 184 } 185 str[j] = '\0'; 186 return j; 187} 188 189static char *usage_to_string(enum volume_id_usage usage_id) 190{ 191 switch (usage_id) { 192 case VOLUME_ID_FILESYSTEM: 193 return "filesystem"; 194 case VOLUME_ID_OTHER: 195 return "other"; 196 case VOLUME_ID_RAID: 197 return "raid"; 198 case VOLUME_ID_DISKLABEL: 199 return "disklabel"; 200 case VOLUME_ID_CRYPTO: 201 return "crypto"; 202 case VOLUME_ID_UNPROBED: 203 return "unprobed"; 204 case VOLUME_ID_UNUSED: 205 return "unused"; 206 } 207 return NULL; 208} 209 210void volume_id_set_usage(struct volume_id *id, enum volume_id_usage usage_id) 211{ 212 id->usage_id = usage_id; 213 id->usage = usage_to_string(usage_id); 214} 215 216void volume_id_set_label_raw(struct volume_id *id, const uint8_t *buf, size_t count) 217{ 218 if (count > sizeof(id->label)) 219 count = sizeof(id->label); 220 221 memcpy(id->label_raw, buf, count); 222 id->label_raw_len = count; 223} 224 225void volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t count) 226{ 227 size_t i; 228 229 if (count >= sizeof(id->label)) 230 count = sizeof(id->label)-1; 231 232 memcpy(id->label, buf, count); 233 id->label[count] = '\0'; 234 235 /* remove trailing whitespace */ 236 i = strnlen(id->label, count); 237 while (i--) { 238 if (!isspace(id->label[i])) 239 break; 240 } 241 id->label[i+1] = '\0'; 242} 243 244void volume_id_set_label_unicode16(struct volume_id *id, const uint8_t *buf, enum endian endianess, size_t count) 245{ 246 if (count >= sizeof(id->label)) 247 count = sizeof(id->label)-1; 248 249 volume_id_set_unicode16((uint8_t *)id->label, sizeof(id->label), buf, endianess, count); 250} 251 252void volume_id_set_uuid(struct volume_id *id, const uint8_t *buf, size_t len, enum uuid_format format) 253{ 254 unsigned int i; 255 unsigned int count = 0; 256 257 if (len > sizeof(id->uuid_raw)) 258 len = sizeof(id->uuid_raw); 259 260 switch(format) { 261 case UUID_STRING: 262 count = len; 263 break; 264 case UUID_HEX_STRING: 265 count = len; 266 break; 267 case UUID_DOS: 268 count = 4; 269 break; 270 case UUID_64BIT_LE: 271 case UUID_64BIT_BE: 272 count = 8; 273 break; 274 case UUID_DCE: 275 count = 16; 276 break; 277 case UUID_FOURINT: 278 count = 35; 279 break; 280 } 281 memcpy(id->uuid_raw, buf, count); 282 id->uuid_raw_len = count; 283 284 /* if set, create string in the same format, the native platform uses */ 285 for (i = 0; i < count; i++) 286 if (buf[i] != 0) 287 goto set; 288 return; 289 290set: 291 switch(format) { 292 case UUID_DOS: 293 sprintf(id->uuid, "%02X%02X-%02X%02X", 294 buf[3], buf[2], buf[1], buf[0]); 295 break; 296 case UUID_64BIT_LE: 297 sprintf(id->uuid,"%02X%02X%02X%02X%02X%02X%02X%02X", 298 buf[7], buf[6], buf[5], buf[4], 299 buf[3], buf[2], buf[1], buf[0]); 300 break; 301 case UUID_64BIT_BE: 302 sprintf(id->uuid,"%02X%02X%02X%02X%02X%02X%02X%02X", 303 buf[0], buf[1], buf[2], buf[3], 304 buf[4], buf[5], buf[6], buf[7]); 305 break; 306 case UUID_DCE: 307 sprintf(id->uuid, 308 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", 309 buf[0], buf[1], buf[2], buf[3], 310 buf[4], buf[5], 311 buf[6], buf[7], 312 buf[8], buf[9], 313 buf[10], buf[11], buf[12], buf[13], buf[14],buf[15]); 314 break; 315 case UUID_HEX_STRING: 316 /* translate A..F to a..f */ 317 memcpy(id->uuid, buf, count); 318 for (i = 0; i < count; i++) 319 if (id->uuid[i] >= 'A' && id->uuid[i] <= 'F') 320 id->uuid[i] = (id->uuid[i] - 'A') + 'a'; 321 id->uuid[count] = '\0'; 322 break; 323 case UUID_STRING: 324 memcpy(id->uuid, buf, count); 325 id->uuid[count] = '\0'; 326 break; 327 case UUID_FOURINT: 328 sprintf(id->uuid, 329 "%02x%02x%02x%02x:%02x%02x%02x%02x:%02x%02x%02x%02x:%02x%02x%02x%02x", 330 buf[0], buf[1], buf[2], buf[3], 331 buf[4], buf[5], buf[6], buf[7], 332 buf[8], buf[9], buf[10], buf[11], 333 buf[12], buf[13], buf[14],buf[15]); 334 break; 335 } 336} 337 338uint8_t *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len) 339{ 340 ssize_t buf_len; 341 342 info("get buffer off 0x%llx(%llu), len 0x%zx", (unsigned long long) off, (unsigned long long) off, len); 343 /* check if requested area fits in superblock buffer */ 344 if (off + len <= SB_BUFFER_SIZE) { 345 if (id->sbbuf == NULL) { 346 id->sbbuf = malloc(SB_BUFFER_SIZE); 347 if (id->sbbuf == NULL) { 348 dbg("error malloc"); 349 return NULL; 350 } 351 } 352 353 /* check if we need to read */ 354 if ((off + len) > id->sbbuf_len) { 355 info("read sbbuf len:0x%llx", (unsigned long long) (off + len)); 356 if (lseek(id->fd, 0, SEEK_SET) < 0) { 357 dbg("lseek failed (%s)", strerror(errno)); 358 return NULL; 359 } 360 buf_len = read(id->fd, id->sbbuf, off + len); 361 if (buf_len < 0) { 362 dbg("read failed (%s)", strerror(errno)); 363 return NULL; 364 } 365 dbg("got 0x%zx (%zi) bytes", buf_len, buf_len); 366 id->sbbuf_len = buf_len; 367 if ((size_t)buf_len < off + len) { 368 dbg("requested 0x%zx bytes, got only 0x%zx bytes", len, buf_len); 369 return NULL; 370 } 371 } 372 373 return &(id->sbbuf[off]); 374 } else { 375 if (len > SEEK_BUFFER_SIZE) { 376 dbg("seek buffer too small %d", SEEK_BUFFER_SIZE); 377 return NULL; 378 } 379 380 /* get seek buffer */ 381 if (id->seekbuf == NULL) { 382 id->seekbuf = malloc(SEEK_BUFFER_SIZE); 383 if (id->seekbuf == NULL) { 384 dbg("error malloc"); 385 return NULL; 386 } 387 } 388 389 /* check if we need to read */ 390 if ((off < id->seekbuf_off) || ((off + len) > (id->seekbuf_off + id->seekbuf_len))) { 391 info("read seekbuf off:0x%llx len:0x%zx", (unsigned long long) off, len); 392 if (lseek(id->fd, off, SEEK_SET) < 0) { 393 dbg("lseek failed (%s)", strerror(errno)); 394 return NULL; 395 } 396 buf_len = read(id->fd, id->seekbuf, len); 397 if (buf_len < 0) { 398 dbg("read failed (%s)", strerror(errno)); 399 return NULL; 400 } 401 dbg("got 0x%zx (%zi) bytes", buf_len, buf_len); 402 id->seekbuf_off = off; 403 id->seekbuf_len = buf_len; 404 if ((size_t)buf_len < len) { 405 dbg("requested 0x%zx bytes, got only 0x%zx bytes", len, buf_len); 406 return NULL; 407 } 408 } 409 410 return &(id->seekbuf[off - id->seekbuf_off]); 411 } 412} 413 414void volume_id_free_buffer(struct volume_id *id) 415{ 416 if (id->sbbuf != NULL) { 417 free(id->sbbuf); 418 id->sbbuf = NULL; 419 id->sbbuf_len = 0; 420 } 421 if (id->seekbuf != NULL) { 422 free(id->seekbuf); 423 id->seekbuf = NULL; 424 id->seekbuf_len = 0; 425 } 426} 427