]> git.saurik.com Git - apple/hfs.git/blob - hfs_util/hfsutil_jnl.c
2a619e87134568ea1e7a751c447e4e71fa4e6f4c
[apple/hfs.git] / hfs_util / hfsutil_jnl.c
1 /*
2 * Copyright (c) 1999-2001 Apple Computer, 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 <errno.h>
48 #include <fcntl.h>
49 #include <libgen.h>
50 #include <pwd.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55
56 #include <architecture/byte_order.h>
57
58 // just in case these aren't in <hfs/hfs_mount.h> yet
59 #ifndef HFS_ENABLE_JOURNALING
60 #define HFS_ENABLE_JOURNALING 0x082969
61 #endif
62 #ifndef HFS_DISABLE_JOURNALING
63 #define HFS_DISABLE_JOURNALING 0x031272
64 #endif
65 #ifndef HFS_GET_JOURNAL_INFO
66 #define HFS_GET_JOURNAL_INFO 0x6a6e6c69
67 #endif
68
69 /* getattrlist buffers start with an extra length field */
70 struct ExtentsAttrBuf {
71 unsigned long infoLength;
72 HFSPlusExtentRecord extents;
73 };
74 typedef struct ExtentsAttrBuf ExtentsAttrBuf;
75
76
77
78 #define kIsInvisible 0x4000
79
80 /*
81 * Generic Finder file/dir data
82 */
83 struct FinderInfo {
84 u_int32_t opaque_1[2];
85 u_int16_t fdFlags; /* Finder flags */
86 int16_t opaque_2[11];
87 };
88 typedef struct FinderInfo FinderInfo;
89
90 /* getattrlist buffers start with an extra length field */
91 struct FinderAttrBuf {
92 unsigned long infoLength;
93 FinderInfo finderInfo;
94 };
95 typedef struct FinderAttrBuf FinderAttrBuf;
96
97
98 int hide_file(const char * file)
99 {
100 struct attrlist alist = {0};
101 FinderAttrBuf finderInfoBuf = {0};
102 int result;
103
104 alist.bitmapcount = ATTR_BIT_MAP_COUNT;
105 alist.commonattr = ATTR_CMN_FNDRINFO;
106
107 result = getattrlist(file, &alist, &finderInfoBuf, sizeof(finderInfoBuf), 0);
108 if (result) {
109 return (errno);
110 }
111
112 if (finderInfoBuf.finderInfo.fdFlags & kIsInvisible) {
113 printf("hide: %s is alreadly invisible\n", file);
114 return (0);
115 }
116
117 finderInfoBuf.finderInfo.fdFlags |= kIsInvisible;
118
119 result = setattrlist(file, &alist, &finderInfoBuf.finderInfo, sizeof(FinderInfo), 0);
120
121 return (result == -1 ? errno : result);
122 }
123
124 off_t
125 get_start_block(const char *file, uint32_t fs_block_size)
126 {
127 off_t cur_pos, phys_start, len;
128 int fd, err;
129 struct log2phys l2p;
130 struct stat st;
131
132 fd = open(file, O_RDONLY);
133 if (fd < 0) {
134 return -1;
135 }
136
137 if (fstat(fd, &st) < 0) {
138 fprintf(stderr, "can't stat %s (%s)\n", file, strerror(errno));
139 close(fd);
140 return -1;
141 }
142
143 fs_block_size = st.st_blksize; // XXXdbg quick hack for now
144
145 phys_start = len = 0;
146 for(cur_pos=0; cur_pos < st.st_size; cur_pos += fs_block_size) {
147 memset(&l2p, 0, sizeof(l2p));
148 lseek(fd, cur_pos, SEEK_SET);
149 err = fcntl(fd, F_LOG2PHYS, &l2p);
150
151 if (phys_start == 0) {
152 phys_start = l2p.l2p_devoffset;
153 len = fs_block_size;
154 } else if (l2p.l2p_devoffset != (phys_start + len)) {
155 // printf(" %lld : %lld - %lld\n", cur_pos, phys_start / fs_block_size, len / fs_block_size);
156 fprintf(stderr, "%s : is not contiguous!\n", file);
157 close(fd);
158 return -1;
159 // phys_start = l2p.l2p_devoffset;
160 // len = fs_block_size;
161 } else {
162 len += fs_block_size;
163 }
164 }
165
166 close(fd);
167
168 //printf("%s start offset %lld; byte len %lld (blksize %d)\n",
169 // file, phys_start, len, fs_block_size);
170
171 if ((phys_start / (unsigned)fs_block_size) & 0xffffffff00000000LL) {
172 fprintf(stderr, "%s : starting block is > 32bits!\n", file);
173 return -1;
174 }
175
176 return phys_start;
177 }
178
179
180 //
181 // Get the embedded offset (if any) for an hfs+ volume.
182 // This is pretty skanky that we have to do this but
183 // that's life...
184 //
185 #include <sys/disk.h>
186 #include <hfs/hfs_format.h>
187
188 #include <machine/endian.h>
189
190 #define HFS_PRI_SECTOR(blksize) (1024 / (blksize))
191 #define HFS_PRI_OFFSET(blksize) ((blksize) > 1024 ? 1024 : 0)
192
193 #define SWAP_BE16(x) ntohs(x)
194 #define SWAP_BE32(x) ntohl(x)
195
196
197 off_t
198 get_embedded_offset(char *devname)
199 {
200 int fd = -1;
201 off_t ret = 0;
202 char *buff = NULL, rawdev[256];
203 u_int64_t blkcnt;
204 u_int32_t blksize;
205 HFSMasterDirectoryBlock *mdbp;
206 off_t embeddedOffset;
207 struct statfs sfs;
208 struct stat st;
209
210 restart:
211 if (stat(devname, &st) != 0) {
212 fprintf(stderr, "Could not access %s (%s)\n", devname, strerror(errno));
213 ret = -1;
214 goto out;
215 }
216
217 if (S_ISCHR(st.st_mode) == 0) {
218 // hmmm, it's not the character special raw device so we
219 // should try to figure out the real device.
220 if (statfs(devname, &sfs) != 0) {
221 fprintf(stderr, "Can't find out any info about the fs for path %s (%s)\n",
222 devname, strerror(errno));
223 ret = -1;
224 goto out;
225 }
226
227 // copy the "/dev/"
228 strncpy(rawdev, sfs.f_mntfromname, 5);
229 rawdev[5] = 'r';
230 strcpy(&rawdev[6], &sfs.f_mntfromname[5]);
231 devname = &rawdev[0];
232 goto restart;
233 }
234
235 fd = open(devname, O_RDONLY);
236 if (fd < 0) {
237 fprintf(stderr, "can't open: %s (%s)\n", devname, strerror(errno));
238 ret = -1;
239 goto out;
240 }
241
242 /* Get the real physical block size. */
243 if (ioctl(fd, DKIOCGETBLOCKSIZE, (caddr_t)&blksize) != 0) {
244 fprintf(stderr, "can't get the device block size (%s). assuming 512\n", strerror(errno));
245 blksize = 512;
246 ret = -1;
247 goto out;
248 }
249
250 /* Get the number of physical blocks. */
251 if (ioctl(fd, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt)) {
252 struct stat st;
253 fprintf(stderr, "failed to get block count. trying stat().\n");
254 if (fstat(fd, &st) != 0) {
255 ret = -1;
256 goto out;
257 }
258
259 blkcnt = st.st_size / blksize;
260 }
261
262 /*
263 * There are only 31 bits worth of block count in
264 * the buffer cache. So for large volumes a 4K
265 * physical block size is needed.
266 */
267 if (blksize == 512 && blkcnt > (u_int64_t)0x000000007fffffff) {
268 blksize = 4096;
269 }
270
271 /*
272 * At this point:
273 * blksize has our prefered physical block size
274 * blkcnt has the total number of physical blocks
275 */
276
277 buff = (char *)malloc(blksize);
278
279 if (pread(fd, buff, blksize, HFS_PRI_SECTOR(blksize)*blksize) != blksize) {
280 fprintf(stderr, "failed to read volume header @ offset %d (%s)\n",
281 HFS_PRI_SECTOR(blksize), strerror(errno));
282 ret = -1;
283 goto out;
284 }
285
286 mdbp = (HFSMasterDirectoryBlock *)buff;
287 if ( (SWAP_BE16(mdbp->drSigWord) != kHFSSigWord)
288 && (SWAP_BE16(mdbp->drSigWord) != kHFSPlusSigWord)
289 && (SWAP_BE16(mdbp->drSigWord) != kHFSXSigWord)) {
290 ret = -1;
291 goto out;
292 }
293
294 if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) && (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord)) {
295 ret = -1;
296 goto out;
297 } else if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) {
298 /* Get the embedded Volume Header */
299 embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * 512;
300 embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) *
301 (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
302
303 /*
304 * If the embedded volume doesn't start on a block
305 * boundary, then switch the device to a 512-byte
306 * block size so everything will line up on a block
307 * boundary.
308 */
309 if ((embeddedOffset % blksize) != 0) {
310 fprintf(stderr, "HFS Mount: embedded volume offset not"
311 " a multiple of physical block size (%d);"
312 " switching to 512\n", blksize);
313
314 blkcnt *= (blksize / 512);
315 blksize = 512;
316 }
317
318 } else { /* pure HFS+ */
319 embeddedOffset = 0;
320 }
321
322 ret = embeddedOffset;
323
324 out:
325 if (buff) {
326 free(buff);
327 }
328 if (fd >= 0)
329 close(fd);
330
331 return ret;
332 }
333
334
335
336 static const char *journal_fname = ".journal";
337 static const char *jib_fname = ".journal_info_block";
338
339 int
340 DoMakeJournaled(char *volname, int jsize)
341 {
342 int fd, i, block_size, journal_size = 8*1024*1024;
343 char *buf;
344 int ret;
345 fstore_t fst;
346 int32_t jstart_block, jinfo_block;
347 int sysctl_info[8];
348 JournalInfoBlock jib;
349 struct statfs sfs;
350 static char tmpname[MAXPATHLEN];
351 off_t start_block, embedded_offset;
352
353 if (statfs(volname, &sfs) != 0) {
354 fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
355 return 10;
356 }
357
358 // Make sure that we're HFS+. First we check the fstypename.
359 // If that's ok then we try to create a symlink (which won't
360 // work on plain hfs volumes but will work on hfs+ volumes).
361 //
362 sprintf(tmpname, "%s/is_vol_hfs_plus", volname);
363 if (strcmp(sfs.f_fstypename, "hfs") != 0 ||
364 ((ret = symlink(tmpname, tmpname)) != 0 && errno == ENOTSUP)) {
365 fprintf(stderr, "%s is not an HFS+ volume. Journaling only works on HFS+ volumes.\n",
366 volname);
367 return 10;
368 }
369 unlink(tmpname);
370
371 if (sfs.f_flags & MNT_JOURNALED) {
372 fprintf(stderr, "Volume %s is already journaled.\n", volname);
373 return 1;
374 }
375
376 if (jsize != 0) {
377 journal_size = jsize;
378 } else {
379 int scale;
380
381 //
382 // we want at least 8 megs of journal for each 100 gigs of
383 // disk space. We cap the size at 512 megs though.
384 //
385 scale = ((long long)sfs.f_bsize * (long long)((unsigned)sfs.f_blocks)) / (100*1024*1024*1024ULL);
386 journal_size *= (scale + 1);
387 if (journal_size > 512 * 1024 * 1024) {
388 journal_size = 512 * 1024 * 1024;
389 }
390 }
391
392 if (chdir(volname) != 0) {
393 fprintf(stderr, "Can't locate volume %s to make it journaled (%s).\n",
394 volname, strerror(errno));
395 return 10;
396 }
397
398
399 embedded_offset = get_embedded_offset(volname);
400 if (embedded_offset < 0) {
401 fprintf(stderr, "Can't calculate the embedded offset (if any) for %s.\n", volname);
402 fprintf(stderr, "Journal creation failure.\n");
403 return 15;
404 }
405 // printf("Embedded offset == 0x%llx\n", embedded_offset);
406
407 fd = open(journal_fname, O_CREAT|O_TRUNC|O_RDWR, 000);
408 if (fd < 0) {
409 fprintf(stderr, "Can't create journal file on volume %s (%s)\n",
410 volname, strerror(errno));
411 return 5;
412 }
413
414 // make sure that it has no r/w/x privs (only could happen if
415 // the file already existed since open() doesn't reset the mode
416 // bits).
417 //
418 fchmod(fd, 0);
419
420 block_size = sfs.f_bsize;
421 if ((journal_size % block_size) != 0) {
422 fprintf(stderr, "Journal size %dk is not a multiple of volume %s block size (%d).\n",
423 journal_size/1024, volname, block_size);
424 close(fd);
425 unlink(journal_fname);
426 return 5;
427 }
428
429 retry:
430 memset(&fst, 0, sizeof(fst));
431 fst.fst_flags = F_ALLOCATECONTIG|F_ALLOCATEALL;
432 fst.fst_length = journal_size;
433 fst.fst_posmode = F_PEOFPOSMODE;
434
435 ret = fcntl(fd, F_PREALLOCATE, &fst);
436 if (ret < 0) {
437 if (journal_size >= 2*1024*1024) {
438 fprintf(stderr, "Not enough contiguous space for a %d k journal. Retrying.\n",
439 journal_size/1024);
440 journal_size /= 2;
441 ftruncate(fd, 0); // make sure the file is zero bytes long.
442 goto retry;
443 } else {
444 fprintf(stderr, "Disk too fragmented to enable journaling.\n");
445 fprintf(stderr, "Please run a defragmenter on %s.\n", volname);
446 close(fd);
447 unlink(journal_fname);
448 return 10;
449 }
450 }
451
452 printf("Allocated %lldK for journal file.\n", fst.fst_bytesalloc/1024LL);
453 buf = (char *)calloc(block_size, 1);
454 if (buf) {
455 for(i=0; i < journal_size/block_size; i++) {
456 ret = write(fd, buf, block_size);
457 if (ret != block_size) {
458 break;
459 }
460 }
461
462 if (i*block_size != journal_size) {
463 fprintf(stderr, "Failed to write %dk to journal on volume %s (%s)\n",
464 journal_size/1024, volname, strerror(errno));
465 }
466 } else {
467 printf("Could not allocate memory to write to the journal on volume %s (%s)\n",
468 volname, strerror(errno));
469 }
470
471 fsync(fd);
472 close(fd);
473 hide_file(journal_fname);
474
475 start_block = get_start_block(journal_fname, block_size);
476 if (start_block == (off_t)-1) {
477 fprintf(stderr, "Failed to get start block for %s (%s)\n",
478 journal_fname, strerror(errno));
479 unlink(journal_fname);
480 return 20;
481 }
482 jstart_block = (start_block / block_size) - (embedded_offset / block_size);
483
484 memset(&jib, 'Z', sizeof(jib));
485 jib.flags = kJIJournalInFSMask;
486 jib.offset = (off_t)((unsigned)jstart_block) * (off_t)((unsigned)block_size);
487 jib.size = (off_t)((unsigned)journal_size);
488
489 fd = open(jib_fname, O_CREAT|O_TRUNC|O_RDWR, 000);
490 if (fd < 0) {
491 fprintf(stderr, "Could not create journal info block file on volume %s (%s)\n",
492 volname, strerror(errno));
493 unlink(journal_fname);
494 return 5;
495 }
496
497 // swap the data before we copy it
498 jib.flags = NXSwapBigLongToHost(jib.flags);
499 jib.offset = NXSwapBigLongLongToHost(jib.offset);
500 jib.size = NXSwapBigLongLongToHost(jib.size);
501
502 memcpy(buf, &jib, sizeof(jib));
503
504 // now put it back the way it was
505 jib.size = NXSwapBigLongLongToHost(jib.size);
506 jib.offset = NXSwapBigLongLongToHost(jib.offset);
507 jib.flags = NXSwapBigLongToHost(jib.flags);
508
509 if (write(fd, buf, block_size) != block_size) {
510 fprintf(stderr, "Failed to write journal info block on volume %s (%s)!\n",
511 volname, strerror(errno));
512 unlink(journal_fname);
513 return 10;
514 }
515
516 fsync(fd);
517 close(fd);
518 hide_file(jib_fname);
519
520 start_block = get_start_block(jib_fname, block_size);
521 if (start_block == (off_t)-1) {
522 fprintf(stderr, "Failed to get start block for %s (%s)\n",
523 jib_fname, strerror(errno));
524 unlink(journal_fname);
525 unlink(jib_fname);
526 return 20;
527 }
528 jinfo_block = (start_block / block_size) - (embedded_offset / block_size);
529
530
531 //
532 // Now make the volume journaled!
533 //
534 memset(sysctl_info, 0, sizeof(sysctl_info));
535 sysctl_info[0] = CTL_VFS;
536 sysctl_info[1] = sfs.f_fsid.val[1];
537 sysctl_info[2] = HFS_ENABLE_JOURNALING;
538 sysctl_info[3] = jinfo_block;
539 sysctl_info[4] = jstart_block;
540 sysctl_info[5] = journal_size;
541
542 //printf("fs type: 0x%x\n", sysctl_info[1]);
543 //printf("jinfo block : 0x%x\n", jinfo_block);
544 //printf("jstart block: 0x%x\n", jstart_block);
545 //printf("journal size: 0x%x\n", journal_size);
546
547 ret = sysctl((void *)sysctl_info, 6, NULL, NULL, NULL, 0);
548 if (ret != 0) {
549 fprintf(stderr, "Failed to make volume %s journaled (%s)\n",
550 volname, strerror(errno));
551 unlink(journal_fname);
552 unlink(jib_fname);
553 return 20;
554 }
555
556 return 0;
557 }
558
559
560 int
561 DoUnJournal(char *volname)
562 {
563 int result;
564 int sysctl_info[8];
565 struct statfs sfs;
566 char jbuf[MAXPATHLEN];
567
568 if (statfs(volname, &sfs) != 0) {
569 fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
570 return 10;
571 }
572
573 if ((sfs.f_flags & MNT_JOURNALED) == 0) {
574 fprintf(stderr, "Volume %s is not journaled.\n", volname);
575 return 1;
576 }
577
578 if (chdir(volname) != 0) {
579 fprintf(stderr, "Can't locate volume %s to turn off journaling (%s).\n",
580 volname, strerror(errno));
581 return 10;
582 }
583
584 memset(sysctl_info, 0, sizeof(sysctl_info));
585 sysctl_info[0] = CTL_VFS;
586 sysctl_info[1] = sfs.f_fsid.val[1];
587 sysctl_info[2] = HFS_DISABLE_JOURNALING;
588
589 result = sysctl((void *)sysctl_info, 3, NULL, NULL, NULL, 0);
590 if (result != 0) {
591 fprintf(stderr, "Failed to make volume %s UN-journaled (%s)\n",
592 volname, strerror(errno));
593 return 20;
594 }
595
596 sprintf(jbuf, "%s/%s", volname, journal_fname);
597 if (unlink(jbuf) != 0) {
598 fprintf(stderr, "Failed to remove the journal %s (%s)\n",
599 jbuf, strerror(errno));
600 }
601
602 sprintf(jbuf, "%s/%s", volname, jib_fname);
603 if (unlink(jbuf) != 0) {
604 fprintf(stderr, "Failed to remove the journal info block %s (%s)\n",
605 jbuf, strerror(errno));
606 }
607
608 printf("Journaling disabled on %s\n", volname);
609
610 return 0;
611 }
612
613
614 int
615 DoGetJournalInfo(char *volname)
616 {
617 int result;
618 int sysctl_info[8];
619 struct statfs sfs;
620 off_t jstart, jsize;
621
622 if (statfs(volname, &sfs) != 0) {
623 fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
624 return 10;
625 }
626
627 if ((sfs.f_flags & MNT_JOURNALED) == 0) {
628 fprintf(stderr, "Volume %s is not journaled.\n", volname);
629 return 1;
630 }
631
632 if (chdir(volname) != 0) {
633 fprintf(stderr, "Can't cd to volume %s to get journal info (%s).\n",
634 volname, strerror(errno));
635 return 10;
636 }
637
638 memset(sysctl_info, 0, sizeof(sysctl_info));
639 sysctl_info[0] = CTL_VFS;
640 sysctl_info[1] = sfs.f_fsid.val[1];
641 sysctl_info[2] = HFS_GET_JOURNAL_INFO;
642 sysctl_info[3] = (int)&jstart;
643 sysctl_info[4] = (int)&jsize;
644
645 result = sysctl((void *)sysctl_info, 5, NULL, NULL, NULL, 0);
646 if (result != 0) {
647 fprintf(stderr, "Failed to get journal info for volume %s (%s)\n",
648 volname, strerror(errno));
649 return 20;
650 }
651
652 if (jsize == 0) {
653 printf("%s : not journaled.\n", volname);
654 } else {
655 printf("%s : journal size %lld k at offset 0x%llx\n", volname, jsize/1024, jstart);
656 }
657
658 return 0;
659 }