]>
Commit | Line | Data |
---|---|---|
08cdaae8 A |
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.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. | |
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 <dev/disk.h> | |
42 | #include <sys/loadable_fs.h> | |
43 | #include <hfs/hfs_format.h> | |
44 | ||
45 | #include <errno.h> | |
46 | #include <fcntl.h> | |
47 | #include <libgen.h> | |
48 | #include <pwd.h> | |
49 | #include <stdio.h> | |
50 | #include <stdlib.h> | |
51 | #include <string.h> | |
52 | #include <unistd.h> | |
53 | ||
54 | #include <architecture/byte_order.h> | |
55 | ||
56 | // | |
57 | // Secret HFS sysctl's to instruct it to turn | |
58 | // journaling on/off for a volume. | |
59 | // | |
60 | #define HFS_BECOME_JOURNALED 0x082969 | |
61 | #define HFS_BECOME_UNJOURNALED 0x031272 | |
62 | ||
63 | ||
64 | /* getattrlist buffers start with an extra length field */ | |
65 | struct ExtentsAttrBuf { | |
66 | unsigned long infoLength; | |
67 | HFSPlusExtentRecord extents; | |
68 | }; | |
69 | typedef struct ExtentsAttrBuf ExtentsAttrBuf; | |
70 | ||
71 | ||
72 | ||
73 | #define kIsInvisible 0x4000 | |
74 | ||
75 | /* | |
76 | * Generic Finder file/dir data | |
77 | */ | |
78 | struct FinderInfo { | |
79 | u_int32_t opaque_1[2]; | |
80 | u_int16_t fdFlags; /* Finder flags */ | |
81 | int16_t opaque_2[11]; | |
82 | }; | |
83 | typedef struct FinderInfo FinderInfo; | |
84 | ||
85 | /* getattrlist buffers start with an extra length field */ | |
86 | struct FinderAttrBuf { | |
87 | unsigned long infoLength; | |
88 | FinderInfo finderInfo; | |
89 | }; | |
90 | typedef struct FinderAttrBuf FinderAttrBuf; | |
91 | ||
92 | ||
93 | int hide_file(const char * file) | |
94 | { | |
95 | struct attrlist alist = {0}; | |
96 | FinderAttrBuf finderInfoBuf = {0}; | |
97 | int result; | |
98 | ||
99 | alist.bitmapcount = ATTR_BIT_MAP_COUNT; | |
100 | alist.commonattr = ATTR_CMN_FNDRINFO; | |
101 | ||
102 | result = getattrlist(file, &alist, &finderInfoBuf, sizeof(finderInfoBuf), 0); | |
103 | if (result) { | |
104 | return (errno); | |
105 | } | |
106 | ||
107 | if (finderInfoBuf.finderInfo.fdFlags & kIsInvisible) { | |
108 | printf("hide: %s is alreadly invisible\n", file); | |
109 | return (0); | |
110 | } | |
111 | ||
112 | finderInfoBuf.finderInfo.fdFlags |= kIsInvisible; | |
113 | ||
114 | result = setattrlist(file, &alist, &finderInfoBuf.finderInfo, sizeof(FinderInfo), 0); | |
115 | ||
116 | return (result == -1 ? errno : result); | |
117 | } | |
118 | ||
119 | int | |
120 | get_start_block(const char *file) | |
121 | { | |
122 | struct attrlist alist = {0}; | |
123 | ExtentsAttrBuf extentsbuf = {0}; | |
124 | ||
125 | alist.bitmapcount = ATTR_BIT_MAP_COUNT; | |
126 | alist.fileattr = ATTR_FILE_DATAEXTENTS; | |
127 | ||
128 | if (getattrlist(file, &alist, &extentsbuf, sizeof(extentsbuf), 0)) { | |
129 | fprintf(stderr, "could not get attrlist for %s (%s)", file, strerror(errno)); | |
130 | return -1; | |
131 | } | |
132 | ||
133 | if (extentsbuf.extents[1].startBlock != 0) { | |
134 | fprintf(stderr, "Journal File not contiguous!\n"); | |
135 | return -1; | |
136 | } | |
137 | ||
138 | return extentsbuf.extents[0].startBlock; | |
139 | } | |
140 | ||
141 | static const char *journal_fname = ".journal"; | |
142 | static const char *jib_fname = ".journal_info_block"; | |
143 | ||
144 | int | |
145 | DoMakeJournaled(char *volname, int jsize) | |
146 | { | |
147 | int fd, i, block_size, journal_size = 8*1024*1024; | |
148 | char *buf; | |
149 | int ret; | |
150 | fstore_t fst; | |
151 | int jstart_block, jinfo_block, sysctl_info[8]; | |
152 | JournalInfoBlock jib; | |
153 | struct statfs sfs; | |
154 | static char tmpname[MAXPATHLEN]; | |
155 | ||
156 | if (jsize != 0) { | |
157 | journal_size = jsize; | |
158 | } | |
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 (chdir(volname) != 0) { | |
184 | fprintf(stderr, "Can't locate volume %s to make it journaled (%s).\n", | |
185 | volname, strerror(errno)); | |
186 | return 10; | |
187 | } | |
188 | ||
189 | fd = open(journal_fname, O_CREAT|O_TRUNC|O_RDWR, 000); | |
190 | if (fd < 0) { | |
191 | fprintf(stderr, "Can't create journal file on volume %s (%s)\n", | |
192 | volname, strerror(errno)); | |
193 | return 5; | |
194 | } | |
195 | ||
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 | |
198 | // bits). | |
199 | // | |
200 | fchmod(fd, 0); | |
201 | ||
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); | |
206 | close(fd); | |
207 | unlink(journal_fname); | |
208 | return 5; | |
209 | } | |
210 | ||
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; | |
215 | ||
216 | ret = fcntl(fd, F_PREALLOCATE, &fst); | |
217 | if (ret < 0) { | |
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); | |
221 | close(fd); | |
222 | unlink(journal_fname); | |
223 | return 10; | |
224 | } | |
225 | ||
226 | printf("Allocated %lldK for journal file.\n", fst.fst_bytesalloc/1024LL); | |
227 | buf = (char *)calloc(block_size, 1); | |
228 | if (buf) { | |
229 | for(i=0; i < journal_size/block_size; i++) { | |
230 | ret = write(fd, buf, block_size); | |
231 | if (ret != block_size) { | |
232 | break; | |
233 | } | |
234 | } | |
235 | ||
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)); | |
239 | } | |
240 | } else { | |
241 | printf("Could not allocate memory to write to the journal on volume %s (%s)\n", | |
242 | volname, strerror(errno)); | |
243 | } | |
244 | ||
245 | fsync(fd); | |
246 | close(fd); | |
247 | hide_file(journal_fname); | |
248 | ||
249 | jstart_block = get_start_block(journal_fname); | |
250 | ||
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; | |
255 | ||
256 | fd = open(jib_fname, O_CREAT|O_TRUNC|O_RDWR, 000); | |
257 | if (fd < 0) { | |
258 | fprintf(stderr, "Could not create journal info block file on volume %s (%s)\n", | |
259 | volname, strerror(errno)); | |
260 | unlink(journal_fname); | |
261 | return 5; | |
262 | } | |
263 | ||
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); | |
269 | return 10; | |
270 | } | |
271 | ||
272 | fsync(fd); | |
273 | close(fd); | |
274 | hide_file(jib_fname); | |
275 | ||
276 | jinfo_block = get_start_block(jib_fname); | |
277 | ||
278 | ||
279 | // | |
280 | // Now make the volume journaled! | |
281 | // | |
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; | |
289 | ||
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); | |
294 | ||
295 | ret = sysctl((void *)sysctl_info, 6, NULL, NULL, NULL, 0); | |
296 | if (ret != 0) { | |
297 | fprintf(stderr, "Failed to make volume %s journaled (%s)\n", | |
298 | volname, strerror(errno)); | |
299 | unlink(journal_fname); | |
300 | unlink(jib_fname); | |
301 | return 20; | |
302 | } | |
303 | ||
304 | return 0; | |
305 | } | |
306 | ||
307 | ||
308 | int | |
309 | DoUnJournal(char *volname) | |
310 | { | |
311 | int result; | |
312 | int sysctl_info[8]; | |
313 | struct statfs sfs; | |
314 | char jbuf[MAXPATHLEN]; | |
315 | ||
316 | if (statfs(volname, &sfs) != 0) { | |
317 | fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno)); | |
318 | return 10; | |
319 | } | |
320 | ||
321 | if ((sfs.f_flags & MNT_JOURNALED) == 0) { | |
322 | fprintf(stderr, "Volume %s is not journaled.\n", volname); | |
323 | return 1; | |
324 | } | |
325 | ||
326 | if (chdir(volname) != 0) { | |
327 | fprintf(stderr, "Can't locate volume %s to turn off journaling (%s).\n", | |
328 | volname, strerror(errno)); | |
329 | return 10; | |
330 | } | |
331 | ||
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; | |
336 | ||
337 | result = sysctl((void *)sysctl_info, 6, NULL, NULL, NULL, 0); | |
338 | if (result != 0) { | |
339 | fprintf(stderr, "Failed to make volume %s UN-journaled (%s)\n", | |
340 | volname, strerror(errno)); | |
341 | return 20; | |
342 | } | |
343 | ||
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)); | |
348 | } | |
349 | ||
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)); | |
354 | } | |
355 | ||
356 | printf("Journaling disabled on %s\n", volname); | |
357 | ||
358 | return 0; | |
359 | } |