]> git.saurik.com Git - apple/hfs.git/blob - hfs_util/hfsutil_jnl.c
hfs-285.tar.gz
[apple/hfs.git] / hfs_util / hfsutil_jnl.c
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 */
75 struct ExtentsAttrBuf {
76 unsigned long infoLength;
77 HFSPlusExtentRecord extents;
78 };
79 typedef struct ExtentsAttrBuf ExtentsAttrBuf;
80
81 #ifndef HFSIOC_GET_JOURNAL_INFO
82 # include <sys/ioctl.h>
83 struct 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 */
96 struct FinderInfo {
97 u_int32_t opaque_1[2];
98 u_int16_t fdFlags; /* Finder flags */
99 int16_t opaque_2[11];
100 };
101 typedef struct FinderInfo FinderInfo;
102
103 /* getattrlist buffers start with an extra length field */
104 struct FinderAttrBuf {
105 unsigned long infoLength;
106 FinderInfo finderInfo;
107 };
108 typedef struct FinderAttrBuf FinderAttrBuf;
109
110
111 int 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
137 off_t
138 get_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
213 off_t
214 get_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
343 static const char *journal_fname = ".journal";
344 static const char *jib_fname = ".journal_info_block";
345
346 int
347 DoMakeJournaled(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
454 retry:
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
600 int
601 DoUnJournal(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
660 int
661 get_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
829 out:
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
841 int
842 DoGetJournalInfo(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
914 int
915 RawDisableJournaling(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
932 restart:
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
1115 int
1116 SetJournalInFSState(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 }