]> git.saurik.com Git - apple/hfs.git/blob - hfs_util/hfsutil_jnl.c
931fb74a18519b85a1c4ff9d2e6c29a799ce8f91
[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
41 #include <sys/disk.h>
42 #include <sys/loadable_fs.h>
43 #include <hfs/hfs_format.h>
44 #include <hfs/hfs_mount.h> /* for hfs sysctl values */
45
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <libgen.h>
49 #include <pwd.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54
55 #include <architecture/byte_order.h>
56
57 // just in case these aren't in <hfs/hfs_mount.h> yet
58 #ifndef HFS_ENABLE_JOURNALING
59 #define HFS_ENABLE_JOURNALING 0x082969
60 #endif
61 #ifndef HFS_DISABLE_JOURNALING
62 #define HFS_DISABLE_JOURNALING 0x031272
63 #endif
64 #ifndef HFS_GET_JOURNAL_INFO
65 #define HFS_GET_JOURNAL_INFO 0x6a6e6c69
66 #endif
67
68 /* getattrlist buffers start with an extra length field */
69 struct ExtentsAttrBuf {
70 unsigned long infoLength;
71 HFSPlusExtentRecord extents;
72 };
73 typedef struct ExtentsAttrBuf ExtentsAttrBuf;
74
75
76
77 #define kIsInvisible 0x4000
78
79 /*
80 * Generic Finder file/dir data
81 */
82 struct FinderInfo {
83 u_int32_t opaque_1[2];
84 u_int16_t fdFlags; /* Finder flags */
85 int16_t opaque_2[11];
86 };
87 typedef struct FinderInfo FinderInfo;
88
89 /* getattrlist buffers start with an extra length field */
90 struct FinderAttrBuf {
91 unsigned long infoLength;
92 FinderInfo finderInfo;
93 };
94 typedef struct FinderAttrBuf FinderAttrBuf;
95
96
97 int hide_file(const char * file)
98 {
99 struct attrlist alist = {0};
100 FinderAttrBuf finderInfoBuf = {0};
101 int result;
102
103 alist.bitmapcount = ATTR_BIT_MAP_COUNT;
104 alist.commonattr = ATTR_CMN_FNDRINFO;
105
106 result = getattrlist(file, &alist, &finderInfoBuf, sizeof(finderInfoBuf), 0);
107 if (result) {
108 return (errno);
109 }
110
111 if (finderInfoBuf.finderInfo.fdFlags & kIsInvisible) {
112 printf("hide: %s is alreadly invisible\n", file);
113 return (0);
114 }
115
116 finderInfoBuf.finderInfo.fdFlags |= kIsInvisible;
117
118 result = setattrlist(file, &alist, &finderInfoBuf.finderInfo, sizeof(FinderInfo), 0);
119
120 return (result == -1 ? errno : result);
121 }
122
123 int
124 get_start_block(const char *file)
125 {
126 struct attrlist alist = {0};
127 ExtentsAttrBuf extentsbuf = {0};
128
129 alist.bitmapcount = ATTR_BIT_MAP_COUNT;
130 alist.fileattr = ATTR_FILE_DATAEXTENTS;
131
132 if (getattrlist(file, &alist, &extentsbuf, sizeof(extentsbuf), 0)) {
133 fprintf(stderr, "could not get attrlist for %s (%s)", file, strerror(errno));
134 return -1;
135 }
136
137 if (extentsbuf.extents[1].startBlock != 0) {
138 fprintf(stderr, "Journal File not contiguous!\n");
139 return -1;
140 }
141
142 return extentsbuf.extents[0].startBlock;
143 }
144
145 static const char *journal_fname = ".journal";
146 static const char *jib_fname = ".journal_info_block";
147
148 int
149 DoMakeJournaled(char *volname, int jsize)
150 {
151 int fd, i, block_size, journal_size = 8*1024*1024;
152 char *buf;
153 int ret;
154 fstore_t fst;
155 int jstart_block, jinfo_block, sysctl_info[8];
156 JournalInfoBlock jib;
157 struct statfs sfs;
158 static char tmpname[MAXPATHLEN];
159
160 if (statfs(volname, &sfs) != 0) {
161 fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
162 return 10;
163 }
164
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).
168 //
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",
173 volname);
174 return 10;
175 }
176 unlink(tmpname);
177
178 if (sfs.f_flags & MNT_JOURNALED) {
179 fprintf(stderr, "Volume %s is already journaled.\n", volname);
180 return 1;
181 }
182
183 if (jsize != 0) {
184 journal_size = jsize;
185 } else {
186 int scale;
187
188 //
189 // we want at least 8 megs of journal for each 100 gigs of
190 // disk space. We cap the size at 512 megs though.
191 //
192 scale = ((long long)sfs.f_bsize * (long long)((unsigned)sfs.f_blocks)) / (100*1024*1024*1024ULL);
193 journal_size *= (scale + 1);
194 if (journal_size > 512 * 1024 * 1024) {
195 journal_size = 512 * 1024 * 1024;
196 }
197 }
198
199 if (chdir(volname) != 0) {
200 fprintf(stderr, "Can't locate volume %s to make it journaled (%s).\n",
201 volname, strerror(errno));
202 return 10;
203 }
204
205 fd = open(journal_fname, O_CREAT|O_TRUNC|O_RDWR, 000);
206 if (fd < 0) {
207 fprintf(stderr, "Can't create journal file on volume %s (%s)\n",
208 volname, strerror(errno));
209 return 5;
210 }
211
212 // make sure that it has no r/w/x privs (only could happen if
213 // the file already existed since open() doesn't reset the mode
214 // bits).
215 //
216 fchmod(fd, 0);
217
218 block_size = sfs.f_bsize;
219 if ((journal_size % block_size) != 0) {
220 fprintf(stderr, "Journal size %dk is not a multiple of volume %s block size (%d).\n",
221 journal_size/1024, volname, block_size);
222 close(fd);
223 unlink(journal_fname);
224 return 5;
225 }
226
227 retry:
228 memset(&fst, 0, sizeof(fst));
229 fst.fst_flags = F_ALLOCATECONTIG|F_ALLOCATEALL;
230 fst.fst_length = journal_size;
231 fst.fst_posmode = F_PEOFPOSMODE;
232
233 ret = fcntl(fd, F_PREALLOCATE, &fst);
234 if (ret < 0) {
235 if (journal_size >= 2*1024*1024) {
236 fprintf(stderr, "Not enough contiguous space for a %d k journal. Retrying.\n",
237 journal_size/1024);
238 journal_size /= 2;
239 ftruncate(fd, 0); // make sure the file is zero bytes long.
240 goto retry;
241 } else {
242 fprintf(stderr, "Disk too fragmented to enable journaling.\n");
243 fprintf(stderr, "Please run a defragmenter on %s.\n", volname);
244 close(fd);
245 unlink(journal_fname);
246 return 10;
247 }
248 }
249
250 printf("Allocated %lldK for journal file.\n", fst.fst_bytesalloc/1024LL);
251 buf = (char *)calloc(block_size, 1);
252 if (buf) {
253 for(i=0; i < journal_size/block_size; i++) {
254 ret = write(fd, buf, block_size);
255 if (ret != block_size) {
256 break;
257 }
258 }
259
260 if (i*block_size != journal_size) {
261 fprintf(stderr, "Failed to write %dk to journal on volume %s (%s)\n",
262 journal_size/1024, volname, strerror(errno));
263 }
264 } else {
265 printf("Could not allocate memory to write to the journal on volume %s (%s)\n",
266 volname, strerror(errno));
267 }
268
269 fsync(fd);
270 close(fd);
271 hide_file(journal_fname);
272
273 jstart_block = get_start_block(journal_fname);
274
275 memset(&jib, 0, sizeof(jib));
276 jib.flags = kJIJournalInFSMask;
277 jib.offset = (off_t)((unsigned)jstart_block) * (off_t)((unsigned)block_size);
278 jib.size = (off_t)((unsigned)journal_size);
279
280 fd = open(jib_fname, O_CREAT|O_TRUNC|O_RDWR, 000);
281 if (fd < 0) {
282 fprintf(stderr, "Could not create journal info block file on volume %s (%s)\n",
283 volname, strerror(errno));
284 unlink(journal_fname);
285 return 5;
286 }
287
288 // swap the data before we copy it
289 jib.flags = NXSwapBigLongToHost(jib.flags);
290 jib.offset = NXSwapBigLongLongToHost(jib.offset);
291 jib.size = NXSwapBigLongLongToHost(jib.size);
292
293 memcpy(buf, &jib, sizeof(jib));
294
295 // now put it back the way it was
296 jib.size = NXSwapBigLongLongToHost(jib.size);
297 jib.offset = NXSwapBigLongLongToHost(jib.offset);
298 jib.flags = NXSwapBigLongToHost(jib.flags);
299
300 if (write(fd, buf, block_size) != block_size) {
301 fprintf(stderr, "Failed to write journal info block on volume %s (%s)!\n",
302 volname, strerror(errno));
303 unlink(journal_fname);
304 return 10;
305 }
306
307 fsync(fd);
308 close(fd);
309 hide_file(jib_fname);
310
311 jinfo_block = get_start_block(jib_fname);
312
313
314 //
315 // Now make the volume journaled!
316 //
317 memset(sysctl_info, 0, sizeof(sysctl_info));
318 sysctl_info[0] = CTL_VFS;
319 sysctl_info[1] = sfs.f_fsid.val[1];
320 sysctl_info[2] = HFS_ENABLE_JOURNALING;
321 sysctl_info[3] = jinfo_block;
322 sysctl_info[4] = jstart_block;
323 sysctl_info[5] = journal_size;
324
325 //printf("fs type: 0x%x\n", sysctl_info[1]);
326 //printf("jinfo block : 0x%x\n", jinfo_block);
327 //printf("jstart block: 0x%x\n", jstart_block);
328 //printf("journal size: 0x%x\n", journal_size);
329
330 ret = sysctl((void *)sysctl_info, 6, NULL, NULL, NULL, 0);
331 if (ret != 0) {
332 fprintf(stderr, "Failed to make volume %s journaled (%s)\n",
333 volname, strerror(errno));
334 unlink(journal_fname);
335 unlink(jib_fname);
336 return 20;
337 }
338
339 return 0;
340 }
341
342
343 int
344 DoUnJournal(char *volname)
345 {
346 int result;
347 int sysctl_info[8];
348 struct statfs sfs;
349 char jbuf[MAXPATHLEN];
350
351 if (statfs(volname, &sfs) != 0) {
352 fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
353 return 10;
354 }
355
356 if ((sfs.f_flags & MNT_JOURNALED) == 0) {
357 fprintf(stderr, "Volume %s is not journaled.\n", volname);
358 return 1;
359 }
360
361 if (chdir(volname) != 0) {
362 fprintf(stderr, "Can't locate volume %s to turn off journaling (%s).\n",
363 volname, strerror(errno));
364 return 10;
365 }
366
367 memset(sysctl_info, 0, sizeof(sysctl_info));
368 sysctl_info[0] = CTL_VFS;
369 sysctl_info[1] = sfs.f_fsid.val[1];
370 sysctl_info[2] = HFS_DISABLE_JOURNALING;
371
372 result = sysctl((void *)sysctl_info, 3, NULL, NULL, NULL, 0);
373 if (result != 0) {
374 fprintf(stderr, "Failed to make volume %s UN-journaled (%s)\n",
375 volname, strerror(errno));
376 return 20;
377 }
378
379 sprintf(jbuf, "%s/%s", volname, journal_fname);
380 if (unlink(jbuf) != 0) {
381 fprintf(stderr, "Failed to remove the journal %s (%s)\n",
382 jbuf, strerror(errno));
383 }
384
385 sprintf(jbuf, "%s/%s", volname, jib_fname);
386 if (unlink(jbuf) != 0) {
387 fprintf(stderr, "Failed to remove the journal info block %s (%s)\n",
388 jbuf, strerror(errno));
389 }
390
391 printf("Journaling disabled on %s\n", volname);
392
393 return 0;
394 }
395
396
397 int
398 DoGetJournalInfo(char *volname)
399 {
400 int result;
401 int sysctl_info[8];
402 struct statfs sfs;
403 off_t jstart, jsize;
404
405 if (statfs(volname, &sfs) != 0) {
406 fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
407 return 10;
408 }
409
410 if ((sfs.f_flags & MNT_JOURNALED) == 0) {
411 fprintf(stderr, "Volume %s is not journaled.\n", volname);
412 return 1;
413 }
414
415 if (chdir(volname) != 0) {
416 fprintf(stderr, "Can't cd to volume %s to get journal info (%s).\n",
417 volname, strerror(errno));
418 return 10;
419 }
420
421 memset(sysctl_info, 0, sizeof(sysctl_info));
422 sysctl_info[0] = CTL_VFS;
423 sysctl_info[1] = sfs.f_fsid.val[1];
424 sysctl_info[2] = HFS_GET_JOURNAL_INFO;
425 sysctl_info[3] = (int)&jstart;
426 sysctl_info[4] = (int)&jsize;
427
428 result = sysctl((void *)sysctl_info, 5, NULL, NULL, NULL, 0);
429 if (result != 0) {
430 fprintf(stderr, "Failed to get journal info for volume %s (%s)\n",
431 volname, strerror(errno));
432 return 20;
433 }
434
435 if (jsize == 0) {
436 printf("%s : not journaled.\n", volname);
437 } else {
438 printf("%s : journal size %lld k at offset 0x%llx\n", volname, jsize/1024, jstart);
439 }
440
441 return 0;
442 }