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