1/* 2 * Copyright (c) 1999-2014 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.2 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22/* 23 Copyright (c) 2002 Apple Computer, Inc. 24 All Rights Reserved. 25 26 This file contains the routine to make an HFS+ volume journaled 27 and a corresponding routine to turn it off. 28 29 */ 30 31#include <sys/types.h> 32#include <sys/attr.h> 33#include <sys/stat.h> 34#include <sys/time.h> 35#include <sys/sysctl.h> 36#include <sys/resource.h> 37#include <sys/vmmeter.h> 38#include <sys/mount.h> 39#include <sys/wait.h> 40#include <sys/ioctl.h> 41 42#include <sys/disk.h> 43#include <sys/loadable_fs.h> 44#include <hfs/hfs_format.h> 45#include <hfs/hfs_mount.h> /* for hfs sysctl values */ 46 47#include <System/hfs/hfs_fsctl.h> 48 49#include <System/sys/content_protection.h> 50#include <TargetConditionals.h> 51 52#include <errno.h> 53#include <fcntl.h> 54#include <libgen.h> 55#include <pwd.h> 56#include <stdio.h> 57#include <stdlib.h> 58#include <string.h> 59#include <unistd.h> 60 61#include <architecture/byte_order.h> 62 63// just in case these aren't in <hfs/hfs_mount.h> yet 64#ifndef HFS_ENABLE_JOURNALING 65#define HFS_ENABLE_JOURNALING 0x082969 66#endif 67#ifndef HFS_DISABLE_JOURNALING 68#define HFS_DISABLE_JOURNALING 0x031272 69#endif 70#ifndef HFS_GET_JOURNAL_INFO 71#define HFS_GET_JOURNAL_INFO 0x6a6e6c69 72#endif 73 74/* getattrlist buffers start with an extra length field */ 75struct ExtentsAttrBuf { 76 unsigned long infoLength; 77 HFSPlusExtentRecord extents; 78}; 79typedef struct ExtentsAttrBuf ExtentsAttrBuf; 80 81#ifndef HFSIOC_GET_JOURNAL_INFO 82# include <sys/ioctl.h> 83struct hfs_journal_info { 84 off_t jstart; 85 off_t jsize; 86}; 87# define HFSIOC_GET_JOURNAL_INFO _IOR('h', 17, struct hfs_journal_info) 88#endif 89 90 91#define kIsInvisible 0x4000 92 93/* 94 * Generic Finder file/dir data 95 */ 96struct FinderInfo { 97 u_int32_t opaque_1[2]; 98 u_int16_t fdFlags; /* Finder flags */ 99 int16_t opaque_2[11]; 100}; 101typedef struct FinderInfo FinderInfo; 102 103/* getattrlist buffers start with an extra length field */ 104struct FinderAttrBuf { 105 unsigned long infoLength; 106 FinderInfo finderInfo; 107}; 108typedef struct FinderAttrBuf FinderAttrBuf; 109 110 111int hide_file(const char * file) 112{ 113 struct attrlist alist = {0}; 114 FinderAttrBuf finderInfoBuf = {0}; 115 int result; 116 117 alist.bitmapcount = ATTR_BIT_MAP_COUNT; 118 alist.commonattr = ATTR_CMN_FNDRINFO; 119 120 result = getattrlist(file, &alist, &finderInfoBuf, sizeof(finderInfoBuf), 0); 121 if (result) { 122 return (errno); 123 } 124 125 if (finderInfoBuf.finderInfo.fdFlags & kIsInvisible) { 126 printf("hide: %s is alreadly invisible\n", file); 127 return (0); 128 } 129 130 finderInfoBuf.finderInfo.fdFlags |= kIsInvisible; 131 132 result = setattrlist(file, &alist, &finderInfoBuf.finderInfo, sizeof(FinderInfo), 0); 133 134 return (result == -1 ? errno : result); 135} 136 137off_t 138get_start_block(const char *file, uint32_t fs_block_size) 139{ 140 off_t cur_pos, phys_start, len; 141 int fd, err; 142 struct log2phys l2p; 143 struct stat st; 144 145 fd = open(file, O_RDONLY); 146 if (fd < 0) { 147 return -1; 148 } 149 150 if (fstat(fd, &st) < 0) { 151 fprintf(stderr, "can't stat %s (%s)\n", file, strerror(errno)); 152 close(fd); 153 return -1; 154 } 155 156 fs_block_size = st.st_blksize; // XXXdbg quick hack for now 157 158 phys_start = len = 0; 159 for(cur_pos=0; cur_pos < st.st_size; cur_pos += fs_block_size) { 160 memset(&l2p, 0, sizeof(l2p)); 161 lseek(fd, cur_pos, SEEK_SET); 162 err = fcntl(fd, F_LOG2PHYS, &l2p); 163 164 if (phys_start == 0) { 165 phys_start = l2p.l2p_devoffset; 166 len = fs_block_size; 167 } else if (l2p.l2p_devoffset != (phys_start + len)) { 168 // printf(" %lld : %lld - %lld\n", cur_pos, phys_start / fs_block_size, len / fs_block_size); 169 fprintf(stderr, "%s : is not contiguous!\n", file); 170 close(fd); 171 return -1; 172 // phys_start = l2p.l2p_devoffset; 173 // len = fs_block_size; 174 } else { 175 len += fs_block_size; 176 } 177 } 178 179 close(fd); 180 181 //printf("%s start offset %lld; byte len %lld (blksize %d)\n", 182 // file, phys_start, len, fs_block_size); 183 184 if ((phys_start / (unsigned int)fs_block_size) & 0xffffffff00000000LL) { 185 fprintf(stderr, "%s : starting block is > 32bits!\n", file); 186 return -1; 187 } 188 189 return phys_start; 190} 191 192 193// 194// Get the embedded offset (if any) for an hfs+ volume. 195// This is pretty skanky that we have to do this but 196// that's life... 197// 198#include <sys/disk.h> 199#include <hfs/hfs_format.h> 200 201#include <machine/endian.h> 202 203#define HFS_PRI_SECTOR(blksize) (1024 / (blksize)) 204#define HFS_PRI_OFFSET(blksize) ((blksize) > 1024 ? 1024 : 0) 205 206#include <libkern/OSByteOrder.h> 207 208#define SWAP_BE16(x) ntohs(x) 209#define SWAP_BE32(x) ntohl(x) 210#define SWAP_BE64(x) OSSwapConstInt64(x) 211 212 213off_t 214get_embedded_offset(char *devname) 215{ 216 int fd = -1; 217 off_t ret = 0; 218 char *buff = NULL, rawdev[256]; 219 u_int64_t blkcnt; 220 u_int32_t blksize; 221 HFSMasterDirectoryBlock *mdbp; 222 off_t embeddedOffset; 223 struct statfs sfs; 224 struct stat st; 225 226 restart: 227 if (stat(devname, &st) != 0) { 228 fprintf(stderr, "Could not access %s (%s)\n", devname, strerror(errno)); 229 ret = -1; 230 goto out; 231 } 232 233 if (S_ISCHR(st.st_mode) == 0) { 234 // hmmm, it's not the character special raw device so we 235 // should try to figure out the real device. 236 if (statfs(devname, &sfs) != 0) { 237 fprintf(stderr, "Can't find out any info about the fs for path %s (%s)\n", 238 devname, strerror(errno)); 239 ret = -1; 240 goto out; 241 } 242 243 // This assumes it begins with "/dev/". The old code assumed 244 // it began with five characters. Should probably use strrchr or equivalent. 245 snprintf(rawdev, sizeof(rawdev), "/dev/r%s", &sfs.f_mntfromname[5]); 246 devname = &rawdev[0]; 247 goto restart; 248 } 249 250 fd = open(devname, O_RDONLY); 251 if (fd < 0) { 252 fprintf(stderr, "can't open: %s (%s)\n", devname, strerror(errno)); 253 ret = -1; 254 goto out; 255 } 256 257 /* Get the real physical block size. */ 258 if (ioctl(fd, DKIOCGETBLOCKSIZE, (caddr_t)&blksize) != 0) { 259 fprintf(stderr, "can't get the device block size (%s). assuming 512\n", strerror(errno)); 260 blksize = 512; 261 ret = -1; 262 goto out; 263 } 264 265 /* Get the number of physical blocks. */ 266 if (ioctl(fd, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt)) { 267 struct stat st; 268 fprintf(stderr, "failed to get block count. trying stat().\n"); 269 if (fstat(fd, &st) != 0) { 270 ret = -1; 271 goto out; 272 } 273 274 blkcnt = st.st_size / blksize; 275 } 276 277 /* 278 * At this point: 279 * blksize has our prefered physical block size 280 * blkcnt has the total number of physical blocks 281 */ 282 283 buff = (char *)malloc(blksize); 284 285 if (pread(fd, buff, blksize, HFS_PRI_SECTOR(blksize)*blksize) != blksize) { 286 fprintf(stderr, "failed to read volume header @ offset %d (%s)\n", 287 HFS_PRI_SECTOR(blksize), strerror(errno)); 288 ret = -1; 289 goto out; 290 } 291 292 mdbp = (HFSMasterDirectoryBlock *)(buff + HFS_PRI_OFFSET(blksize)); 293 if ( (SWAP_BE16(mdbp->drSigWord) != kHFSSigWord) 294 && (SWAP_BE16(mdbp->drSigWord) != kHFSPlusSigWord) 295 && (SWAP_BE16(mdbp->drSigWord) != kHFSXSigWord)) { 296 printf ("get_embedded_offset: invalid volume signature \n"); 297 ret = -1; 298 goto out; 299 } 300 301 if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) && (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord)) { 302 ret = -1; 303 goto out; 304 } else if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) { 305 /* Get the embedded Volume Header */ 306 embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * 512; 307 embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) * 308 (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz); 309 310 /* 311 * If the embedded volume doesn't start on a block 312 * boundary, then switch the device to a 512-byte 313 * block size so everything will line up on a block 314 * boundary. 315 */ 316 if ((embeddedOffset % blksize) != 0) { 317 fprintf(stderr, "HFS Mount: embedded volume offset not" 318 " a multiple of physical block size (%d);" 319 " switching to 512\n", blksize); 320 321 blkcnt *= (blksize / 512); 322 blksize = 512; 323 } 324 325 } else { /* pure HFS+ */ 326 embeddedOffset = 0; 327 } 328 329 ret = embeddedOffset; 330 331 out: 332 if (buff) { 333 free(buff); 334 } 335 if (fd >= 0) 336 close(fd); 337 338 return ret; 339} 340 341 342 343static const char *journal_fname = ".journal"; 344static const char *jib_fname = ".journal_info_block"; 345 346int 347DoMakeJournaled(char *volname, int jsize) { 348 int fd, i, block_size, journal_size = 8*1024*1024; 349 char *buf; 350 int ret; 351 fstore_t fst; 352 int32_t jstart_block, jinfo_block; 353 int sysctl_info[8]; 354 JournalInfoBlock jib; 355 struct statfs sfs; 356 static char tmpname[MAXPATHLEN]; 357 off_t start_block, embedded_offset; 358 359 if (statfs(volname, &sfs) != 0) { 360 fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno)); 361 return 10; 362 } 363 364 // Make sure that we're HFS+. First we check the fstypename. 365 // If that's ok then we try to create a symlink (which won't 366 // work on plain hfs volumes but will work on hfs+ volumes). 367 // 368 if (strcmp(sfs.f_fstypename, "devfs") == 0) { 369 fprintf (stderr, "%s is a device node. Journal enable only works on a mounted HFS+ volume.\n", volname); 370 return 10; 371 } 372 snprintf(tmpname, sizeof(tmpname), "%s/is_vol_hfs_plus", volname); 373 if (strcmp(sfs.f_fstypename, "hfs") != 0 || 374 ((ret = symlink(tmpname, tmpname)) != 0 && errno == ENOTSUP)) { 375 fprintf(stderr, "%s is not an HFS+ volume. Journaling only works on HFS+ volumes.\n", 376 volname); 377 return 10; 378 } 379 unlink(tmpname); 380 381 if (sfs.f_flags & MNT_JOURNALED) { 382 fprintf(stderr, "Volume %s is already journaled.\n", volname); 383 return 1; 384 } 385 386 if (jsize != 0) { 387 journal_size = jsize; 388 } else { 389 int scale; 390 391 // 392 // we want at least 8 megs of journal for each 100 gigs of 393 // disk space. We cap the size at 512 megs though. 394 // 395 scale = ((long long)sfs.f_bsize * (long long)((unsigned int)sfs.f_blocks)) / (100*1024*1024*1024ULL); 396 journal_size *= (scale + 1); 397 if (journal_size > 512 * 1024 * 1024) { 398 journal_size = 512 * 1024 * 1024; 399 } 400 } 401 402 if (chdir(volname) != 0) { 403 fprintf(stderr, "Can't locate volume %s to make it journaled (%s).\n", 404 volname, strerror(errno)); 405 return 10; 406 } 407 408 409 embedded_offset = get_embedded_offset(volname); 410 if (embedded_offset < 0) { 411 fprintf(stderr, "Can't calculate the embedded offset (if any) for %s.\n", volname); 412 fprintf(stderr, "Journal creation failure.\n"); 413 return 15; 414 } 415 // printf("Embedded offset == 0x%llx\n", embedded_offset); 416 417#if TARGET_OS_EMBEDDED 418 /* 419 * Must use open_dprotected_np to create a class D file. This will 420 * be the same as standard open(2) on systems that do not support content protection 421 */ 422 fd = open_dprotected_np (journal_fname, O_CREAT|O_TRUNC|O_RDWR, PROTECTION_CLASS_D, 0, 000); 423#else 424 fd = open (journal_fname, O_CREAT|O_TRUNC|O_RDWR, 000); 425#endif 426 if (fd < 0) { 427 fprintf(stderr, "Can't create journal file on volume %s (%s)\n", 428 volname, strerror(errno)); 429 return 5; 430 } 431 432 if (fcntl(fd, F_NOCACHE, 1)) { 433 fprintf(stderr, "Can't create journal file (NC) on volume %s (%s)\n", 434 volname, strerror(errno)); 435 return 5; 436 } 437 438 439 // make sure that it has no r/w/x privs (only could happen if 440 // the file already existed since open() doesn't reset the mode 441 // bits). 442 // 443 fchmod(fd, 0); 444 445 block_size = sfs.f_bsize; 446 if ((journal_size % block_size) != 0) { 447 fprintf(stderr, "Journal size %dk is not a multiple of volume %s block size (%d).\n", 448 journal_size/1024, volname, block_size); 449 close(fd); 450 unlink(journal_fname); 451 return 5; 452 } 453 454retry: 455 memset(&fst, 0, sizeof(fst)); 456 fst.fst_flags = F_ALLOCATECONTIG|F_ALLOCATEALL; 457 fst.fst_length = journal_size; 458 fst.fst_posmode = F_PEOFPOSMODE; 459 460 ret = fcntl(fd, F_PREALLOCATE, &fst); 461 if (ret < 0) { 462 if (journal_size >= 2*1024*1024) { 463 fprintf(stderr, "Not enough contiguous space for a %d k journal. Retrying.\n", 464 journal_size/1024); 465 journal_size /= 2; 466 ftruncate(fd, 0); // make sure the file is zero bytes long. 467 goto retry; 468 } else { 469 fprintf(stderr, "Disk too fragmented to enable journaling.\n"); 470 fprintf(stderr, "Please run a defragmenter on %s.\n", volname); 471 close(fd); 472 unlink(journal_fname); 473 return 10; 474 } 475 } 476 477 printf("Allocated %lldK for journal file.\n", fst.fst_bytesalloc/1024LL); 478 buf = (char *)calloc(block_size, 1); 479 if (buf) { 480 for(i=0; i < journal_size/block_size; i++) { 481 ret = write(fd, buf, block_size); 482 if (ret != block_size) { 483 break; 484 } 485 } 486 487 if (i*block_size != journal_size) { 488 fprintf(stderr, "Failed to write %dk to journal on volume %s (%s)\n", 489 journal_size/1024, volname, strerror(errno)); 490 } 491 } else { 492 printf("Could not allocate memory to write to the journal on volume %s (%s)\n", 493 volname, strerror(errno)); 494 } 495 496 fsync(fd); 497 close(fd); 498 hide_file(journal_fname); 499 500 start_block = get_start_block(journal_fname, block_size); 501 if (start_block == (off_t)-1) { 502 fprintf(stderr, "Failed to get start block for %s (%s)\n", 503 journal_fname, strerror(errno)); 504 unlink(journal_fname); 505 return 20; 506 } 507 jstart_block = (start_block / block_size) - (embedded_offset / block_size); 508 509 memset(&jib, 'Z', sizeof(jib)); 510 jib.flags = kJIJournalInFSMask; 511 jib.offset = (off_t)((unsigned int)jstart_block) * (off_t)((unsigned int)block_size); 512 jib.size = (off_t)((unsigned int)journal_size); 513 514#if TARGET_OS_EMBEDDED 515 /* 516 * Use open_dprotected_np to create JIB as a class D file. This will 517 * behave the same as a standard open(2) on systems that do not support content protection 518 */ 519 fd = open_dprotected_np(jib_fname, O_CREAT|O_TRUNC|O_RDWR, PROTECTION_CLASS_D, 0, 000); 520#else 521 fd = open(jib_fname, O_CREAT|O_TRUNC|O_RDWR, 000); 522#endif 523 524 if (fd < 0) { 525 fprintf(stderr, "Could not create journal info block file on volume %s (%s)\n", 526 volname, strerror(errno)); 527 unlink(journal_fname); 528 return 5; 529 } 530 531 if (fcntl(fd, F_NOCACHE, 1)) { 532 fprintf(stderr, "Could not create journal info block (NC) file on volume %s (%s)\n", 533 volname, strerror(errno)); 534 return 5; 535 } 536 537 // swap the data before we copy it 538 jib.flags = OSSwapBigToHostInt32(jib.flags); 539 jib.offset = OSSwapBigToHostInt64(jib.offset); 540 jib.size = OSSwapBigToHostInt64(jib.size); 541 542 memcpy(buf, &jib, sizeof(jib)); 543 544 // now put it back the way it was 545 jib.size = OSSwapBigToHostInt64(jib.size); 546 jib.offset = OSSwapBigToHostInt64(jib.offset); 547 jib.flags = OSSwapBigToHostInt32(jib.flags); 548 549 if (write(fd, buf, block_size) != block_size) { 550 fprintf(stderr, "Failed to write journal info block on volume %s (%s)!\n", 551 volname, strerror(errno)); 552 unlink(journal_fname); 553 return 10; 554 } 555 556 fsync(fd); 557 close(fd); 558 hide_file(jib_fname); 559 560 start_block = get_start_block(jib_fname, block_size); 561 if (start_block == (off_t)-1) { 562 fprintf(stderr, "Failed to get start block for %s (%s)\n", 563 jib_fname, strerror(errno)); 564 unlink(journal_fname); 565 unlink(jib_fname); 566 return 20; 567 } 568 jinfo_block = (start_block / block_size) - (embedded_offset / block_size); 569 570 571 // 572 // Now make the volume journaled! 573 // 574 memset(sysctl_info, 0, sizeof(sysctl_info)); 575 sysctl_info[0] = CTL_VFS; 576 sysctl_info[1] = sfs.f_fsid.val[1]; 577 sysctl_info[2] = HFS_ENABLE_JOURNALING; 578 sysctl_info[3] = jinfo_block; 579 sysctl_info[4] = jstart_block; 580 sysctl_info[5] = journal_size; 581 582 //printf("fs type: 0x%x\n", sysctl_info[1]); 583 //printf("jinfo block : 0x%x\n", jinfo_block); 584 //printf("jstart block: 0x%x\n", jstart_block); 585 //printf("journal size: 0x%x\n", journal_size); 586 587 ret = sysctl((void *)sysctl_info, 6, NULL, NULL, NULL, 0); 588 if (ret != 0) { 589 fprintf(stderr, "Failed to make volume %s journaled (%s)\n", 590 volname, strerror(errno)); 591 unlink(journal_fname); 592 unlink(jib_fname); 593 return 20; 594 } 595 596 return 0; 597} 598 599 600int 601DoUnJournal(char *volname) { 602 int result; 603 int sysctl_info[8]; 604 struct statfs sfs; 605 char jbuf[MAXPATHLEN]; 606 607 if (statfs(volname, &sfs) != 0) { 608 fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno)); 609 return 10; 610 } 611 612 if (strcmp(sfs.f_fstypename, "hfs") != 0) { 613 fprintf(stderr, "Volume %s (%s) is not a HFS volume.\n", volname, sfs.f_mntfromname); 614 return 1; 615 } 616 617 if ((sfs.f_flags & MNT_JOURNALED) == 0) { 618 fprintf(stderr, "Volume %s (%s) is not journaled.\n", volname, sfs.f_mntfromname); 619 return 1; 620 } 621 622 if (chdir(volname) != 0) { 623 fprintf(stderr, "Can't locate volume %s to turn off journaling (%s).\n", 624 volname, strerror(errno)); 625 return 10; 626 } 627 628 memset(sysctl_info, 0, sizeof(sysctl_info)); 629 sysctl_info[0] = CTL_VFS; 630 sysctl_info[1] = sfs.f_fsid.val[1]; 631 sysctl_info[2] = HFS_DISABLE_JOURNALING; 632 633 result = sysctl((void *)sysctl_info, 3, NULL, NULL, NULL, 0); 634 if (result != 0) { 635 fprintf(stderr, "Failed to make volume %s UN-journaled (%s)\n", 636 volname, strerror(errno)); 637 return 20; 638 } 639 640 snprintf(jbuf, sizeof(jbuf), "%s/%s", volname, journal_fname); 641 if (unlink(jbuf) != 0) { 642 fprintf(stderr, "Failed to remove the journal %s (%s)\n", 643 jbuf, strerror(errno)); 644 } 645 646 snprintf(jbuf, sizeof(jbuf), "%s/%s", volname, jib_fname); 647 if (unlink(jbuf) != 0) { 648 fprintf(stderr, "Failed to remove the journal info block %s (%s)\n", 649 jbuf, strerror(errno)); 650 } 651 652 printf("Journaling disabled on %s mounted at %s.\n", sfs.f_mntfromname, volname); 653 654 return 0; 655} 656 657 658 659 660int 661get_journal_info(char *devname, struct JournalInfoBlock *jib) 662{ 663 int fd = -1, ret = 0; 664 char *buff = NULL, *buff2 = NULL; 665 u_int64_t disksize; 666 u_int64_t blkcnt; 667 u_int32_t blksize; 668 daddr_t mdb_offset; 669 HFSMasterDirectoryBlock *mdbp; 670 HFSPlusVolumeHeader *vhp; 671 off_t embeddedOffset, pos; 672 struct JournalInfoBlock *myjib; 673 674 fd = open(devname, O_RDONLY); 675 if (fd < 0) { 676 printf("can't open: %s (%s)\n", devname, strerror(errno)); 677 ret = -5; 678 goto out; 679 } 680 681 /* Get the real physical block size. */ 682 if (ioctl(fd, DKIOCGETBLOCKSIZE, (caddr_t)&blksize) != 0) { 683 printf("can't get the device block size (%s). assuming 512\n", strerror(errno)); 684 blksize = 512; 685 ret = -1; 686 goto out; 687 } 688 689 /* Get the number of physical blocks. */ 690 if (ioctl(fd, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt)) { 691 struct stat st; 692 printf("failed to get block count. trying stat().\n"); 693 if (fstat(fd, &st) != 0) { 694 ret = -1; 695 goto out; 696 } 697 698 blkcnt = st.st_size / blksize; 699 } 700 701 /* Compute an accurate disk size */ 702 disksize = blkcnt * (u_int64_t)blksize; 703 704 /* 705 * There are only 31 bits worth of block count in 706 * the buffer cache. So for large volumes a 4K 707 * physical block size is needed. 708 */ 709 if (blksize == 512 && blkcnt > (u_int64_t)0x000000007fffffff) { 710 blksize = 4096; 711 } 712 713 /* 714 * At this point: 715 * blksize has our prefered physical block size 716 * blkcnt has the total number of physical blocks 717 */ 718 719 buff = (char *)malloc(blksize); 720 buff2 = (char *)malloc(blksize); 721 722 if (pread(fd, buff, blksize, HFS_PRI_SECTOR(blksize)*blksize) != blksize) { 723 printf("failed to read volume header @ offset %d (%s)\n", 724 HFS_PRI_SECTOR(blksize), strerror(errno)); 725 ret = -1; 726 goto out; 727 } 728 729 mdbp = (HFSMasterDirectoryBlock *)(buff + HFS_PRI_OFFSET(blksize)); 730 731 if ( (SWAP_BE16(mdbp->drSigWord) != kHFSSigWord) 732 && (SWAP_BE16(mdbp->drSigWord) != kHFSPlusSigWord) 733 && (SWAP_BE16(mdbp->drSigWord) != kHFSXSigWord)) { 734 ret = -1; 735 printf("get_journal_info: invalid volume signature\n"); 736 goto out; 737 } 738 739 mdbp->drSigWord = SWAP_BE16(mdbp->drSigWord); 740 mdbp->drEmbedSigWord = SWAP_BE16(mdbp->drEmbedSigWord); 741 mdbp->drAlBlSt = SWAP_BE16(mdbp->drAlBlSt); 742 mdbp->drEmbedExtent.startBlock = SWAP_BE16(mdbp->drEmbedExtent.startBlock); 743 mdbp->drAlBlkSiz = SWAP_BE32(mdbp->drAlBlkSiz); 744 mdbp->drEmbedExtent.blockCount = SWAP_BE16(mdbp->drEmbedExtent.blockCount); 745 746 747 if ((mdbp->drSigWord == kHFSSigWord) && (mdbp->drEmbedSigWord != kHFSPlusSigWord)) { 748 // normal hfs can not ever be journaled 749 goto out; 750 } 751 752 /* Get the embedded Volume Header */ 753 if (mdbp->drEmbedSigWord == kHFSPlusSigWord) { 754 embeddedOffset = mdbp->drAlBlSt * 512; 755 embeddedOffset += (u_int64_t)mdbp->drEmbedExtent.startBlock * 756 (u_int64_t)mdbp->drAlBlkSiz; 757 758 /* 759 * If the embedded volume doesn't start on a block 760 * boundary, then switch the device to a 512-byte 761 * block size so everything will line up on a block 762 * boundary. 763 */ 764 if ((embeddedOffset % blksize) != 0) { 765 printf("HFS Mount: embedded volume offset not" 766 " a multiple of physical block size (%d);" 767 " switching to 512\n", blksize); 768 769 blkcnt *= (blksize / 512); 770 blksize = 512; 771 } 772 773 disksize = (u_int64_t)mdbp->drEmbedExtent.blockCount * 774 (u_int64_t)mdbp->drAlBlkSiz; 775 776 mdb_offset = (embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize); 777 if (pread(fd, buff, blksize, mdb_offset * blksize) != blksize) { 778 printf("failed to read the embedded vhp @ offset %d\n", mdb_offset * blksize); 779 ret = -1; 780 goto out; 781 } 782 783 vhp = (HFSPlusVolumeHeader*) (buff + HFS_PRI_OFFSET(blksize)); 784 785 mdbp = (HFSMasterDirectoryBlock *)vhp; 786 if ( (SWAP_BE16(mdbp->drSigWord) != kHFSSigWord) 787 && (SWAP_BE16(mdbp->drSigWord) != kHFSPlusSigWord) 788 && (SWAP_BE16(mdbp->drSigWord) != kHFSXSigWord)) { 789 ret = -1; 790 791 printf("get_journal_info: invalid embedded volume signature \n"); 792 goto out; 793 } 794 795 } else /* pure HFS+ */ { 796 embeddedOffset = 0; 797 vhp = (HFSPlusVolumeHeader*) mdbp; 798 } 799 800 if ((SWAP_BE32(vhp->attributes) & kHFSVolumeJournaledMask) == 0) { 801 ret = 0; 802 goto out; 803 } 804 805 // 806 // Now read the journal info block... (when calculating the 807 // position, make sure to cast to unsigned first, then to 808 // off_t so that things don't get sign-extended improperly 809 // or truncated). 810 // 811 pos = (off_t)((off_t)embeddedOffset + 812 (off_t)((unsigned int)SWAP_BE32(vhp->journalInfoBlock))*(off_t)((unsigned int)SWAP_BE32(vhp->blockSize))); 813 814 if (pread(fd, buff2, blksize, pos) != blksize) { 815 printf("failed to read the journal info block (%s).\n", strerror(errno)); 816 ret = -1; 817 goto out; 818 } 819 820 myjib = (struct JournalInfoBlock *)buff2; 821 myjib->flags = SWAP_BE32(myjib->flags); 822 myjib->offset = SWAP_BE64(myjib->offset); 823 myjib->size = SWAP_BE64(myjib->size); 824 825 memcpy(jib, myjib, sizeof(*myjib)); 826 827 ret = 1; 828 829out: 830 if (buff) 831 free(buff); 832 if (buff2) 833 free(buff2); 834 if (fd >= 0) 835 close(fd); 836 837 return ret; 838} 839 840 841int 842DoGetJournalInfo(char *volname) 843{ 844 struct statfs sfs; 845 struct hfs_journal_info jinfo; 846 847 if (strncmp(volname, "/dev/", 5) == 0 || strncmp(volname, "disk", 4) == 0 || strncmp(volname, "rdisk", 5) == 0) { 848 struct JournalInfoBlock jib; 849 int ret; 850 char fulldevname[256]; 851 852 if (strncmp(volname, "disk", 4) == 0 || strncmp(volname, "rdisk", 5) == 0) { 853 snprintf(fulldevname, sizeof(fulldevname), "/dev/%s", volname); 854 volname = &fulldevname[0]; 855 } 856 857 // try the name as a device name... 858 ret = get_journal_info(volname, &jib); 859 if (ret == 0) { 860 printf("Volume %s is not journaled.\n", volname); 861 return 0; 862 } else if (ret < 0) { 863 printf("Volume %s does not appear to be an HFS+ volume.\n", volname); 864 return 10; 865 } else { 866 if (jib.flags & kJIJournalInFSMask) { 867 printf("%s : journal size %lld k at offset 0x%llx\n", volname, jib.size/1024, jib.offset); 868 } else { 869 printf("%s: external journal stored on partition with uuid %s on machine w/serial number %s\n", 870 volname, jib.ext_jnl_uuid, jib.machine_serial_num); 871 } 872 873 return 0; 874 } 875 876 } 877 878 if (statfs(volname, &sfs) != 0) { 879 fprintf(stderr, "Unable to get fs info for %s\n", volname); 880 return 10; 881 } 882 883 if ((sfs.f_flags & MNT_JOURNALED) == 0) { 884 fprintf(stderr, "Volume %s is not journaled.\n", volname); 885 return 1; 886 } 887 888 if (fsctl(volname, HFSIOC_GET_JOURNAL_INFO, &jinfo, 0) != 0) { 889 fprintf(stderr, "Failed to get journal info for volume %s (%s)\n", 890 volname, strerror(errno)); 891 return 20; 892 } 893 894 if (jinfo.jstart == 0) { 895 char rawdev[256]; 896 897 snprintf(rawdev, sizeof(rawdev), "/dev/r%s", &sfs.f_mntfromname[5]); 898 899 // it's an external journal so get the info the 900 // other way. 901 return DoGetJournalInfo(&rawdev[0]); 902 } 903 904 if (jinfo.jsize == 0) { 905 printf("%s : not journaled.\n", volname); 906 } else { 907 printf("%s : journal size %lld k at offset 0x%llx\n", volname, jinfo.jsize/1024, jinfo.jstart); 908 } 909 910 return 0; 911} 912 913 914int 915RawDisableJournaling(char *devname) 916{ 917 int fd = -1, ret = 0; 918 char *buff = NULL, rawdev[256], unrawdev[256]; 919 u_int64_t disksize; 920 u_int64_t blkcnt; 921 u_int32_t blksize; 922 daddr_t mdb_offset; 923 HFSMasterDirectoryBlock *mdbp; 924 HFSPlusVolumeHeader *vhp; 925 off_t embeddedOffset, hdr_offset; 926 struct stat st; 927 struct statfs *fsinfo; 928 929 // assume that the name provided is a raw device name 930 strlcpy(rawdev, devname, sizeof(rawdev)); 931 932restart: 933 if (stat(rawdev, &st) != 0) { 934 fprintf(stderr, "Could not access %s (%s)\n", devname, strerror(errno)); 935 ret = -1; 936 goto out; 937 } 938 939 if (S_ISCHR(st.st_mode) == 0) { 940 if (S_ISBLK(st.st_mode)) { 941 // this is a block device, convert the name to 942 // raw character device and try again 943 snprintf(rawdev, sizeof(rawdev), "/dev/r%s", devname + 5); 944 goto restart; 945 } else { 946 // probably it is a mount point 947 return DoUnJournal(devname); 948 } 949 } else { 950 // convert the character raw device name to 951 // block device name to compare with getmntinfo output 952 snprintf(unrawdev, sizeof(unrawdev), "/dev/%s", rawdev + 6); 953 } 954 955 // make sure that the file system on the device node is not mounted 956 ret = getmntinfo(&fsinfo, MNT_NOWAIT); 957 if (ret == 0) { 958 fprintf (stderr, "Error getting list of mounted filesystems\n"); 959 ret = -1; 960 goto out; 961 } 962 963 while (ret--) { 964 // the file system on this device node is currently mounted 965 if (strcmp(unrawdev, fsinfo[ret].f_mntfromname) == 0) { 966 return DoUnJournal(fsinfo[ret].f_mntonname); 967 } 968 } 969 970 fd = open(rawdev, O_RDWR); 971 if (fd < 0) { 972 fprintf(stderr, "can't open: %s (%s)\n", devname, strerror(errno)); 973 ret = -1; 974 goto out; 975 } 976 977 /* Get the real physical block size. */ 978 if (ioctl(fd, DKIOCGETBLOCKSIZE, (caddr_t)&blksize) != 0) { 979 fprintf(stderr, "can't get the device block size (%s). assuming 512\n", strerror(errno)); 980 blksize = 512; 981 ret = -1; 982 goto out; 983 } 984 985 /* Get the number of physical blocks. */ 986 if (ioctl(fd, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt)) { 987 struct stat st; 988 989 if (fstat(fd, &st) != 0) { 990 ret = -1; 991 goto out; 992 } 993 994 blkcnt = st.st_size / blksize; 995 } 996 997 /* Compute an accurate disk size */ 998 disksize = blkcnt * (u_int64_t)blksize; 999 1000 /* 1001 * There are only 31 bits worth of block count in 1002 * the buffer cache. So for large volumes a 4K 1003 * physical block size is needed. 1004 */ 1005 if (blksize == 512 && blkcnt > (u_int64_t)0x000000007fffffff) { 1006 blksize = 4096; 1007 } 1008 1009 /* 1010 * At this point: 1011 * blksize has our prefered physical block size 1012 * blkcnt has the total number of physical blocks 1013 */ 1014 1015 buff = (char *)malloc(blksize); 1016 1017 hdr_offset = HFS_PRI_SECTOR(blksize)*blksize; 1018 if (pread(fd, buff, blksize, hdr_offset) != blksize) { 1019 fprintf(stderr, "RawDisableJournaling: failed to read volume header @ offset %lld (%s)\n", 1020 hdr_offset, strerror(errno)); 1021 ret = -1; 1022 goto out; 1023 } 1024 1025 mdbp = (HFSMasterDirectoryBlock *)(buff + HFS_PRI_OFFSET(blksize)); 1026 if ( (SWAP_BE16(mdbp->drSigWord) != kHFSSigWord) 1027 && (SWAP_BE16(mdbp->drSigWord) != kHFSPlusSigWord) 1028 && (SWAP_BE16(mdbp->drSigWord) != kHFSXSigWord)) { 1029 ret = -1; 1030 printf("RawDisableJournaling: Invalid Volume Signature \n"); 1031 goto out; 1032 } 1033 1034 if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) && (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord)) { 1035 // normal hfs can not ever be journaled 1036 fprintf(stderr, "disable_journaling: volume is only regular HFS, not HFS+\n"); 1037 goto out; 1038 } 1039 1040 /* Get the embedded Volume Header */ 1041 if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) { 1042 embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * 512; 1043 embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) * (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz); 1044 1045 /* 1046 * If the embedded volume doesn't start on a block 1047 * boundary, then switch the device to a 512-byte 1048 * block size so everything will line up on a block 1049 * boundary. 1050 */ 1051 if ((embeddedOffset % blksize) != 0) { 1052 fprintf(stderr, "HFS Mount: embedded volume offset not" 1053 " a multiple of physical block size (%d);" 1054 " switching to 512\n", blksize); 1055 1056 blkcnt *= (blksize / 512); 1057 blksize = 512; 1058 } 1059 1060 disksize = (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.blockCount) * (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz); 1061 1062 mdb_offset = (embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize); 1063 hdr_offset = mdb_offset * blksize; 1064 if (pread(fd, buff, blksize, hdr_offset) != blksize) { 1065 fprintf(stderr, "failed to read the embedded vhp @ offset %d\n", mdb_offset * blksize); 1066 ret = -1; 1067 goto out; 1068 } 1069 1070 vhp = (HFSPlusVolumeHeader*) (buff + HFS_PRI_OFFSET(blksize)); 1071 1072 mdbp = (HFSMasterDirectoryBlock *)vhp; 1073 if ( (SWAP_BE16(mdbp->drSigWord) != kHFSSigWord) 1074 && (SWAP_BE16(mdbp->drSigWord) != kHFSPlusSigWord) 1075 && (SWAP_BE16(mdbp->drSigWord) != kHFSXSigWord)) { 1076 ret = -1; 1077 1078 printf("RawDisableJournaling: invalid embedded volume signature \n"); 1079 goto out; 1080 } 1081 1082 } else /* pure HFS+ */ { 1083 embeddedOffset = 0; 1084 vhp = (HFSPlusVolumeHeader*) mdbp; 1085 } 1086 1087 1088 if ((SWAP_BE32(vhp->attributes) & kHFSVolumeJournaledMask) != 0) { 1089 unsigned int tmp = SWAP_BE32(vhp->attributes); 1090 1091 tmp &= ~kHFSVolumeJournaledMask; 1092 vhp->attributes = SWAP_BE32(tmp); 1093 if ((tmp = pwrite(fd, buff, blksize, hdr_offset)) != blksize) { 1094 fprintf(stderr, "Update of super-block on %s failed! (%d != %d, %s)\n", 1095 devname, tmp, blksize, strerror(errno)); 1096 } else { 1097 fprintf(stderr, "Turned off the journaling bit for %s\n", devname); 1098 } 1099 } else { 1100 fprintf(stderr, "disable_journaling: %s is not journaled.\n", devname); 1101 } 1102 1103 1104 out: 1105 if (buff) 1106 free(buff); 1107 if (fd >= 0) 1108 close(fd); 1109 1110 return ret; 1111} 1112 1113 1114 1115int 1116SetJournalInFSState(const char *devname, int journal_in_fs) 1117{ 1118 int fd = -1, ret = 0; 1119 char *buff = NULL, *buff2 = NULL; 1120 u_int64_t blkcnt; 1121 u_int32_t blksize; 1122 daddr_t mdb_offset; 1123 HFSMasterDirectoryBlock *mdbp; 1124 HFSPlusVolumeHeader *vhp; 1125 off_t embeddedOffset, pos; 1126 struct JournalInfoBlock *myjib; 1127 1128 fd = open(devname, O_RDWR); 1129 if (fd < 0) { 1130 printf("can't open: %s (%s)\n", devname, strerror(errno)); 1131 ret = -1; 1132 goto out; 1133 } 1134 1135 /* Get the real physical block size. */ 1136 if (ioctl(fd, DKIOCGETBLOCKSIZE, (caddr_t)&blksize) != 0) { 1137 printf("can't get the device block size (%s). assuming 512\n", strerror(errno)); 1138 blksize = 512; 1139 ret = -1; 1140 goto out; 1141 } 1142 1143 /* Get the number of physical blocks. */ 1144 if (ioctl(fd, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt)) { 1145 struct stat st; 1146 printf("failed to get block count. trying stat().\n"); 1147 if (fstat(fd, &st) != 0) { 1148 ret = -1; 1149 goto out; 1150 } 1151 1152 blkcnt = st.st_size / blksize; 1153 } 1154 1155 /* 1156 * There used to only be 31 bits worth of block count in 1157 * the buffer cache. So for large volumes a 4K 1158 * physical block size is needed. 1159 */ 1160 if (blksize == 512 && blkcnt > (u_int64_t)0x000000007fffffff) { 1161 blksize = 4096; 1162 } 1163 1164 /* 1165 * At this point: 1166 * blksize has our prefered physical block size 1167 * blkcnt has the total number of physical blocks 1168 */ 1169 1170 buff = (char *)malloc(blksize); 1171 buff2 = (char *)malloc(blksize); 1172 1173 if (pread(fd, buff, blksize, HFS_PRI_SECTOR(blksize)*blksize) != blksize) { 1174 printf("failed to read volume header @ offset %d (%s)\n", 1175 HFS_PRI_SECTOR(blksize), strerror(errno)); 1176 ret = -1; 1177 goto out; 1178 } 1179 1180 mdbp = (HFSMasterDirectoryBlock *)(buff + HFS_PRI_OFFSET(blksize)); 1181 1182 1183 if ( (SWAP_BE16(mdbp->drSigWord) != kHFSSigWord) 1184 && (SWAP_BE16(mdbp->drSigWord) != kHFSPlusSigWord) 1185 && (SWAP_BE16(mdbp->drSigWord) != kHFSXSigWord)) { 1186 ret = -1; 1187 printf ("SetJournalInFSState: Invalid Volume Signature \n"); 1188 goto out; 1189 } 1190 1191 if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) && (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord)) { 1192 // normal hfs can not ever be journaled 1193 goto out; 1194 } 1195 1196 /* Get the embedded Volume Header */ 1197 if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) { 1198 embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * 512; 1199 embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) * 1200 (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz); 1201 1202 /* 1203 * If the embedded volume doesn't start on a block 1204 * boundary, then switch the device to a 512-byte 1205 * block size so everything will line up on a block 1206 * boundary. 1207 */ 1208 if ((embeddedOffset % blksize) != 0) { 1209 printf("HFS Mount: embedded volume offset not" 1210 " a multiple of physical block size (%d);" 1211 " switching to 512\n", blksize); 1212 1213 blkcnt *= (blksize / 512); 1214 blksize = 512; 1215 } 1216 1217 mdb_offset = (embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize); 1218 if (pread(fd, buff, blksize, mdb_offset * blksize) != blksize) { 1219 printf("failed to read the embedded vhp @ offset %d\n", mdb_offset * blksize); 1220 ret = -1; 1221 goto out; 1222 } 1223 1224 vhp = (HFSPlusVolumeHeader*) (buff + HFS_PRI_OFFSET(blksize)); 1225 1226 mdbp = (HFSMasterDirectoryBlock *)(vhp); 1227 if ( (SWAP_BE16(mdbp->drSigWord) != kHFSSigWord) 1228 && (SWAP_BE16(mdbp->drSigWord) != kHFSPlusSigWord) 1229 && (SWAP_BE16(mdbp->drSigWord) != kHFSXSigWord)) { 1230 ret = -1; 1231 printf("SetJournalInFSState: Invalid Embedded Volume Signature \n"); 1232 goto out; 1233 } 1234 1235 1236 } else /* pure HFS+ */ { 1237 embeddedOffset = 0; 1238 vhp = (HFSPlusVolumeHeader*) mdbp; 1239 } 1240 1241 //printf("vol header attributes: 0x%x\n", SWAP_BE32(vhp->attributes)); 1242 if ((SWAP_BE32(vhp->attributes) & kHFSVolumeJournaledMask) == 0) { 1243 ret = 0; 1244 goto out; 1245 } 1246 1247 // 1248 // Now read the journal info block... (when calculating the 1249 // position, make sure to cast to unsigned first, then to 1250 // off_t so that things don't get sign-extended improperly 1251 // or truncated). 1252 // 1253 pos = (off_t)((off_t)embeddedOffset + 1254 (off_t)((unsigned int )SWAP_BE32(vhp->journalInfoBlock))*(off_t)((unsigned int)SWAP_BE32(vhp->blockSize))); 1255 1256 if (pread(fd, buff2, blksize, pos) != blksize) { 1257 printf("failed to read the journal info block (%s).\n", strerror(errno)); 1258 ret = -1; 1259 goto out; 1260 } 1261 1262 myjib = (struct JournalInfoBlock *)buff2; 1263 1264 // swap this to host native format so we can diddle with the bits 1265 myjib->flags = SWAP_BE32(myjib->flags); 1266 1267 if (journal_in_fs) { 1268 myjib->flags |= kJIJournalInFSMask; 1269 } else { 1270 myjib->flags &= ~kJIJournalInFSMask; 1271 } 1272 myjib->flags |= kJIJournalNeedInitMask; 1273 1274 // and now swap back before writing it out 1275 myjib->flags = SWAP_BE32(myjib->flags); 1276 1277 if (pwrite(fd, buff2, blksize, pos) != blksize) { 1278 printf("failed to re-write the journal info block (%s).\n", strerror(errno)); 1279 ret = -1; 1280 goto out; 1281 } 1282 1283 ret = 0; 1284 1285 out: 1286 if (buff) 1287 free(buff); 1288 if (buff2) 1289 free(buff2); 1290 if (fd >= 0) 1291 close(fd); 1292 1293 return ret; 1294} 1295