1/* 2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <stdio.h> 25#include <string.h> 26#include <stdlib.h> 27#include <limits.h> 28#include <errno.h> 29#include <err.h> 30#include <fcntl.h> 31#include <pwd.h> 32#include <grp.h> 33#include <unistd.h> 34#include <zlib.h> 35#include <sys/types.h> 36#include <sys/stat.h> 37#include <sys/disk.h> 38 39#include <CoreFoundation/CoreFoundation.h> 40#include <Kernel/libkern/OSByteOrder.h> 41 42#include <IOKit/storage/IOAppleLabelScheme.h> 43 44#include "util.h" 45 46#define MIN(a, b) \ 47 ({ typeof (a) _x = (a); typeof (b) _y = (b); \ 48 _x < _y ? _x : _y; }) 49 50/* 51 * When setting properties, a value can be forced to be 52 * a string by putting it in quotes. e.g., foo="123" 53 * This function copies the string, and removes the quotes. 54 * If the string does not begin with a double-quote, it is 55 * simply duplicated and the duplicate returned; if it does 56 * begin with a double-quote, it MUST end with a double-quote, 57 * or NULL is returned. 58 * The string that is returned must be free()'d by the 59 * caller. 60 */ 61static char * 62RemoveQuotes(const char *s) { 63 char *retval; 64 int len; 65 66 if (*s != '"') 67 return strdup(s); 68 69 retval = strdup(s + 1); 70 len = strlen(retval); 71 if (len < 2 || retval[len - 1] != '"') { 72 warnx("Quoted string `%s' is mal-formed", s); 73 return NULL; 74 } 75 retval[len - 1] = 0; 76 return retval; 77} 78 79/* 80 * Do some sanity checks on the given device name 81 * (e.g., device-name=fred). Right now, we check to 82 * see if it has a '/' in it, or if the device already 83 * exists in /dev. Having a '/' is an error; already 84 * existing is a warning. 85 * In case of error, we return -1; in case of a warning, 86 * we print it out to stderr, but still return success (0). 87 */ 88static int 89CheckDeviceName(const char *name) { 90 int len = strlen(name); 91 char devname[len + 6]; 92 struct stat sbuf; 93 94 if (strchr(name, '/') != NULL) { 95 warnx("Device name `%s' is invalid: cannot contain a '/'", name); 96 return -1; 97 } 98 99 snprintf(devname, sizeof(devname), "/dev/%s", name); 100 if (stat(devname, &sbuf) != -1) { 101 warnx("Device name `%s' already exists in /dev", name); 102 } 103 104 // Any other checks to do? 105 return 0; 106} 107 108/* 109 * Do a basic sanity check on the user name ("user-uid=..."). 110 * If it's a string, and the user exists, set *uidp to it; 111 * if it's a number, set *uidp to it; 112 * we return a positive number in those case. 113 * Otherwise, print out an appropriate warning, and return -1. 114 */ 115static int 116CheckUserName(const char *name, int asString, uid_t *uidp) { 117 uid_t uid; 118 struct passwd *pwd; 119 120 if (!asString) { 121 char *endptr; 122 uid = strtoul(name, &endptr, 10); 123 if (*name != 0 && *endptr == 0) { 124 // It was a digit 125 pwd = getpwuid(uid); 126 if (pwd == NULL) { 127 warnx("UID %d does not exist on this system", uid); 128 return -1; 129 } 130 *uidp = uid; 131 return 1; 132 } 133 } 134 // Otherwise, treat it as a string 135 pwd = getpwnam(name); 136 if (pwd == NULL) { 137 warnx("User-name `%s' does not exist on this system", name); 138 return -1; 139 } 140 141 *uidp = pwd->pw_uid; 142 return 1; 143} 144 145/* 146 * Same thing as above, but for the group ("owner-group=..."). 147 */ 148static int 149CheckGroupName(const char *name, int asString, gid_t *gidp) { 150 gid_t uid; 151 struct group *grp; 152 153 if (!asString) { 154 char *endptr; 155 uid = strtoul(name, &endptr, 10); 156 if (*name != 0 && *endptr == 0) { 157 // It was a digit 158 grp = getgrgid(uid); 159 if (grp == NULL) { 160 warnx("GID %d does not exist on this system", uid); 161 return -1; 162 } 163 *gidp = uid; 164 return 1; 165 } 166 } 167 // Otherwise, treat it as a string 168 grp = getgrnam(name); 169 if (grp == NULL) { 170 warnx("Group-name `%s' does not exist on this system", name); 171 return -1; 172 } 173 174 *gidp = grp->gr_gid; 175 return 1; 176} 177 178/* 179 * As above, but check the device mode ("owner-mode=..."). 180 * Right now, it must be an integer, but it could conceivably 181 * be made into a string later (e.g., "owner-mode=rw-rw---", 182 * perhaps?). It returns 0 on success, and -1 on failure. 183 */ 184static int 185CheckDeviceMode(const char *mode, int asString) { 186 unsigned long m; 187 char *endptr; 188 189 if (asString) { 190 warnx("Device mode must be an integer"); 191 return -1; 192 } 193 194 m = strtoul(mode, &endptr, 0); 195 196 if (*mode != 0 && *endptr == 0) { 197 return 0; 198 } 199 warnx("Device mode `%s' is invalid", mode); 200 return -1; 201} 202 203/* 204 * A routine to get the applelabel structure. This will be at 205 * the beginning of the device. On error, it returns NULL; on 206 * success, it returns a pointer to allocated memory. It also 207 * swaps the values of the structure around to host order, so 208 * the caller does not need to. 209 */ 210static struct applelabel * 211getDeviceLabel(const char *dev) { 212 int fd; 213 struct applelabel *retval = NULL; 214 215 fd = open(dev, O_RDONLY); 216 if (fd == -1) { 217 warn("getDeviceLabel: unable to open device %s for reading", dev); 218 return NULL; 219 } 220 retval = malloc(sizeof(*retval)); 221 if (retval == NULL) { 222 warnx("getDeviceLabel: unable to allocate memory for device label"); 223 goto done; 224 } 225 if (read(fd, retval, sizeof(*retval)) != sizeof(*retval)) { 226 warnx("getDeviceLabel: unable to read a full label for device %s", dev); 227 free(retval); 228 retval = NULL; 229 goto done; 230 } 231 retval->al_magic = OSSwapBigToHostInt16(retval->al_magic); 232 retval->al_type = OSSwapBigToHostInt16(retval->al_type); 233 retval->al_flags = OSSwapBigToHostInt32(retval->al_flags); 234 retval->al_offset = OSSwapBigToHostInt64(retval->al_offset); 235 retval->al_size = OSSwapBigToHostInt32(retval->al_size); 236 retval->al_checksum = OSSwapBigToHostInt32(retval->al_checksum); 237 238done: 239 close(fd); 240 return retval; 241} 242 243/* 244 * The opposite of the above, this sets the device label. 245 * It returns -1 on failure. It assumes the various fields 246 * are in host-order, and swaps them around as appropriate. 247 */ 248static int 249setDeviceLabel(const char *dev, const struct applelabel *lbl) { 250 int fd; 251 int retval = -1; 252 struct applelabel tmp; 253 254 if (lbl == NULL) 255 return retval; 256 257 fd = open(dev, O_WRONLY); 258 if (fd == -1) { 259 warn("setDeviceLabel: unable to open device %s", dev); 260 return retval; 261 } 262 263 tmp = *lbl; 264 tmp.al_magic = OSSwapHostToBigInt16(tmp.al_magic); 265 tmp.al_type = OSSwapHostToBigInt16(tmp.al_type); 266 tmp.al_flags = OSSwapHostToBigInt32(tmp.al_flags); 267 tmp.al_offset = OSSwapHostToBigInt64(tmp.al_offset); 268 tmp.al_size = OSSwapHostToBigInt32(tmp.al_size); 269 tmp.al_checksum = OSSwapHostToBigInt32(tmp.al_checksum); 270 271 if (write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) { 272 warn("setDeviceLabel: unable to write a full label to device %s", dev); 273 goto done; 274 } 275 retval = 0; 276done: 277 close(fd); 278 return retval; 279} 280 281/* 282 * Apply the crc32 checksum function to the metadata. 283 * It needs to know the size so that it can fill out the 284 * pad area as needed. It returns 0 on error, although 285 * that's not great. 286 */ 287static uint32_t 288ChecksumData(CFDictionaryRef dict, int32_t size) { 289 uint32_t retval = 0; 290 unsigned char *bytes = NULL; 291 int len; 292 CFDataRef data = nil; 293 294 bytes = malloc(size); 295 if (bytes == NULL) { 296 warnx("ChecksumData: cannot allocate %d bytes of data\n", size); 297 return 0; 298 } 299 memset(bytes, 0, size); 300 301 data = CFPropertyListCreateXMLData(nil, (CFPropertyListRef)dict); 302 if (data == nil) { 303 warnx("ChecksumData: cannot create data from dictionary"); 304 goto done; 305 } 306 307 len = CFDataGetLength(data); 308 memcpy(bytes, (void*)CFDataGetBytePtr(data), len); 309 310 if (gDebug && gVerbose) { 311 fprintf(stderr, "ChecksumData: calling crc32(0, %p, %d)\n", bytes, size); 312 } 313 314 retval = crc32(0, bytes, size); 315 316done: 317 if (data) 318 CFRelease(data); 319 free(bytes); 320 321 return retval; 322} 323 324/* 325 * Given a C string property (e.g., "owner-uid=fred" or "owner-mode"), 326 * turn it into a CFStringRef key, and optionally CFStringRef or CFNumberRef value. 327 * Return 0 on error, 1 on success. 328 * If the input string has an '=' in it, and defines a value, and the input 329 * pointer valuePtr is not NULL, then it will set valuePtr to a CFTypeRef 330 * (either a CFStringRef or a CFNumberRef). If the value is being set, and 331 * is one of a few known properties, it will also do some basic sanity checking 332 * on the values (see the static functions above). 333 */ 334int 335parseProperty(const char *term, CFStringRef *namePtr, CFTypeRef *valuePtr) { 336 CFStringRef k = nil; 337 CFTypeRef v = nil; 338 char *tag; 339 char *value = strdup(term); 340 char *endptr; 341 unsigned int tInt = -1; 342 char forceInt = 0; 343 char forceString = 0; 344 345 tag = strsep(&value, "="); 346 347 if (gDebug > 1) 348 fprintf(stderr, "parseProperty: property `%s' = value `%s'\n", 349 tag, value); 350 k = CFStringCreateWithCString(nil, tag, kCFStringEncodingASCII); 351 if (value) { 352 if (*value == '"') { // force string 353 endptr = RemoveQuotes(value); 354 free(tag); 355 tag = value = endptr; 356 forceString = 1; 357 } 358 359 if (!strcmp(tag, "dev-name")) { 360 forceString = 1; // just in case 361 if (CheckDeviceName(value) == -1) { 362 goto bad; 363 } 364 } 365 if (!strcmp(tag, "owner-uid")) { 366 if (CheckUserName(value, forceString, &tInt) == -1) { 367 // An illegal name 368 goto bad; 369 } 370 } 371 if (!strcmp(tag, "owner-gid")) { 372 if (CheckGroupName(value, forceString, &tInt) == -1) { 373 // An illegal group 374 goto bad; 375 } 376 } 377 if (!strcmp(tag, "owner-mode")) { 378 if (CheckDeviceMode(value, forceString) == -1) { 379 // An illegal mode 380 goto bad; 381 } 382 } 383 384 if (tInt == -1) { 385 tInt = strtol(value, &endptr, 0); 386 } else { 387 forceInt = 1; 388 } 389 if (!forceInt && (forceString || (endptr == value || 390 (*endptr != 0 && *value != 0)))) { 391 if (gDebug) 392 fprintf(stderr, "Property `%s' has value `%s'\n", 393 tag, value); 394 v = CFStringCreateWithCString(nil, value, kCFStringEncodingASCII); 395 } else { 396 if (gDebug) 397 fprintf(stderr, "Property `%s' has int value %d\n", 398 tag, tInt); 399 v = CFNumberCreate(nil, kCFNumberSInt32Type, &tInt); 400 } 401 } 402 if (k) { 403 if (namePtr) { 404 *namePtr = k; 405 } else { 406 CFRelease(k); 407 } 408 } 409 if (v) { 410 if (valuePtr) { 411 *valuePtr = v; 412 } else { 413 CFRelease(v); 414 } 415 } 416 free(tag); 417 418 return 1; 419 420bad: 421 if (v) 422 CFRelease(v); 423 if (k) 424 CFRelease(k); 425 if (tag) 426 free(tag); 427 return 0; 428} 429 430/* 431 * Read the metadata from the device. It returns NULL on error, 432 * and a CFDictionaryRef on success. It opens up the device, gets 433 * the applelabel from it, and then uses that to read in the data. 434 */ 435CFDictionaryRef 436ReadMetadata(const char *dev) { 437 int fd; 438 struct stat sbuf; 439 int64_t off; 440 struct applelabel *lbl = NULL; 441 CFDictionaryRef retval = nil; 442 CFStringRef errStr; 443 CFDataRef data = nil; 444 void *buf = NULL; 445 int len; 446 447 if ((fd = open(dev, O_RDONLY)) == -1) { 448 warn("cannot open device %s", dev); 449 return nil; 450 } 451 lbl = getDeviceLabel(dev); 452 if (lbl == NULL) { 453 warnx("ReadMetadata: cannot get label for device %s", dev); 454 goto done; 455 } 456 off = lbl->al_offset; 457 len = lbl->al_size; 458 459 fstat(fd, &sbuf); 460 461 if (lseek(fd, off, SEEK_SET) == -1) { 462 warn("ReadMetadata: cannot seek to metadata offset for device %s", dev); 463 goto done; 464 } 465 466 if (gDebug) { 467 fprintf(stderr, "For device %s, metadata len (max) = %d\n", dev, len); 468 } 469 buf = malloc(len); 470 if (buf == NULL) { 471 warnx("cannot allocate %d bytes to read metadata for device %s", len, dev); 472 goto done; 473 } 474 read(fd, buf, len); 475 476 data = CFDataCreate(nil, buf, len); 477 if (data == nil) { 478 warnx("cannot create CFData instance of XML"); 479 goto done; 480 } 481 retval = (CFPropertyListRef)CFPropertyListCreateFromXMLData(nil, 482 data, 483 kCFPropertyListImmutable, &errStr); 484 485 if (retval == NULL) { 486 int l = CFStringGetLength(errStr); 487 char buf[l * 2]; 488 if (CFStringGetCString(errStr, buf, sizeof(buf), kCFStringEncodingASCII)) { 489 warnx("cannot create property list: %s", buf); 490 } 491 CFRelease(errStr); 492 } 493done: 494 if (buf) 495 free(buf); 496 if (data) 497 CFRelease(data); 498 close(fd); 499 if (lbl) 500 free(lbl); 501 502 return retval; 503} 504 505/* 506 * Used to create the initial metadata area. It sets up an 507 * applelabel structure at the beginning of the file, computes the 508 * initial checksum, and writes out the metadata. It returns -1 509 * on failure. 510 */ 511int 512InitialMetadata(const char *dev, CFDictionaryRef dict, uint64_t size) { 513 int fd; 514 int retval = -1; 515 uint32_t bs; 516 uint64_t dSize; 517 struct applelabel lbl = { { 0 } }; 518 519 if (gDebug && gVerbose) { 520 fprintf(stderr, "InitialMetadata(%s, dict, %qu)\n", dev, size); 521 } 522 523 fd = open(dev, O_RDWR); 524 if (fd == -1) { 525 warn("InitialMetadata: cannot open device file %s", dev); 526 return -1; 527 } 528 529 bs = GetBlockSize(dev); 530 if (bs == 0) { 531 warnx("InitialMetadata: cannot get block size for device %s", dev); 532 goto done; 533 } 534 535 dSize = GetDiskSize(dev); 536 if (dSize == 0) { 537 warnx("InitialMetadata: cannot get disk size for device %s", dev); 538 goto done; 539 } 540 541 if (dSize <= size) { 542 warnx("InitialMetadata: Disk device size (%qu) is not large enough " 543 "for metadata size (%qu) for device %s", 544 dSize, size, dev); 545 goto done; 546 } 547 548 lbl.al_magic = AL_MAGIC; 549 lbl.al_type = AL_TYPE_DEFAULT; 550 lbl.al_flags = AL_FLAG_DEFAULT; 551 lbl.al_offset = bs; // start at block #1 552 lbl.al_size = (uint32_t)size - bs; 553 lbl.al_checksum = ChecksumData(dict, lbl.al_size); 554 555 if (gDebug) { 556 fprintf(stderr, "lbl = {\n"); 557 fprintf(stderr, "\tal_magic = 0x%x\n", lbl.al_magic); 558 fprintf(stderr, "\tal_type = 0x%x\n", lbl.al_type); 559 fprintf(stderr, "\tal_flags = 0x%x\n", lbl.al_flags); 560 fprintf(stderr, "\tal_offset = %qu\n", lbl.al_offset); 561 fprintf(stderr, "\tal_size = %d\n", lbl.al_size); 562 fprintf(stderr, "\tal_checksum = 0x%x\n};\n", lbl.al_checksum); 563 } 564 565 if (setDeviceLabel(dev, &lbl) == -1) { 566 warnx("InitialMetadata: cannot write header for device %s", dev); 567 goto done; 568 } 569 570 if (WriteMetadata(dev, dict) == -1) { 571 warnx("InitialMetadata: cannot write metadata"); 572 goto done; 573 } 574 575 retval = 0; 576done: 577 close(fd); 578 return retval; 579} 580 581/* 582 * Write out the metadata to the file. It also recomputes 583 * the checksum. It returns -1 on error. 584 */ 585int 586WriteMetadata(const char *dev, CFDictionaryRef dict) { 587 int fd; 588 int64_t off; 589 int len; 590 CFDataRef data = nil; 591 int retval = -1; 592 void *bytes; 593 uint32_t cksum = 0; 594 struct applelabel *lbl; 595 uint32_t mSize; 596 597 lbl = getDeviceLabel(dev); 598 if (lbl == NULL) { 599 warnx("cannot get label for device %s", dev); 600 return -1; 601 } 602 603 mSize = GetMetadataSize(dev); 604 bytes = malloc(mSize); 605 memset(bytes, 0, mSize); 606 607 if (bytes == NULL) { 608 warnx("WriteMetadata: cannot allocate %u bytes\n", mSize); 609 return -1; 610 } 611 612 fd = open(dev, O_RDWR); 613 if (fd == -1) { 614 warn("cannot open device %s", dev); 615 return -1; 616 } 617 if (GetDiskSize(dev) < lbl->al_size) { 618 warnx("device %s is too small for metadata size", dev); 619 return -1; 620 } 621 622 off = lbl->al_offset; 623 624 if (lseek(fd, off, SEEK_SET) == -1) { 625 warn("WriteMetadata: cannot seek to offset %qu for device %s", off, dev); 626 goto done; 627 } 628 629 data = CFPropertyListCreateXMLData(nil, (CFPropertyListRef)dict); 630 if (data == nil) { 631 warnx("cannot create CFData from dictionary"); 632 goto done; 633 } 634 len = CFDataGetLength(data); 635 memcpy(bytes, CFDataGetBytePtr(data), len); 636 637 if (write(fd, bytes, mSize) != mSize) { 638 warn("cannot write %d bytes to metadata area", mSize); 639 goto done; 640 } 641 642 cksum = ChecksumData(dict, mSize); 643 644 lbl->al_checksum = cksum; 645 if (setDeviceLabel(dev, lbl) == -1) { 646 warnx("unable to update label for device %s", dev); 647 goto done; 648 } 649 650 retval = 1; 651done: 652 close(fd); 653 if (data) 654 CFRelease(data); 655 return retval; 656} 657 658/* 659 * Return the checksum of the metadata area *from the disk*. 660 * It returns 0 on error (not great, I know). 661 */ 662uint32_t 663ChecksumMetadata(const char *dev) { 664 int fd = -1; 665 struct stat sbuf; 666 int64_t off; 667 int32_t cksum = 0; 668 unsigned char *buf = NULL; 669 int len; 670 struct applelabel *lbl = NULL; 671 672 if ((fd = open(dev, O_RDONLY)) == -1) { 673 warn("cannot open device %s", dev); 674 return 0; 675 } 676 677 lbl = getDeviceLabel(dev); 678 if (lbl == NULL) { 679 warnx("ChecksumMetadata: cannot get label for device %s", dev); 680 goto done; 681 } 682 fstat(fd, &sbuf); 683 off = lbl->al_offset; 684 len = lbl->al_size; 685 686 if (lseek(fd, off, SEEK_SET) == -1) { 687 warn("ChecksumMetadata: cannot seek to %qu for device %s", off, dev); 688 goto done; 689 } 690 691 if (gDebug) { 692 fprintf(stderr, "ChecksumMetadata: For device %s, metadata len (max) = %d\n", dev, len); 693 } 694 buf = malloc(len); 695 if (buf == NULL) { 696 warnx("ChecksumMetadata: cannot allocate %d bytes to read metadata for device %s", len, dev); 697 goto done; 698 } 699 read(fd, buf, len); 700 701 if (gDebug && gVerbose) { 702 fprintf(stderr, "ChecksumMetadata: calling crc32(0, %p, %d)\n", buf, len); 703 } 704 705 cksum = crc32(0, buf, len); 706 707 if (gDebug) { 708 fprintf(stderr, "ChecksumMetadata: For device %s, checksum = %u\n", dev, cksum); 709 } 710 711done: 712 if (buf) 713 free(buf); 714 close(fd); 715 if (lbl) 716 free(lbl); 717 718 return cksum; 719} 720 721/* 722 * Update the applelabel's value of the checksum to the indicated 723 * value. It returns -1 on error. 724 */ 725int 726UpdateChecksum(const char *dev, uint32_t sum) { 727 struct applelabel *lbl; 728 729 lbl = getDeviceLabel(dev); 730 if (lbl == NULL) { 731 warnx("UpdateChecksum: cannot get label for device %s", dev); 732 return -1; 733 } 734 735 lbl->al_checksum = sum; 736 if (setDeviceLabel(dev, lbl) == -1) { 737 warnx("UpdateChecksum: cannot update label for device %s", dev); 738 return -1; 739 } 740 free(lbl); 741 742 return 0; 743} 744 745/* 746 * A simpler version to get teh checksum, this returns 747 * the value that is in the applelabel structure. 748 */ 749uint32_t 750GetChecksum(const char *dev) { 751 struct applelabel *lbl; 752 uint32_t retval = 0; 753 754 lbl = getDeviceLabel(dev); 755 if (lbl == NULL) { 756 warnx("GetChecksum: cannot get label for device %s", dev); 757 return retval; 758 } 759 retval = lbl->al_checksum; 760 free(lbl); 761 return retval; 762} 763 764/* 765 * Compare the applelabel structure's copy of the checksum against 766 * a computed value of the checksum. Return the difference -- it should 767 * be 0, obviously, on success! 768 */ 769int 770VerifyChecksum(const char *dev) { 771 int32_t written, data; 772 773 if ((data = ChecksumMetadata(dev)) < 1) { 774 warnx("VerifyChecksum: Metadata checksum is 0x%x", data); 775 } 776 if ((written = GetChecksum(dev)) < 1) { 777 warnx("VerifyChecksum: Metadata written checksum is 0x%x", written); 778 } 779 return data - written; 780} 781 782/* 783 * Return the size of the metadata area, as defined by 784 * the applelabel structure. 785 */ 786uint32_t 787GetMetadataSize(const char *dev) { 788 struct applelabel *lbl = NULL; 789 uint32_t retval; 790 lbl = getDeviceLabel(dev); 791 if (lbl == NULL) { 792 warnx("GetMetadataSize: cannot get label for device %s", dev); 793 return 0; 794 } 795 796 retval = lbl->al_size; 797 free(lbl); 798 return retval; 799} 800 801/* 802 * Get the device label (if possible), and see if it's 803 * got the right magic value. If so, return 1; otherwise, 804 * return 0; 805 */ 806int 807IsAppleLabel(const char *dev) { 808 struct applelabel *lbl = NULL; 809 int retval = 0; 810 811 lbl = getDeviceLabel(dev); 812 if (lbl == NULL) { 813 warn("IsAppleLabel: cannot get label for device %s", dev); 814 return 0; 815 } 816 817 retval = lbl->al_magic == AL_MAGIC; 818 free(lbl); 819 return retval; 820} 821 822/* 823 * Return the blocksize for the requested device. 824 * If the device is not a disk, we return an error (0), 825 * UNLESS debugging is turned on (gDebug, -D command line option). 826 * In that case, if we can't get teh block size for the "device," 827 * we assume 4k. 828 */ 829uint32_t 830GetBlockSize(const char *dev) { 831 uint32_t retval = 0; 832 int fd; 833 834 fd = open(dev, O_RDONLY); 835 if (fd == -1) { 836 warn("BlockSize: cannot open %s", dev); 837 return 0; 838 } 839 if (ioctl(fd, DKIOCGETBLOCKSIZE, &retval) == -1) { 840 if (gDebug) { 841 retval = 4 * 1024; // a default blocksize 842 } else { 843 retval = 0; 844 warn("BlockSize: cannot get blocksize for device %s", dev); 845 } 846 } 847 close(fd); 848 return retval; 849} 850 851/* 852 * Similar to above, but it returns the size (in bytes) 853 * of the device. If debugging is enabled, and it is 854 * a file, it just returns the size of the file. 855 */ 856uint64_t 857GetDiskSize(const char *dev) { 858 int bs; 859 uint64_t bc; 860 int fd; 861 uint64_t retval = 0; 862 863 fd = open(dev, O_RDONLY); 864 if (fd == -1) { 865 warn("GetDiskSize: cannot open %s", dev); 866 return 0; 867 } 868 bs = GetBlockSize(dev); 869 if (bs == 0) { 870 return 0; 871 } 872 873 if (ioctl(fd, DKIOCGETBLOCKCOUNT, &bc) == -1) { 874 if (gDebug) { 875 struct stat sbuf; 876 if (fstat(fd, &sbuf) == -1) { 877 warn("GetDiskSize: cannot fstat %s", dev); 878 } else { 879 retval = sbuf.st_size; 880 } 881 } else { 882 warn("GetDiskSize: cannot get block count for device %s", dev); 883 } 884 } else { 885 retval = bs * bc; 886 } 887 close(fd); 888 return retval; 889} 890 891