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