]> git.saurik.com Git - apple/hfs.git/blob - hfs_util/hfsutil_jnl.c
hfs-183.tar.gz
[apple/hfs.git] / hfs_util / hfsutil_jnl.c
1 /*
2 * Copyright (c) 1999-2008 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;
290 if ( (SWAP_BE16(mdbp->drSigWord) != kHFSSigWord)
291 && (SWAP_BE16(mdbp->drSigWord) != kHFSPlusSigWord)
292 && (SWAP_BE16(mdbp->drSigWord) != kHFSXSigWord)) {
293 ret = -1;
294 goto out;
295 }
296
297 if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) && (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord)) {
298 ret = -1;
299 goto out;
300 } else if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) {
301 /* Get the embedded Volume Header */
302 embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * 512;
303 embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) *
304 (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
305
306 /*
307 * If the embedded volume doesn't start on a block
308 * boundary, then switch the device to a 512-byte
309 * block size so everything will line up on a block
310 * boundary.
311 */
312 if ((embeddedOffset % blksize) != 0) {
313 fprintf(stderr, "HFS Mount: embedded volume offset not"
314 " a multiple of physical block size (%d);"
315 " switching to 512\n", blksize);
316
317 blkcnt *= (blksize / 512);
318 blksize = 512;
319 }
320
321 } else { /* pure HFS+ */
322 embeddedOffset = 0;
323 }
324
325 ret = embeddedOffset;
326
327 out:
328 if (buff) {
329 free(buff);
330 }
331 if (fd >= 0)
332 close(fd);
333
334 return ret;
335 }
336
337
338
339 static const char *journal_fname = ".journal";
340 static const char *jib_fname = ".journal_info_block";
341
342 int
343 DoMakeJournaled(char *volname, int jsize)
344 {
345 int fd, i, block_size, journal_size = 8*1024*1024;
346 char *buf;
347 int ret;
348 fstore_t fst;
349 int32_t jstart_block, jinfo_block;
350 int sysctl_info[8];
351 JournalInfoBlock jib;
352 struct statfs sfs;
353 static char tmpname[MAXPATHLEN];
354 off_t start_block, embedded_offset;
355
356 if (statfs(volname, &sfs) != 0) {
357 fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
358 return 10;
359 }
360
361 // Make sure that we're HFS+. First we check the fstypename.
362 // If that's ok then we try to create a symlink (which won't
363 // work on plain hfs volumes but will work on hfs+ volumes).
364 //
365 if (strcmp(sfs.f_fstypename, "devfs") == 0) {
366 fprintf (stderr, "%s is a device node. Journal enable only works on a mounted HFS+ volume.\n", volname);
367 return 10;
368 }
369 snprintf(tmpname, sizeof(tmpname), "%s/is_vol_hfs_plus", volname);
370 if (strcmp(sfs.f_fstypename, "hfs") != 0 ||
371 ((ret = symlink(tmpname, tmpname)) != 0 && errno == ENOTSUP)) {
372 fprintf(stderr, "%s is not an HFS+ volume. Journaling only works on HFS+ volumes.\n",
373 volname);
374 return 10;
375 }
376 unlink(tmpname);
377
378 if (sfs.f_flags & MNT_JOURNALED) {
379 fprintf(stderr, "Volume %s is already journaled.\n", volname);
380 return 1;
381 }
382
383 if (jsize != 0) {
384 journal_size = jsize;
385 } else {
386 int scale;
387
388 //
389 // we want at least 8 megs of journal for each 100 gigs of
390 // disk space. We cap the size at 512 megs though.
391 //
392 scale = ((long long)sfs.f_bsize * (long long)((unsigned int)sfs.f_blocks)) / (100*1024*1024*1024ULL);
393 journal_size *= (scale + 1);
394 if (journal_size > 512 * 1024 * 1024) {
395 journal_size = 512 * 1024 * 1024;
396 }
397 }
398
399 if (chdir(volname) != 0) {
400 fprintf(stderr, "Can't locate volume %s to make it journaled (%s).\n",
401 volname, strerror(errno));
402 return 10;
403 }
404
405
406 embedded_offset = get_embedded_offset(volname);
407 if (embedded_offset < 0) {
408 fprintf(stderr, "Can't calculate the embedded offset (if any) for %s.\n", volname);
409 fprintf(stderr, "Journal creation failure.\n");
410 return 15;
411 }
412 // printf("Embedded offset == 0x%llx\n", embedded_offset);
413
414 fd = open(journal_fname, O_CREAT|O_TRUNC|O_RDWR, 000);
415 if (fd < 0) {
416 fprintf(stderr, "Can't create journal file on volume %s (%s)\n",
417 volname, strerror(errno));
418 return 5;
419 }
420
421 // make sure that it has no r/w/x privs (only could happen if
422 // the file already existed since open() doesn't reset the mode
423 // bits).
424 //
425 fchmod(fd, 0);
426
427 block_size = sfs.f_bsize;
428 if ((journal_size % block_size) != 0) {
429 fprintf(stderr, "Journal size %dk is not a multiple of volume %s block size (%d).\n",
430 journal_size/1024, volname, block_size);
431 close(fd);
432 unlink(journal_fname);
433 return 5;
434 }
435
436 retry:
437 memset(&fst, 0, sizeof(fst));
438 fst.fst_flags = F_ALLOCATECONTIG|F_ALLOCATEALL;
439 fst.fst_length = journal_size;
440 fst.fst_posmode = F_PEOFPOSMODE;
441
442 ret = fcntl(fd, F_PREALLOCATE, &fst);
443 if (ret < 0) {
444 if (journal_size >= 2*1024*1024) {
445 fprintf(stderr, "Not enough contiguous space for a %d k journal. Retrying.\n",
446 journal_size/1024);
447 journal_size /= 2;
448 ftruncate(fd, 0); // make sure the file is zero bytes long.
449 goto retry;
450 } else {
451 fprintf(stderr, "Disk too fragmented to enable journaling.\n");
452 fprintf(stderr, "Please run a defragmenter on %s.\n", volname);
453 close(fd);
454 unlink(journal_fname);
455 return 10;
456 }
457 }
458
459 printf("Allocated %lldK for journal file.\n", fst.fst_bytesalloc/1024LL);
460 buf = (char *)calloc(block_size, 1);
461 if (buf) {
462 for(i=0; i < journal_size/block_size; i++) {
463 ret = write(fd, buf, block_size);
464 if (ret != block_size) {
465 break;
466 }
467 }
468
469 if (i*block_size != journal_size) {
470 fprintf(stderr, "Failed to write %dk to journal on volume %s (%s)\n",
471 journal_size/1024, volname, strerror(errno));
472 }
473 } else {
474 printf("Could not allocate memory to write to the journal on volume %s (%s)\n",
475 volname, strerror(errno));
476 }
477
478 fsync(fd);
479 close(fd);
480 hide_file(journal_fname);
481
482 start_block = get_start_block(journal_fname, block_size);
483 if (start_block == (off_t)-1) {
484 fprintf(stderr, "Failed to get start block for %s (%s)\n",
485 journal_fname, strerror(errno));
486 unlink(journal_fname);
487 return 20;
488 }
489 jstart_block = (start_block / block_size) - (embedded_offset / block_size);
490
491 memset(&jib, 'Z', sizeof(jib));
492 jib.flags = kJIJournalInFSMask;
493 jib.offset = (off_t)((unsigned int)jstart_block) * (off_t)((unsigned int)block_size);
494 jib.size = (off_t)((unsigned int)journal_size);
495
496 fd = open(jib_fname, O_CREAT|O_TRUNC|O_RDWR, 000);
497 if (fd < 0) {
498 fprintf(stderr, "Could not create journal info block file on volume %s (%s)\n",
499 volname, strerror(errno));
500 unlink(journal_fname);
501 return 5;
502 }
503
504 // swap the data before we copy it
505 jib.flags = OSSwapBigToHostInt32(jib.flags);
506 jib.offset = OSSwapBigToHostInt64(jib.offset);
507 jib.size = OSSwapBigToHostInt64(jib.size);
508
509 memcpy(buf, &jib, sizeof(jib));
510
511 // now put it back the way it was
512 jib.size = OSSwapBigToHostInt64(jib.size);
513 jib.offset = OSSwapBigToHostInt64(jib.offset);
514 jib.flags = OSSwapBigToHostInt32(jib.flags);
515
516 if (write(fd, buf, block_size) != block_size) {
517 fprintf(stderr, "Failed to write journal info block on volume %s (%s)!\n",
518 volname, strerror(errno));
519 unlink(journal_fname);
520 return 10;
521 }
522
523 fsync(fd);
524 close(fd);
525 hide_file(jib_fname);
526
527 start_block = get_start_block(jib_fname, block_size);
528 if (start_block == (off_t)-1) {
529 fprintf(stderr, "Failed to get start block for %s (%s)\n",
530 jib_fname, strerror(errno));
531 unlink(journal_fname);
532 unlink(jib_fname);
533 return 20;
534 }
535 jinfo_block = (start_block / block_size) - (embedded_offset / block_size);
536
537
538 //
539 // Now make the volume journaled!
540 //
541 memset(sysctl_info, 0, sizeof(sysctl_info));
542 sysctl_info[0] = CTL_VFS;
543 sysctl_info[1] = sfs.f_fsid.val[1];
544 sysctl_info[2] = HFS_ENABLE_JOURNALING;
545 sysctl_info[3] = jinfo_block;
546 sysctl_info[4] = jstart_block;
547 sysctl_info[5] = journal_size;
548
549 //printf("fs type: 0x%x\n", sysctl_info[1]);
550 //printf("jinfo block : 0x%x\n", jinfo_block);
551 //printf("jstart block: 0x%x\n", jstart_block);
552 //printf("journal size: 0x%x\n", journal_size);
553
554 ret = sysctl((void *)sysctl_info, 6, NULL, NULL, NULL, 0);
555 if (ret != 0) {
556 fprintf(stderr, "Failed to make volume %s journaled (%s)\n",
557 volname, strerror(errno));
558 unlink(journal_fname);
559 unlink(jib_fname);
560 return 20;
561 }
562
563 return 0;
564 }
565
566
567 int
568 DoUnJournal(char *volname)
569 {
570 int result;
571 int sysctl_info[8];
572 struct statfs sfs;
573 char jbuf[MAXPATHLEN];
574
575 if (statfs(volname, &sfs) != 0) {
576 fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
577 return 10;
578 }
579
580 if (strcmp(sfs.f_fstypename, "hfs") != 0) {
581 fprintf(stderr, "Volume %s (%s) is not a HFS volume.\n", volname, sfs.f_mntfromname);
582 return 1;
583 }
584
585 if ((sfs.f_flags & MNT_JOURNALED) == 0) {
586 fprintf(stderr, "Volume %s (%s) is not journaled.\n", volname, sfs.f_mntfromname);
587 return 1;
588 }
589
590 if (chdir(volname) != 0) {
591 fprintf(stderr, "Can't locate volume %s to turn off journaling (%s).\n",
592 volname, strerror(errno));
593 return 10;
594 }
595
596 memset(sysctl_info, 0, sizeof(sysctl_info));
597 sysctl_info[0] = CTL_VFS;
598 sysctl_info[1] = sfs.f_fsid.val[1];
599 sysctl_info[2] = HFS_DISABLE_JOURNALING;
600
601 result = sysctl((void *)sysctl_info, 3, NULL, NULL, NULL, 0);
602 if (result != 0) {
603 fprintf(stderr, "Failed to make volume %s UN-journaled (%s)\n",
604 volname, strerror(errno));
605 return 20;
606 }
607
608 snprintf(jbuf, sizeof(jbuf), "%s/%s", volname, journal_fname);
609 if (unlink(jbuf) != 0) {
610 fprintf(stderr, "Failed to remove the journal %s (%s)\n",
611 jbuf, strerror(errno));
612 }
613
614 snprintf(jbuf, sizeof(jbuf), "%s/%s", volname, jib_fname);
615 if (unlink(jbuf) != 0) {
616 fprintf(stderr, "Failed to remove the journal info block %s (%s)\n",
617 jbuf, strerror(errno));
618 }
619
620 printf("Journaling disabled on %s mounted at %s.\n", sfs.f_mntfromname, volname);
621
622 return 0;
623 }
624
625
626
627
628 int
629 get_journal_info(char *devname, struct JournalInfoBlock *jib)
630 {
631 int fd = -1, ret = 0;
632 char *buff = NULL, *buff2 = NULL;
633 u_int64_t disksize;
634 u_int64_t blkcnt;
635 u_int32_t blksize;
636 daddr_t mdb_offset;
637 HFSMasterDirectoryBlock *mdbp;
638 HFSPlusVolumeHeader *vhp;
639 off_t embeddedOffset, pos;
640 struct JournalInfoBlock *myjib;
641
642 fd = open(devname, O_RDONLY);
643 if (fd < 0) {
644 printf("can't open: %s (%s)\n", devname, strerror(errno));
645 ret = -5;
646 goto out;
647 }
648
649 /* Get the real physical block size. */
650 if (ioctl(fd, DKIOCGETBLOCKSIZE, (caddr_t)&blksize) != 0) {
651 printf("can't get the device block size (%s). assuming 512\n", strerror(errno));
652 blksize = 512;
653 ret = -1;
654 goto out;
655 }
656
657 /* Get the number of physical blocks. */
658 if (ioctl(fd, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt)) {
659 struct stat st;
660 printf("failed to get block count. trying stat().\n");
661 if (fstat(fd, &st) != 0) {
662 ret = -1;
663 goto out;
664 }
665
666 blkcnt = st.st_size / blksize;
667 }
668
669 /* Compute an accurate disk size */
670 disksize = blkcnt * (u_int64_t)blksize;
671
672 /*
673 * There are only 31 bits worth of block count in
674 * the buffer cache. So for large volumes a 4K
675 * physical block size is needed.
676 */
677 if (blksize == 512 && blkcnt > (u_int64_t)0x000000007fffffff) {
678 blksize = 4096;
679 }
680
681 /*
682 * At this point:
683 * blksize has our prefered physical block size
684 * blkcnt has the total number of physical blocks
685 */
686
687 buff = (char *)malloc(blksize);
688 buff2 = (char *)malloc(blksize);
689
690 if (pread(fd, buff, blksize, HFS_PRI_SECTOR(blksize)*blksize) != blksize) {
691 printf("failed to read volume header @ offset %d (%s)\n",
692 HFS_PRI_SECTOR(blksize), strerror(errno));
693 ret = -1;
694 goto out;
695 }
696
697 if (blksize == 512) {
698 mdbp = (HFSMasterDirectoryBlock *)buff;
699 } else {
700 mdbp = (HFSMasterDirectoryBlock *)(buff + 1024);
701 }
702
703 mdbp->drSigWord = SWAP_BE16(mdbp->drSigWord);
704 mdbp->drEmbedSigWord = SWAP_BE16(mdbp->drEmbedSigWord);
705 mdbp->drAlBlSt = SWAP_BE16(mdbp->drAlBlSt);
706 mdbp->drEmbedExtent.startBlock = SWAP_BE16(mdbp->drEmbedExtent.startBlock);
707 mdbp->drAlBlkSiz = SWAP_BE32(mdbp->drAlBlkSiz);
708 mdbp->drEmbedExtent.blockCount = SWAP_BE16(mdbp->drEmbedExtent.blockCount);
709
710 // first check if it's even hfs at all...
711 if ( mdbp->drSigWord != kHFSSigWord
712 && mdbp->drSigWord != kHFSPlusSigWord
713 && mdbp->drSigWord != kHFSXSigWord) {
714
715 ret = -1;
716 goto out;
717 }
718
719 if ((mdbp->drSigWord == kHFSSigWord) && (mdbp->drEmbedSigWord != kHFSPlusSigWord)) {
720 // normal hfs can not ever be journaled
721 goto out;
722 }
723
724 /* Get the embedded Volume Header */
725 if (mdbp->drEmbedSigWord == kHFSPlusSigWord) {
726 embeddedOffset = mdbp->drAlBlSt * 512;
727 embeddedOffset += (u_int64_t)mdbp->drEmbedExtent.startBlock *
728 (u_int64_t)mdbp->drAlBlkSiz;
729
730 /*
731 * If the embedded volume doesn't start on a block
732 * boundary, then switch the device to a 512-byte
733 * block size so everything will line up on a block
734 * boundary.
735 */
736 if ((embeddedOffset % blksize) != 0) {
737 printf("HFS Mount: embedded volume offset not"
738 " a multiple of physical block size (%d);"
739 " switching to 512\n", blksize);
740
741 blkcnt *= (blksize / 512);
742 blksize = 512;
743 }
744
745 disksize = (u_int64_t)mdbp->drEmbedExtent.blockCount *
746 (u_int64_t)mdbp->drAlBlkSiz;
747
748 mdb_offset = (embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize);
749 if (pread(fd, buff, blksize, mdb_offset * blksize) != blksize) {
750 printf("failed to read the embedded vhp @ offset %d\n", mdb_offset * blksize);
751 ret = -1;
752 goto out;
753 }
754
755 vhp = (HFSPlusVolumeHeader*) buff;
756 } else /* pure HFS+ */ {
757 embeddedOffset = 0;
758 vhp = (HFSPlusVolumeHeader*) mdbp;
759 }
760
761 if ((SWAP_BE32(vhp->attributes) & kHFSVolumeJournaledMask) == 0) {
762 ret = 0;
763 goto out;
764 }
765
766 //
767 // Now read the journal info block... (when calculating the
768 // position, make sure to cast to unsigned first, then to
769 // off_t so that things don't get sign-extended improperly
770 // or truncated).
771 //
772 pos = (off_t)((off_t)embeddedOffset +
773 (off_t)((unsigned int)SWAP_BE32(vhp->journalInfoBlock))*(off_t)((unsigned int)SWAP_BE32(vhp->blockSize)));
774
775 if (pread(fd, buff2, blksize, pos) != blksize) {
776 printf("failed to read the journal info block (%s).\n", strerror(errno));
777 ret = -1;
778 goto out;
779 }
780
781 myjib = (struct JournalInfoBlock *)buff2;
782 myjib->flags = SWAP_BE32(myjib->flags);
783 myjib->offset = SWAP_BE64(myjib->offset);
784 myjib->size = SWAP_BE64(myjib->size);
785
786 memcpy(jib, myjib, sizeof(*myjib));
787
788 ret = 1;
789
790 out:
791 if (buff)
792 free(buff);
793 if (buff2)
794 free(buff2);
795 if (fd >= 0)
796 close(fd);
797
798 return ret;
799 }
800
801
802 int
803 DoGetJournalInfo(char *volname)
804 {
805 struct statfs sfs;
806 struct hfs_journal_info jinfo;
807
808 if (strncmp(volname, "/dev/", 5) == 0 || strncmp(volname, "disk", 4) == 0 || strncmp(volname, "rdisk", 5) == 0) {
809 struct JournalInfoBlock jib;
810 int ret;
811 char fulldevname[256];
812
813 if (strncmp(volname, "disk", 4) == 0 || strncmp(volname, "rdisk", 5) == 0) {
814 snprintf(fulldevname, sizeof(fulldevname), "/dev/%s", volname);
815 volname = &fulldevname[0];
816 }
817
818 // try the name as a device name...
819 ret = get_journal_info(volname, &jib);
820 if (ret == 0) {
821 printf("Volume %s is not journaled.\n", volname);
822 return 0;
823 } else if (ret < 0) {
824 printf("Volume %s does not appear to be an HFS+ volume.\n", volname);
825 return 10;
826 } else {
827 if (jib.flags & kJIJournalInFSMask) {
828 printf("%s : journal size %lld k at offset 0x%llx\n", volname, jib.size/1024, jib.offset);
829 } else {
830 printf("%s: external journal stored on partition with uuid %s on machine w/serial number %s\n",
831 volname, jib.ext_jnl_uuid, jib.machine_serial_num);
832 }
833
834 return 0;
835 }
836
837 }
838
839 if (statfs(volname, &sfs) != 0) {
840 fprintf(stderr, "Unable to get fs info for %s\n", volname);
841 return 10;
842 }
843
844 if ((sfs.f_flags & MNT_JOURNALED) == 0) {
845 fprintf(stderr, "Volume %s is not journaled.\n", volname);
846 return 1;
847 }
848
849 if (fsctl(volname, HFSIOC_GET_JOURNAL_INFO, &jinfo, 0) != 0) {
850 fprintf(stderr, "Failed to get journal info for volume %s (%s)\n",
851 volname, strerror(errno));
852 return 20;
853 }
854
855 if (jinfo.jstart == 0) {
856 char rawdev[256];
857
858 snprintf(rawdev, sizeof(rawdev), "/dev/r%s", &sfs.f_mntfromname[5]);
859
860 // it's an external journal so get the info the
861 // other way.
862 return DoGetJournalInfo(&rawdev[0]);
863 }
864
865 if (jinfo.jsize == 0) {
866 printf("%s : not journaled.\n", volname);
867 } else {
868 printf("%s : journal size %lld k at offset 0x%llx\n", volname, jinfo.jsize/1024, jinfo.jstart);
869 }
870
871 return 0;
872 }
873
874
875 int
876 RawDisableJournaling(char *devname)
877 {
878 int fd = -1, ret = 0;
879 char *buff = NULL, rawdev[256], unrawdev[256];
880 u_int64_t disksize;
881 u_int64_t blkcnt;
882 u_int32_t blksize;
883 daddr_t mdb_offset;
884 HFSMasterDirectoryBlock *mdbp;
885 HFSPlusVolumeHeader *vhp;
886 off_t embeddedOffset, hdr_offset;
887 struct stat st;
888 struct statfs *fsinfo;
889
890 // assume that the name provided is a raw device name
891 strlcpy(rawdev, devname, sizeof(rawdev));
892
893 restart:
894 if (stat(rawdev, &st) != 0) {
895 fprintf(stderr, "Could not access %s (%s)\n", devname, strerror(errno));
896 ret = -1;
897 goto out;
898 }
899
900 if (S_ISCHR(st.st_mode) == 0) {
901 if (S_ISBLK(st.st_mode)) {
902 // this is a block device, convert the name to
903 // raw character device and try again
904 snprintf(rawdev, sizeof(rawdev), "/dev/r%s", devname + 5);
905 goto restart;
906 } else {
907 // probably it is a mount point
908 return DoUnJournal(devname);
909 }
910 } else {
911 // convert the character raw device name to
912 // block device name to compare with getmntinfo output
913 snprintf(unrawdev, sizeof(unrawdev), "/dev/%s", rawdev + 6);
914 }
915
916 // make sure that the file system on the device node is not mounted
917 ret = getmntinfo(&fsinfo, MNT_NOWAIT);
918 if (ret == 0) {
919 fprintf (stderr, "Error getting list of mounted filesystems\n");
920 ret = -1;
921 goto out;
922 }
923
924 while (ret--) {
925 // the file system on this device node is currently mounted
926 if (strcmp(unrawdev, fsinfo[ret].f_mntfromname) == 0) {
927 return DoUnJournal(fsinfo[ret].f_mntonname);
928 }
929 }
930
931 fd = open(rawdev, O_RDWR);
932 if (fd < 0) {
933 fprintf(stderr, "can't open: %s (%s)\n", devname, strerror(errno));
934 ret = -1;
935 goto out;
936 }
937
938 /* Get the real physical block size. */
939 if (ioctl(fd, DKIOCGETBLOCKSIZE, (caddr_t)&blksize) != 0) {
940 fprintf(stderr, "can't get the device block size (%s). assuming 512\n", strerror(errno));
941 blksize = 512;
942 ret = -1;
943 goto out;
944 }
945
946 /* Get the number of physical blocks. */
947 if (ioctl(fd, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt)) {
948 struct stat st;
949
950 if (fstat(fd, &st) != 0) {
951 ret = -1;
952 goto out;
953 }
954
955 blkcnt = st.st_size / blksize;
956 }
957
958 /* Compute an accurate disk size */
959 disksize = blkcnt * (u_int64_t)blksize;
960
961 /*
962 * There are only 31 bits worth of block count in
963 * the buffer cache. So for large volumes a 4K
964 * physical block size is needed.
965 */
966 if (blksize == 512 && blkcnt > (u_int64_t)0x000000007fffffff) {
967 blksize = 4096;
968 }
969
970 /*
971 * At this point:
972 * blksize has our prefered physical block size
973 * blkcnt has the total number of physical blocks
974 */
975
976 buff = (char *)malloc(blksize);
977
978 hdr_offset = HFS_PRI_SECTOR(blksize)*blksize;
979 if (pread(fd, buff, blksize, hdr_offset) != blksize) {
980 fprintf(stderr, "failed to read volume header @ offset %lld (%s)\n",
981 hdr_offset, strerror(errno));
982 ret = -1;
983 goto out;
984 }
985
986 // if we read in an empty bunch of junk at location zero, then
987 // retry at offset 0x400 which is where the header normally is.
988 if (*(int *)buff == 0 && hdr_offset == 0) {
989 hdr_offset = 0x400;
990 if (pread(fd, buff, blksize, hdr_offset) != blksize) {
991 fprintf(stderr, "failed to read volume header @ offset %lld (%s)\n",
992 hdr_offset, strerror(errno));
993 ret = -1;
994 goto out;
995 }
996 }
997
998
999
1000 mdbp = (HFSMasterDirectoryBlock *)buff;
1001 if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) && (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord)) {
1002 // normal hfs can not ever be journaled
1003 fprintf(stderr, "disable_journaling: volume is only regular HFS, not HFS+\n");
1004 goto out;
1005 }
1006
1007 /* Get the embedded Volume Header */
1008 if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) {
1009 embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * 512;
1010 embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) * (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
1011
1012 /*
1013 * If the embedded volume doesn't start on a block
1014 * boundary, then switch the device to a 512-byte
1015 * block size so everything will line up on a block
1016 * boundary.
1017 */
1018 if ((embeddedOffset % blksize) != 0) {
1019 fprintf(stderr, "HFS Mount: embedded volume offset not"
1020 " a multiple of physical block size (%d);"
1021 " switching to 512\n", blksize);
1022
1023 blkcnt *= (blksize / 512);
1024 blksize = 512;
1025 }
1026
1027 disksize = (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.blockCount) * (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
1028
1029 mdb_offset = (embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize);
1030 hdr_offset = mdb_offset * blksize;
1031 if (pread(fd, buff, blksize, hdr_offset) != blksize) {
1032 fprintf(stderr, "failed to read the embedded vhp @ offset %d\n", mdb_offset * blksize);
1033 ret = -1;
1034 goto out;
1035 }
1036
1037 vhp = (HFSPlusVolumeHeader*) buff;
1038 } else /* pure HFS+ */ {
1039 embeddedOffset = 0;
1040 vhp = (HFSPlusVolumeHeader*) mdbp;
1041 }
1042
1043
1044 if ((SWAP_BE32(vhp->attributes) & kHFSVolumeJournaledMask) != 0) {
1045 unsigned int tmp = SWAP_BE32(vhp->attributes);
1046
1047 tmp &= ~kHFSVolumeJournaledMask;
1048 vhp->attributes = SWAP_BE32(tmp);
1049 if ((tmp = pwrite(fd, buff, blksize, hdr_offset)) != blksize) {
1050 fprintf(stderr, "Update of super-block on %s failed! (%d != %d, %s)\n",
1051 devname, tmp, blksize, strerror(errno));
1052 } else {
1053 fprintf(stderr, "Turned off the journaling bit for %s\n", devname);
1054 }
1055 } else {
1056 fprintf(stderr, "disable_journaling: %s is not journaled.\n", devname);
1057 }
1058
1059
1060 out:
1061 if (buff)
1062 free(buff);
1063 if (fd >= 0)
1064 close(fd);
1065
1066 return ret;
1067 }
1068
1069
1070
1071 int
1072 SetJournalInFSState(const char *devname, int journal_in_fs)
1073 {
1074 int fd = -1, ret = 0;
1075 char *buff = NULL, *buff2 = NULL;
1076 u_int64_t blkcnt;
1077 u_int32_t blksize;
1078 daddr_t mdb_offset;
1079 HFSMasterDirectoryBlock *mdbp;
1080 HFSPlusVolumeHeader *vhp;
1081 off_t embeddedOffset, pos;
1082 struct JournalInfoBlock *myjib;
1083
1084 fd = open(devname, O_RDWR);
1085 if (fd < 0) {
1086 printf("can't open: %s (%s)\n", devname, strerror(errno));
1087 ret = -1;
1088 goto out;
1089 }
1090
1091 /* Get the real physical block size. */
1092 if (ioctl(fd, DKIOCGETBLOCKSIZE, (caddr_t)&blksize) != 0) {
1093 printf("can't get the device block size (%s). assuming 512\n", strerror(errno));
1094 blksize = 512;
1095 ret = -1;
1096 goto out;
1097 }
1098
1099 /* Get the number of physical blocks. */
1100 if (ioctl(fd, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt)) {
1101 struct stat st;
1102 printf("failed to get block count. trying stat().\n");
1103 if (fstat(fd, &st) != 0) {
1104 ret = -1;
1105 goto out;
1106 }
1107
1108 blkcnt = st.st_size / blksize;
1109 }
1110
1111 /*
1112 * There used to only be 31 bits worth of block count in
1113 * the buffer cache. So for large volumes a 4K
1114 * physical block size is needed.
1115 */
1116 if (blksize == 512 && blkcnt > (u_int64_t)0x000000007fffffff) {
1117 blksize = 4096;
1118 }
1119
1120 /*
1121 * At this point:
1122 * blksize has our prefered physical block size
1123 * blkcnt has the total number of physical blocks
1124 */
1125
1126 buff = (char *)malloc(blksize);
1127 buff2 = (char *)malloc(blksize);
1128
1129 if (pread(fd, buff, blksize, HFS_PRI_SECTOR(blksize)*blksize) != blksize) {
1130 printf("failed to read volume header @ offset %d (%s)\n",
1131 HFS_PRI_SECTOR(blksize), strerror(errno));
1132 ret = -1;
1133 goto out;
1134 }
1135
1136 if (blksize == 512) {
1137 mdbp = (HFSMasterDirectoryBlock *)buff;
1138 } else {
1139 mdbp = (HFSMasterDirectoryBlock *)(buff + 1024);
1140 }
1141
1142 // first check if it's even hfs at all...
1143 if ( SWAP_BE16(mdbp->drSigWord) != kHFSSigWord
1144 && SWAP_BE16(mdbp->drSigWord) != kHFSPlusSigWord
1145 && SWAP_BE16(mdbp->drSigWord) != kHFSXSigWord) {
1146
1147 ret = -1;
1148 goto out;
1149 }
1150
1151 if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) && (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord)) {
1152 // normal hfs can not ever be journaled
1153 goto out;
1154 }
1155
1156 /* Get the embedded Volume Header */
1157 if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) {
1158 embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * 512;
1159 embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) *
1160 (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
1161
1162 /*
1163 * If the embedded volume doesn't start on a block
1164 * boundary, then switch the device to a 512-byte
1165 * block size so everything will line up on a block
1166 * boundary.
1167 */
1168 if ((embeddedOffset % blksize) != 0) {
1169 printf("HFS Mount: embedded volume offset not"
1170 " a multiple of physical block size (%d);"
1171 " switching to 512\n", blksize);
1172
1173 blkcnt *= (blksize / 512);
1174 blksize = 512;
1175 }
1176
1177 mdb_offset = (embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize);
1178 if (pread(fd, buff, blksize, mdb_offset * blksize) != blksize) {
1179 printf("failed to read the embedded vhp @ offset %d\n", mdb_offset * blksize);
1180 ret = -1;
1181 goto out;
1182 }
1183
1184 vhp = (HFSPlusVolumeHeader*) buff;
1185 } else /* pure HFS+ */ {
1186 embeddedOffset = 0;
1187 vhp = (HFSPlusVolumeHeader*) mdbp;
1188 }
1189
1190 //printf("vol header attributes: 0x%x\n", SWAP_BE32(vhp->attributes));
1191 if ((SWAP_BE32(vhp->attributes) & kHFSVolumeJournaledMask) == 0) {
1192 ret = 0;
1193 goto out;
1194 }
1195
1196 //
1197 // Now read the journal info block... (when calculating the
1198 // position, make sure to cast to unsigned first, then to
1199 // off_t so that things don't get sign-extended improperly
1200 // or truncated).
1201 //
1202 pos = (off_t)((off_t)embeddedOffset +
1203 (off_t)((unsigned int )SWAP_BE32(vhp->journalInfoBlock))*(off_t)((unsigned int)SWAP_BE32(vhp->blockSize)));
1204
1205 if (pread(fd, buff2, blksize, pos) != blksize) {
1206 printf("failed to read the journal info block (%s).\n", strerror(errno));
1207 ret = -1;
1208 goto out;
1209 }
1210
1211 myjib = (struct JournalInfoBlock *)buff2;
1212
1213 // swap this to host native format so we can diddle with the bits
1214 myjib->flags = SWAP_BE32(myjib->flags);
1215
1216 if (journal_in_fs) {
1217 myjib->flags |= kJIJournalInFSMask;
1218 } else {
1219 myjib->flags &= ~kJIJournalInFSMask;
1220 }
1221 myjib->flags |= kJIJournalNeedInitMask;
1222
1223 // and now swap back before writing it out
1224 myjib->flags = SWAP_BE32(myjib->flags);
1225
1226 if (pwrite(fd, buff2, blksize, pos) != blksize) {
1227 printf("failed to re-write the journal info block (%s).\n", strerror(errno));
1228 ret = -1;
1229 goto out;
1230 }
1231
1232 ret = 0;
1233
1234 out:
1235 if (buff)
1236 free(buff);
1237 if (buff2)
1238 free(buff2);
1239 if (fd >= 0)
1240 close(fd);
1241
1242 return ret;
1243 }