]> git.saurik.com Git - apple/hfs.git/blob - hfs_util/hfsutil_jnl.c
91cc13de60c0ba60c0a5a5a70cd527f30e888b4a
[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 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
13 * file.
14 *
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.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 Copyright (c) 2002 Apple Computer, Inc.
27 All Rights Reserved.
28
29 This file contains the routine to make an HFS+ volume journaled
30 and a corresponding routine to turn it off.
31
32 */
33
34 #include <sys/types.h>
35 #include <sys/attr.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <sys/sysctl.h>
39 #include <sys/resource.h>
40 #include <sys/vmmeter.h>
41 #include <sys/mount.h>
42 #include <sys/wait.h>
43
44 #include <sys/disk.h>
45 #include <sys/loadable_fs.h>
46 #include <hfs/hfs_format.h>
47 #include <hfs/hfs_mount.h> /* for hfs sysctl values */
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
79
80 #define kIsInvisible 0x4000
81
82 /*
83 * Generic Finder file/dir data
84 */
85 struct FinderInfo {
86 u_int32_t opaque_1[2];
87 u_int16_t fdFlags; /* Finder flags */
88 int16_t opaque_2[11];
89 };
90 typedef struct FinderInfo FinderInfo;
91
92 /* getattrlist buffers start with an extra length field */
93 struct FinderAttrBuf {
94 unsigned long infoLength;
95 FinderInfo finderInfo;
96 };
97 typedef struct FinderAttrBuf FinderAttrBuf;
98
99
100 int hide_file(const char * file)
101 {
102 struct attrlist alist = {0};
103 FinderAttrBuf finderInfoBuf = {0};
104 int result;
105
106 alist.bitmapcount = ATTR_BIT_MAP_COUNT;
107 alist.commonattr = ATTR_CMN_FNDRINFO;
108
109 result = getattrlist(file, &alist, &finderInfoBuf, sizeof(finderInfoBuf), 0);
110 if (result) {
111 return (errno);
112 }
113
114 if (finderInfoBuf.finderInfo.fdFlags & kIsInvisible) {
115 printf("hide: %s is alreadly invisible\n", file);
116 return (0);
117 }
118
119 finderInfoBuf.finderInfo.fdFlags |= kIsInvisible;
120
121 result = setattrlist(file, &alist, &finderInfoBuf.finderInfo, sizeof(FinderInfo), 0);
122
123 return (result == -1 ? errno : result);
124 }
125
126 int
127 get_start_block(const char *file)
128 {
129 struct attrlist alist = {0};
130 ExtentsAttrBuf extentsbuf = {0};
131
132 alist.bitmapcount = ATTR_BIT_MAP_COUNT;
133 alist.fileattr = ATTR_FILE_DATAEXTENTS;
134
135 if (getattrlist(file, &alist, &extentsbuf, sizeof(extentsbuf), 0)) {
136 fprintf(stderr, "could not get attrlist for %s (%s)", file, strerror(errno));
137 return -1;
138 }
139
140 if (extentsbuf.extents[1].startBlock != 0) {
141 fprintf(stderr, "Journal File not contiguous!\n");
142 return -1;
143 }
144
145 return extentsbuf.extents[0].startBlock;
146 }
147
148 static const char *journal_fname = ".journal";
149 static const char *jib_fname = ".journal_info_block";
150
151 int
152 DoMakeJournaled(char *volname, int jsize)
153 {
154 int fd, i, block_size, journal_size = 8*1024*1024;
155 char *buf;
156 int ret;
157 fstore_t fst;
158 int jstart_block, jinfo_block, sysctl_info[8];
159 JournalInfoBlock jib;
160 struct statfs sfs;
161 static char tmpname[MAXPATHLEN];
162
163 if (statfs(volname, &sfs) != 0) {
164 fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
165 return 10;
166 }
167
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).
171 //
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",
176 volname);
177 return 10;
178 }
179 unlink(tmpname);
180
181 if (sfs.f_flags & MNT_JOURNALED) {
182 fprintf(stderr, "Volume %s is already journaled.\n", volname);
183 return 1;
184 }
185
186 if (jsize != 0) {
187 journal_size = jsize;
188 } else {
189 int scale;
190
191 //
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.
194 //
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;
199 }
200 }
201
202 if (chdir(volname) != 0) {
203 fprintf(stderr, "Can't locate volume %s to make it journaled (%s).\n",
204 volname, strerror(errno));
205 return 10;
206 }
207
208 fd = open(journal_fname, O_CREAT|O_TRUNC|O_RDWR, 000);
209 if (fd < 0) {
210 fprintf(stderr, "Can't create journal file on volume %s (%s)\n",
211 volname, strerror(errno));
212 return 5;
213 }
214
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
217 // bits).
218 //
219 fchmod(fd, 0);
220
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);
225 close(fd);
226 unlink(journal_fname);
227 return 5;
228 }
229
230 retry:
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;
235
236 ret = fcntl(fd, F_PREALLOCATE, &fst);
237 if (ret < 0) {
238 if (journal_size >= 2*1024*1024) {
239 fprintf(stderr, "Not enough contiguous space for a %d k journal. Retrying.\n",
240 journal_size/1024);
241 journal_size /= 2;
242 ftruncate(fd, 0); // make sure the file is zero bytes long.
243 goto retry;
244 } else {
245 fprintf(stderr, "Disk too fragmented to enable journaling.\n");
246 fprintf(stderr, "Please run a defragmenter on %s.\n", volname);
247 close(fd);
248 unlink(journal_fname);
249 return 10;
250 }
251 }
252
253 printf("Allocated %lldK for journal file.\n", fst.fst_bytesalloc/1024LL);
254 buf = (char *)calloc(block_size, 1);
255 if (buf) {
256 for(i=0; i < journal_size/block_size; i++) {
257 ret = write(fd, buf, block_size);
258 if (ret != block_size) {
259 break;
260 }
261 }
262
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));
266 }
267 } else {
268 printf("Could not allocate memory to write to the journal on volume %s (%s)\n",
269 volname, strerror(errno));
270 }
271
272 fsync(fd);
273 close(fd);
274 hide_file(journal_fname);
275
276 jstart_block = get_start_block(journal_fname);
277
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);
282
283 fd = open(jib_fname, O_CREAT|O_TRUNC|O_RDWR, 000);
284 if (fd < 0) {
285 fprintf(stderr, "Could not create journal info block file on volume %s (%s)\n",
286 volname, strerror(errno));
287 unlink(journal_fname);
288 return 5;
289 }
290
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);
295
296 memcpy(buf, &jib, sizeof(jib));
297
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);
302
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);
307 return 10;
308 }
309
310 fsync(fd);
311 close(fd);
312 hide_file(jib_fname);
313
314 jinfo_block = get_start_block(jib_fname);
315
316
317 //
318 // Now make the volume journaled!
319 //
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;
327
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);
332
333 ret = sysctl((void *)sysctl_info, 6, NULL, NULL, NULL, 0);
334 if (ret != 0) {
335 fprintf(stderr, "Failed to make volume %s journaled (%s)\n",
336 volname, strerror(errno));
337 unlink(journal_fname);
338 unlink(jib_fname);
339 return 20;
340 }
341
342 return 0;
343 }
344
345
346 int
347 DoUnJournal(char *volname)
348 {
349 int result;
350 int sysctl_info[8];
351 struct statfs sfs;
352 char jbuf[MAXPATHLEN];
353
354 if (statfs(volname, &sfs) != 0) {
355 fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
356 return 10;
357 }
358
359 if ((sfs.f_flags & MNT_JOURNALED) == 0) {
360 fprintf(stderr, "Volume %s is not journaled.\n", volname);
361 return 1;
362 }
363
364 if (chdir(volname) != 0) {
365 fprintf(stderr, "Can't locate volume %s to turn off journaling (%s).\n",
366 volname, strerror(errno));
367 return 10;
368 }
369
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;
374
375 result = sysctl((void *)sysctl_info, 3, NULL, NULL, NULL, 0);
376 if (result != 0) {
377 fprintf(stderr, "Failed to make volume %s UN-journaled (%s)\n",
378 volname, strerror(errno));
379 return 20;
380 }
381
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));
386 }
387
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));
392 }
393
394 printf("Journaling disabled on %s\n", volname);
395
396 return 0;
397 }
398
399
400 int
401 DoGetJournalInfo(char *volname)
402 {
403 int result;
404 int sysctl_info[8];
405 struct statfs sfs;
406 off_t jstart, jsize;
407
408 if (statfs(volname, &sfs) != 0) {
409 fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
410 return 10;
411 }
412
413 if ((sfs.f_flags & MNT_JOURNALED) == 0) {
414 fprintf(stderr, "Volume %s is not journaled.\n", volname);
415 return 1;
416 }
417
418 if (chdir(volname) != 0) {
419 fprintf(stderr, "Can't cd to volume %s to get journal info (%s).\n",
420 volname, strerror(errno));
421 return 10;
422 }
423
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;
430
431 result = sysctl((void *)sysctl_info, 5, NULL, NULL, NULL, 0);
432 if (result != 0) {
433 fprintf(stderr, "Failed to get journal info for volume %s (%s)\n",
434 volname, strerror(errno));
435 return 20;
436 }
437
438 if (jsize == 0) {
439 printf("%s : not journaled.\n", volname);
440 } else {
441 printf("%s : journal size %lld k at offset 0x%llx\n", volname, jsize/1024, jstart);
442 }
443
444 return 0;
445 }