2 * Copyright (c) 1999-2001 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (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.
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
20 * @APPLE_LICENSE_HEADER_END@
23 Copyright (c) 2002 Apple Computer, Inc.
26 This file contains the routine to make an HFS+ volume journaled
27 and a corresponding routine to turn it off.
31 #include <sys/types.h>
35 #include <sys/sysctl.h>
36 #include <sys/resource.h>
37 #include <sys/vmmeter.h>
38 #include <sys/mount.h>
42 #include <sys/loadable_fs.h>
43 #include <hfs/hfs_format.h>
54 #include <architecture/byte_order.h>
57 // Secret HFS sysctl's to instruct it to turn
58 // journaling on/off for a volume.
60 #define HFS_BECOME_JOURNALED 0x082969
61 #define HFS_BECOME_UNJOURNALED 0x031272
64 /* getattrlist buffers start with an extra length field */
65 struct ExtentsAttrBuf
{
66 unsigned long infoLength
;
67 HFSPlusExtentRecord extents
;
69 typedef struct ExtentsAttrBuf ExtentsAttrBuf
;
73 #define kIsInvisible 0x4000
76 * Generic Finder file/dir data
79 u_int32_t opaque_1
[2];
80 u_int16_t fdFlags
; /* Finder flags */
83 typedef struct FinderInfo FinderInfo
;
85 /* getattrlist buffers start with an extra length field */
86 struct FinderAttrBuf
{
87 unsigned long infoLength
;
88 FinderInfo finderInfo
;
90 typedef struct FinderAttrBuf FinderAttrBuf
;
93 int hide_file(const char * file
)
95 struct attrlist alist
= {0};
96 FinderAttrBuf finderInfoBuf
= {0};
99 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
100 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
102 result
= getattrlist(file
, &alist
, &finderInfoBuf
, sizeof(finderInfoBuf
), 0);
107 if (finderInfoBuf
.finderInfo
.fdFlags
& kIsInvisible
) {
108 printf("hide: %s is alreadly invisible\n", file
);
112 finderInfoBuf
.finderInfo
.fdFlags
|= kIsInvisible
;
114 result
= setattrlist(file
, &alist
, &finderInfoBuf
.finderInfo
, sizeof(FinderInfo
), 0);
116 return (result
== -1 ? errno
: result
);
120 get_start_block(const char *file
)
122 struct attrlist alist
= {0};
123 ExtentsAttrBuf extentsbuf
= {0};
125 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
126 alist
.fileattr
= ATTR_FILE_DATAEXTENTS
;
128 if (getattrlist(file
, &alist
, &extentsbuf
, sizeof(extentsbuf
), 0)) {
129 fprintf(stderr
, "could not get attrlist for %s (%s)", file
, strerror(errno
));
133 if (extentsbuf
.extents
[1].startBlock
!= 0) {
134 fprintf(stderr
, "Journal File not contiguous!\n");
138 return extentsbuf
.extents
[0].startBlock
;
141 static const char *journal_fname
= ".journal";
142 static const char *jib_fname
= ".journal_info_block";
145 DoMakeJournaled(char *volname
, int jsize
)
147 int fd
, i
, block_size
, journal_size
= 8*1024*1024;
151 int jstart_block
, jinfo_block
, sysctl_info
[8];
152 JournalInfoBlock jib
;
154 static char tmpname
[MAXPATHLEN
];
157 journal_size
= jsize
;
160 if (statfs(volname
, &sfs
) != 0) {
161 fprintf(stderr
, "Can't stat volume %s (%s).\n", volname
, strerror(errno
));
165 // Make sure that we're HFS+. First we check the fstypename.
166 // If that's ok then we try to create a symlink (which won't
167 // work on plain hfs volumes but will work on hfs+ volumes).
169 sprintf(tmpname
, "%s/is_vol_hfs_plus", volname
);
170 if (strcmp(sfs
.f_fstypename
, "hfs") != 0 ||
171 ((ret
= symlink(tmpname
, tmpname
)) != 0 && errno
== ENOTSUP
)) {
172 fprintf(stderr
, "%s is not an HFS+ volume. Journaling only works on HFS+ volumes.\n",
178 if (sfs
.f_flags
& MNT_JOURNALED
) {
179 fprintf(stderr
, "Volume %s is already journaled.\n", volname
);
183 if (chdir(volname
) != 0) {
184 fprintf(stderr
, "Can't locate volume %s to make it journaled (%s).\n",
185 volname
, strerror(errno
));
189 fd
= open(journal_fname
, O_CREAT
|O_TRUNC
|O_RDWR
, 000);
191 fprintf(stderr
, "Can't create journal file on volume %s (%s)\n",
192 volname
, strerror(errno
));
196 // make sure that it has no r/w/x privs (only could happen if
197 // the file already existed since open() doesn't reset the mode
202 block_size
= sfs
.f_bsize
;
203 if ((journal_size
% block_size
) != 0) {
204 fprintf(stderr
, "Journal size %dk is not a multiple of volume %s block size (%d).\n",
205 journal_size
/1024, volname
, block_size
);
207 unlink(journal_fname
);
211 memset(&fst
, 0, sizeof(fst
));
212 fst
.fst_flags
= F_ALLOCATECONTIG
|F_ALLOCATEALL
;
213 fst
.fst_length
= journal_size
;
214 fst
.fst_posmode
= F_PEOFPOSMODE
;
216 ret
= fcntl(fd
, F_PREALLOCATE
, &fst
);
218 fprintf(stderr
, "Pre-allocating the journal file failed on volume %s (%s)\n",
219 volname
, strerror(errno
));
220 fprintf(stderr
, "Try using a smaller (%d k) journal size\n", journal_size
/2/1024);
222 unlink(journal_fname
);
226 printf("Allocated %lldK for journal file.\n", fst
.fst_bytesalloc
/1024LL);
227 buf
= (char *)calloc(block_size
, 1);
229 for(i
=0; i
< journal_size
/block_size
; i
++) {
230 ret
= write(fd
, buf
, block_size
);
231 if (ret
!= block_size
) {
236 if (i
*block_size
!= journal_size
) {
237 fprintf(stderr
, "Failed to write %dk to journal on volume %s (%s)\n",
238 journal_size
/1024, volname
, strerror(errno
));
241 printf("Could not allocate memory to write to the journal on volume %s (%s)\n",
242 volname
, strerror(errno
));
247 hide_file(journal_fname
);
249 jstart_block
= get_start_block(journal_fname
);
251 memset(&jib
, 0, sizeof(jib
));
252 jib
.flags
= kJIJournalInFSMask
;
253 jib
.offset
= (off_t
)jstart_block
* (off_t
)block_size
;
254 jib
.size
= (off_t
)journal_size
;
256 fd
= open(jib_fname
, O_CREAT
|O_TRUNC
|O_RDWR
, 000);
258 fprintf(stderr
, "Could not create journal info block file on volume %s (%s)\n",
259 volname
, strerror(errno
));
260 unlink(journal_fname
);
264 memcpy(buf
, &jib
, sizeof(jib
));
265 if (write(fd
, buf
, block_size
) != block_size
) {
266 fprintf(stderr
, "Failed to write journal info block on volume %s (%s)!\n",
267 volname
, strerror(errno
));
268 unlink(journal_fname
);
274 hide_file(jib_fname
);
276 jinfo_block
= get_start_block(jib_fname
);
280 // Now make the volume journaled!
282 memset(sysctl_info
, 0, sizeof(sysctl_info
));
283 sysctl_info
[0] = CTL_VFS
;
284 sysctl_info
[1] = sfs
.f_fsid
.val
[1];
285 sysctl_info
[2] = HFS_BECOME_JOURNALED
;
286 sysctl_info
[3] = jinfo_block
;
287 sysctl_info
[4] = jstart_block
;
288 sysctl_info
[5] = journal_size
;
290 //printf("fs type: 0x%x\n", sysctl_info[1]);
291 //printf("jinfo block : 0x%x\n", jinfo_block);
292 //printf("jstart block: 0x%x\n", jstart_block);
293 //printf("journal size: 0x%x\n", journal_size);
295 ret
= sysctl((void *)sysctl_info
, 6, NULL
, NULL
, NULL
, 0);
297 fprintf(stderr
, "Failed to make volume %s journaled (%s)\n",
298 volname
, strerror(errno
));
299 unlink(journal_fname
);
309 DoUnJournal(char *volname
)
314 char jbuf
[MAXPATHLEN
];
316 if (statfs(volname
, &sfs
) != 0) {
317 fprintf(stderr
, "Can't stat volume %s (%s).\n", volname
, strerror(errno
));
321 if ((sfs
.f_flags
& MNT_JOURNALED
) == 0) {
322 fprintf(stderr
, "Volume %s is not journaled.\n", volname
);
326 if (chdir(volname
) != 0) {
327 fprintf(stderr
, "Can't locate volume %s to turn off journaling (%s).\n",
328 volname
, strerror(errno
));
332 memset(sysctl_info
, 0, sizeof(sysctl_info
));
333 sysctl_info
[0] = CTL_VFS
;
334 sysctl_info
[1] = sfs
.f_fsid
.val
[1];
335 sysctl_info
[2] = HFS_BECOME_UNJOURNALED
;
337 result
= sysctl((void *)sysctl_info
, 6, NULL
, NULL
, NULL
, 0);
339 fprintf(stderr
, "Failed to make volume %s UN-journaled (%s)\n",
340 volname
, strerror(errno
));
344 sprintf(jbuf
, "%s/%s", volname
, journal_fname
);
345 if (unlink(jbuf
) != 0) {
346 fprintf(stderr
, "Failed to remove the journal %s (%s)\n",
347 jbuf
, strerror(errno
));
350 sprintf(jbuf
, "%s/%s", volname
, jib_fname
);
351 if (unlink(jbuf
) != 0) {
352 fprintf(stderr
, "Failed to remove the journal info block %s (%s)\n",
353 jbuf
, strerror(errno
));
356 printf("Journaling disabled on %s\n", volname
);