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