2 * Copyright (c) 1999-2001 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 Copyright (c) 2002 Apple Computer, Inc.
29 This file contains the routine to make an HFS+ volume journaled
30 and a corresponding routine to turn it off.
34 #include <sys/types.h>
38 #include <sys/sysctl.h>
39 #include <sys/resource.h>
40 #include <sys/vmmeter.h>
41 #include <sys/mount.h>
45 #include <sys/loadable_fs.h>
46 #include <hfs/hfs_format.h>
47 #include <hfs/hfs_mount.h> /* for hfs sysctl values */
58 #include <architecture/byte_order.h>
60 // just in case these aren't in <hfs/hfs_mount.h> yet
61 #ifndef HFS_ENABLE_JOURNALING
62 #define HFS_ENABLE_JOURNALING 0x082969
64 #ifndef HFS_DISABLE_JOURNALING
65 #define HFS_DISABLE_JOURNALING 0x031272
67 #ifndef HFS_GET_JOURNAL_INFO
68 #define HFS_GET_JOURNAL_INFO 0x6a6e6c69
71 /* getattrlist buffers start with an extra length field */
72 struct ExtentsAttrBuf
{
73 unsigned long infoLength
;
74 HFSPlusExtentRecord extents
;
76 typedef struct ExtentsAttrBuf ExtentsAttrBuf
;
80 #define kIsInvisible 0x4000
83 * Generic Finder file/dir data
86 u_int32_t opaque_1
[2];
87 u_int16_t fdFlags
; /* Finder flags */
90 typedef struct FinderInfo FinderInfo
;
92 /* getattrlist buffers start with an extra length field */
93 struct FinderAttrBuf
{
94 unsigned long infoLength
;
95 FinderInfo finderInfo
;
97 typedef struct FinderAttrBuf FinderAttrBuf
;
100 int hide_file(const char * file
)
102 struct attrlist alist
= {0};
103 FinderAttrBuf finderInfoBuf
= {0};
106 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
107 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
109 result
= getattrlist(file
, &alist
, &finderInfoBuf
, sizeof(finderInfoBuf
), 0);
114 if (finderInfoBuf
.finderInfo
.fdFlags
& kIsInvisible
) {
115 printf("hide: %s is alreadly invisible\n", file
);
119 finderInfoBuf
.finderInfo
.fdFlags
|= kIsInvisible
;
121 result
= setattrlist(file
, &alist
, &finderInfoBuf
.finderInfo
, sizeof(FinderInfo
), 0);
123 return (result
== -1 ? errno
: result
);
127 get_start_block(const char *file
)
129 struct attrlist alist
= {0};
130 ExtentsAttrBuf extentsbuf
= {0};
132 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
133 alist
.fileattr
= ATTR_FILE_DATAEXTENTS
;
135 if (getattrlist(file
, &alist
, &extentsbuf
, sizeof(extentsbuf
), 0)) {
136 fprintf(stderr
, "could not get attrlist for %s (%s)", file
, strerror(errno
));
140 if (extentsbuf
.extents
[1].startBlock
!= 0) {
141 fprintf(stderr
, "Journal File not contiguous!\n");
145 return extentsbuf
.extents
[0].startBlock
;
148 static const char *journal_fname
= ".journal";
149 static const char *jib_fname
= ".journal_info_block";
152 DoMakeJournaled(char *volname
, int jsize
)
154 int fd
, i
, block_size
, journal_size
= 8*1024*1024;
158 int jstart_block
, jinfo_block
, sysctl_info
[8];
159 JournalInfoBlock jib
;
161 static char tmpname
[MAXPATHLEN
];
163 if (statfs(volname
, &sfs
) != 0) {
164 fprintf(stderr
, "Can't stat volume %s (%s).\n", volname
, strerror(errno
));
168 // Make sure that we're HFS+. First we check the fstypename.
169 // If that's ok then we try to create a symlink (which won't
170 // work on plain hfs volumes but will work on hfs+ volumes).
172 sprintf(tmpname
, "%s/is_vol_hfs_plus", volname
);
173 if (strcmp(sfs
.f_fstypename
, "hfs") != 0 ||
174 ((ret
= symlink(tmpname
, tmpname
)) != 0 && errno
== ENOTSUP
)) {
175 fprintf(stderr
, "%s is not an HFS+ volume. Journaling only works on HFS+ volumes.\n",
181 if (sfs
.f_flags
& MNT_JOURNALED
) {
182 fprintf(stderr
, "Volume %s is already journaled.\n", volname
);
187 journal_size
= jsize
;
192 // we want at least 8 megs of journal for each 100 gigs of
193 // disk space. We cap the size at 512 megs though.
195 scale
= ((long long)sfs
.f_bsize
* (long long)((unsigned)sfs
.f_blocks
)) / (100*1024*1024*1024ULL);
196 journal_size
*= (scale
+ 1);
197 if (journal_size
> 512 * 1024 * 1024) {
198 journal_size
= 512 * 1024 * 1024;
202 if (chdir(volname
) != 0) {
203 fprintf(stderr
, "Can't locate volume %s to make it journaled (%s).\n",
204 volname
, strerror(errno
));
208 fd
= open(journal_fname
, O_CREAT
|O_TRUNC
|O_RDWR
, 000);
210 fprintf(stderr
, "Can't create journal file on volume %s (%s)\n",
211 volname
, strerror(errno
));
215 // make sure that it has no r/w/x privs (only could happen if
216 // the file already existed since open() doesn't reset the mode
221 block_size
= sfs
.f_bsize
;
222 if ((journal_size
% block_size
) != 0) {
223 fprintf(stderr
, "Journal size %dk is not a multiple of volume %s block size (%d).\n",
224 journal_size
/1024, volname
, block_size
);
226 unlink(journal_fname
);
231 memset(&fst
, 0, sizeof(fst
));
232 fst
.fst_flags
= F_ALLOCATECONTIG
|F_ALLOCATEALL
;
233 fst
.fst_length
= journal_size
;
234 fst
.fst_posmode
= F_PEOFPOSMODE
;
236 ret
= fcntl(fd
, F_PREALLOCATE
, &fst
);
238 if (journal_size
>= 2*1024*1024) {
239 fprintf(stderr
, "Not enough contiguous space for a %d k journal. Retrying.\n",
242 ftruncate(fd
, 0); // make sure the file is zero bytes long.
245 fprintf(stderr
, "Disk too fragmented to enable journaling.\n");
246 fprintf(stderr
, "Please run a defragmenter on %s.\n", volname
);
248 unlink(journal_fname
);
253 printf("Allocated %lldK for journal file.\n", fst
.fst_bytesalloc
/1024LL);
254 buf
= (char *)calloc(block_size
, 1);
256 for(i
=0; i
< journal_size
/block_size
; i
++) {
257 ret
= write(fd
, buf
, block_size
);
258 if (ret
!= block_size
) {
263 if (i
*block_size
!= journal_size
) {
264 fprintf(stderr
, "Failed to write %dk to journal on volume %s (%s)\n",
265 journal_size
/1024, volname
, strerror(errno
));
268 printf("Could not allocate memory to write to the journal on volume %s (%s)\n",
269 volname
, strerror(errno
));
274 hide_file(journal_fname
);
276 jstart_block
= get_start_block(journal_fname
);
278 memset(&jib
, 0, sizeof(jib
));
279 jib
.flags
= kJIJournalInFSMask
;
280 jib
.offset
= (off_t
)((unsigned)jstart_block
) * (off_t
)((unsigned)block_size
);
281 jib
.size
= (off_t
)((unsigned)journal_size
);
283 fd
= open(jib_fname
, O_CREAT
|O_TRUNC
|O_RDWR
, 000);
285 fprintf(stderr
, "Could not create journal info block file on volume %s (%s)\n",
286 volname
, strerror(errno
));
287 unlink(journal_fname
);
291 // swap the data before we copy it
292 jib
.flags
= NXSwapBigLongToHost(jib
.flags
);
293 jib
.offset
= NXSwapBigLongLongToHost(jib
.offset
);
294 jib
.size
= NXSwapBigLongLongToHost(jib
.size
);
296 memcpy(buf
, &jib
, sizeof(jib
));
298 // now put it back the way it was
299 jib
.size
= NXSwapBigLongLongToHost(jib
.size
);
300 jib
.offset
= NXSwapBigLongLongToHost(jib
.offset
);
301 jib
.flags
= NXSwapBigLongToHost(jib
.flags
);
303 if (write(fd
, buf
, block_size
) != block_size
) {
304 fprintf(stderr
, "Failed to write journal info block on volume %s (%s)!\n",
305 volname
, strerror(errno
));
306 unlink(journal_fname
);
312 hide_file(jib_fname
);
314 jinfo_block
= get_start_block(jib_fname
);
318 // Now make the volume journaled!
320 memset(sysctl_info
, 0, sizeof(sysctl_info
));
321 sysctl_info
[0] = CTL_VFS
;
322 sysctl_info
[1] = sfs
.f_fsid
.val
[1];
323 sysctl_info
[2] = HFS_ENABLE_JOURNALING
;
324 sysctl_info
[3] = jinfo_block
;
325 sysctl_info
[4] = jstart_block
;
326 sysctl_info
[5] = journal_size
;
328 //printf("fs type: 0x%x\n", sysctl_info[1]);
329 //printf("jinfo block : 0x%x\n", jinfo_block);
330 //printf("jstart block: 0x%x\n", jstart_block);
331 //printf("journal size: 0x%x\n", journal_size);
333 ret
= sysctl((void *)sysctl_info
, 6, NULL
, NULL
, NULL
, 0);
335 fprintf(stderr
, "Failed to make volume %s journaled (%s)\n",
336 volname
, strerror(errno
));
337 unlink(journal_fname
);
347 DoUnJournal(char *volname
)
352 char jbuf
[MAXPATHLEN
];
354 if (statfs(volname
, &sfs
) != 0) {
355 fprintf(stderr
, "Can't stat volume %s (%s).\n", volname
, strerror(errno
));
359 if ((sfs
.f_flags
& MNT_JOURNALED
) == 0) {
360 fprintf(stderr
, "Volume %s is not journaled.\n", volname
);
364 if (chdir(volname
) != 0) {
365 fprintf(stderr
, "Can't locate volume %s to turn off journaling (%s).\n",
366 volname
, strerror(errno
));
370 memset(sysctl_info
, 0, sizeof(sysctl_info
));
371 sysctl_info
[0] = CTL_VFS
;
372 sysctl_info
[1] = sfs
.f_fsid
.val
[1];
373 sysctl_info
[2] = HFS_DISABLE_JOURNALING
;
375 result
= sysctl((void *)sysctl_info
, 3, NULL
, NULL
, NULL
, 0);
377 fprintf(stderr
, "Failed to make volume %s UN-journaled (%s)\n",
378 volname
, strerror(errno
));
382 sprintf(jbuf
, "%s/%s", volname
, journal_fname
);
383 if (unlink(jbuf
) != 0) {
384 fprintf(stderr
, "Failed to remove the journal %s (%s)\n",
385 jbuf
, strerror(errno
));
388 sprintf(jbuf
, "%s/%s", volname
, jib_fname
);
389 if (unlink(jbuf
) != 0) {
390 fprintf(stderr
, "Failed to remove the journal info block %s (%s)\n",
391 jbuf
, strerror(errno
));
394 printf("Journaling disabled on %s\n", volname
);
401 DoGetJournalInfo(char *volname
)
408 if (statfs(volname
, &sfs
) != 0) {
409 fprintf(stderr
, "Can't stat volume %s (%s).\n", volname
, strerror(errno
));
413 if ((sfs
.f_flags
& MNT_JOURNALED
) == 0) {
414 fprintf(stderr
, "Volume %s is not journaled.\n", volname
);
418 if (chdir(volname
) != 0) {
419 fprintf(stderr
, "Can't cd to volume %s to get journal info (%s).\n",
420 volname
, strerror(errno
));
424 memset(sysctl_info
, 0, sizeof(sysctl_info
));
425 sysctl_info
[0] = CTL_VFS
;
426 sysctl_info
[1] = sfs
.f_fsid
.val
[1];
427 sysctl_info
[2] = HFS_GET_JOURNAL_INFO
;
428 sysctl_info
[3] = (int)&jstart
;
429 sysctl_info
[4] = (int)&jsize
;
431 result
= sysctl((void *)sysctl_info
, 5, NULL
, NULL
, NULL
, 0);
433 fprintf(stderr
, "Failed to get journal info for volume %s (%s)\n",
434 volname
, strerror(errno
));
439 printf("%s : not journaled.\n", volname
);
441 printf("%s : journal size %lld k at offset 0x%llx\n", volname
, jsize
/1024, jstart
);