1 /*
2 libparted - a library for manipulating disk partitions
3 Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #ifndef DISCOVER_ONLY
20
21 #include <config.h>
22
23 #include <parted/parted.h>
24 #include <parted/endian.h>
25 #include <parted/debug.h>
26 #include <stdint.h>
27
28 #if ENABLE_NLS
29 # include <libintl.h>
30 # define _(String) dgettext (PACKAGE, String)
31 #else
32 # define _(String) (String)
33 #endif /* ENABLE_NLS */
34
35 #include "hfs.h"
36 #include "reloc_plus.h"
37
38 #include "journal.h"
39
40 static int hfsj_vh_replayed = 0;
41 static int is_le = 0;
42
43 static uint32_t
44 hfsj_calc_checksum(uint8_t *ptr, int len)
45 {
46 int i;
47 uint32_t cksum=0;
48
49 for (i=0; i < len; i++, ptr++) {
50 cksum = (cksum << 8) ^ (cksum + *ptr);
51 }
52
53 return (~cksum);
54 }
55
56 int
57 hfsj_update_jib(PedFileSystem* fs, uint32_t block)
58 {
59 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
60 fs->type_specific;
61
62 priv_data->vh->journal_info_block = PED_CPU_TO_BE32(block);
63
64 if (!hfsplus_update_vh (fs))
65 return 0;
66
67 priv_data->jib_start_block = block;
68 return 1;
69 }
70
71 int
72 hfsj_update_jl(PedFileSystem* fs, uint32_t block)
73 {
74 uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
75 PedSector sector;
76 uint64_t offset;
77 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
78 fs->type_specific;
79 HfsJJournalInfoBlock* jib;
80 int binsect;
81
82 binsect = HFS_32_TO_CPU(priv_data->vh->block_size, is_le) / PED_SECTOR_SIZE_DEFAULT;
83 sector = (PedSector) priv_data->jib_start_block * binsect;
84 if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1))
85 return 0;
86 jib = (HfsJJournalInfoBlock*) buf;
87
88 offset = (uint64_t)block * PED_SECTOR_SIZE_DEFAULT * binsect;
89 jib->offset = HFS_CPU_TO_64(offset, is_le);
90
91 if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1)
92 || !ped_geometry_sync(priv_data->plus_geom))
93 return 0;
94
95 priv_data->jl_start_block = block;
96 return 1;
97 }
98
99 /* Return the sector in the journal that is after the area read */
100 /* or 0 on error */
101 static PedSector
102 hfsj_journal_read(PedGeometry* geom, HfsJJournalHeader* jh,
103 PedSector journ_sect, PedSector journ_length,
104 PedSector read_sect, unsigned int nb_sect,
105 void* buf)
106 {
107 int r;
108
109 while (nb_sect--) {
110 r = ped_geometry_read(geom, buf, journ_sect + read_sect, 1);
111 if (!r) return 0;
112
113 buf = ((uint8_t*)buf) + PED_SECTOR_SIZE_DEFAULT;
114 read_sect++;
115 if (read_sect == journ_length)
116 read_sect = 1; /* skip journal header
117 which is asserted to be
118 1 sector long */
119 }
120
121 return read_sect;
122 }
123
124 static int
125 hfsj_replay_transaction(PedFileSystem* fs, HfsJJournalHeader* jh,
126 PedSector jsector, PedSector jlength)
127 {
128 PedSector start, sector;
129 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
130 fs->type_specific;
131 HfsJBlockListHeader* blhdr;
132 uint8_t* block;
133 unsigned int blhdr_nbsect;
134 int i, r;
135 uint32_t cksum, size;
136
137 blhdr_nbsect = HFS_32_TO_CPU(jh->blhdr_size, is_le) / PED_SECTOR_SIZE_DEFAULT;
138 blhdr = (HfsJBlockListHeader*)
139 ped_malloc (blhdr_nbsect * PED_SECTOR_SIZE_DEFAULT);
140 if (!blhdr) return 0;
141
142 start = HFS_64_TO_CPU(jh->start, is_le) / PED_SECTOR_SIZE_DEFAULT;
143 do {
144 start = hfsj_journal_read(priv_data->plus_geom, jh, jsector,
145 jlength, start, blhdr_nbsect, blhdr);
146 if (!start) goto err_replay;
147
148 cksum = HFS_32_TO_CPU(blhdr->checksum, is_le);
149 blhdr->checksum = 0;
150 if (cksum!=hfsj_calc_checksum((uint8_t*)blhdr, sizeof(*blhdr))){
151 ped_exception_throw (
152 PED_EXCEPTION_ERROR,
153 PED_EXCEPTION_CANCEL,
154 _("Bad block list header checksum."));
155 goto err_replay;
156 }
157 blhdr->checksum = HFS_CPU_TO_32(cksum, is_le);
158
159 for (i=1; i < HFS_16_TO_CPU(blhdr->num_blocks, is_le); ++i) {
160 size = HFS_32_TO_CPU(blhdr->binfo[i].bsize, is_le);
161 sector = HFS_64_TO_CPU(blhdr->binfo[i].bnum, is_le);
162 if (!size) continue;
163 if (size % PED_SECTOR_SIZE_DEFAULT) {
164 ped_exception_throw(
165 PED_EXCEPTION_ERROR,
166 PED_EXCEPTION_CANCEL,
167 _("Invalid size of a transaction "
168 "block while replaying the journal "
169 "(%i bytes)."),
170 size);
171 goto err_replay;
172 }
173 block = (uint8_t*) ped_malloc(size);
174 if (!block) goto err_replay;
175 start = hfsj_journal_read(priv_data->plus_geom, jh,
176 jsector, jlength, start,
177 size / PED_SECTOR_SIZE_DEFAULT,
178 block);
179 if (!start) {
180 ped_free (block);
181 goto err_replay;
182 }
183 /* the sector stored in the journal seems to be
184 relative to the begin of the block device which
185 contains the hfs+ journaled volume */
186 if (sector != ~0LL)
187 r = ped_geometry_write (fs->geom, block, sector,
188 size / PED_SECTOR_SIZE_DEFAULT);
189 else
190 r = 1;
191 ped_free (block);
192 /* check if wrapper mdb or vh with no wrapper has
193 changed */
194 if ( (sector != ~0LL)
195 && (2 >= sector)
196 && (2 < sector + size / PED_SECTOR_SIZE_DEFAULT) )
197 hfsj_vh_replayed = 1;
198 /* check if vh of embedded hfs+ has changed */
199 if ( (sector != ~0LL)
200 && (priv_data->plus_geom != fs->geom)
201 && (sector
202 + fs->geom->start
203 - priv_data->plus_geom->start <= 2)
204 && (sector
205 + size / PED_SECTOR_SIZE_DEFAULT
206 + fs->geom->start
207 - priv_data->plus_geom->start > 2) )
208 hfsj_vh_replayed = 1;
209 if (!r) goto err_replay;
210 }
211 } while (blhdr->binfo[0].next);
212
213 jh->start = HFS_CPU_TO_64(start * PED_SECTOR_SIZE_DEFAULT, is_le);
214
215 ped_free (blhdr);
216 return (ped_geometry_sync (fs->geom));
217
218 err_replay:
219 ped_free (blhdr);
220 return 0;
221 }
222
223 /* 0 => Failure, don't continue to open ! */
224 /* 1 => Success, the journal has been completly replayed, or don't need to */
225 int
226 hfsj_replay_journal(PedFileSystem* fs)
227 {
228 uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
229 PedSector sector, length;
230 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
231 fs->type_specific;
232 HfsJJournalInfoBlock* jib;
233 HfsJJournalHeader* jh;
234 int binsect;
235 uint32_t cksum;
236
237 binsect = PED_BE32_TO_CPU(priv_data->vh->block_size) / PED_SECTOR_SIZE_DEFAULT;
238 priv_data->jib_start_block =
239 PED_BE32_TO_CPU(priv_data->vh->journal_info_block);
240 sector = (PedSector) priv_data->jib_start_block * binsect;
241 if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1))
242 return 0;
243 jib = (HfsJJournalInfoBlock*) buf;
244
245 if ( (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS))
246 && !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) {
247 priv_data->jl_start_block = HFS_64_TO_CPU(jib->offset, is_le)
248 / ( PED_SECTOR_SIZE_DEFAULT * binsect );
249 }
250
251 if (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_NEED_INIT))
252 return 1;
253
254 if ( !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS))
255 || (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) {
256 ped_exception_throw (
257 PED_EXCEPTION_NO_FEATURE,
258 PED_EXCEPTION_CANCEL,
259 _("Journal stored outside of the volume are "
260 "not supported. Try to desactivate the "
261 "journal and run Parted again."));
262 return 0;
263 }
264
265 if ( (PED_BE64_TO_CPU(jib->offset) % PED_SECTOR_SIZE_DEFAULT)
266 || (PED_BE64_TO_CPU(jib->size) % PED_SECTOR_SIZE_DEFAULT) ) {
267 ped_exception_throw (
268 PED_EXCEPTION_NO_FEATURE,
269 PED_EXCEPTION_CANCEL,
270 _("Journal offset or size is not multiple of "
271 "the sector size."));
272 return 0;
273 }
274
275 sector = PED_BE64_TO_CPU(jib->offset) / PED_SECTOR_SIZE_DEFAULT;
276 length = PED_BE64_TO_CPU(jib->size) / PED_SECTOR_SIZE_DEFAULT;
277
278 jib = NULL;
279 if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1))
280 return 0;
281 jh = (HfsJJournalHeader*) buf;
282
283 if (jh->endian == PED_LE32_TO_CPU(HFSJ_ENDIAN_MAGIC))
284 is_le = 1;
285
286 if ( (jh->magic != HFS_32_TO_CPU(HFSJ_HEADER_MAGIC, is_le))
287 || (jh->endian != HFS_32_TO_CPU(HFSJ_ENDIAN_MAGIC, is_le)) ) {
288 ped_exception_throw (
289 PED_EXCEPTION_ERROR,
290 PED_EXCEPTION_CANCEL,
291 _("Incorrect magic values in the journal header."));
292 return 0;
293 }
294
295 if ( (HFS_64_TO_CPU(jh->size, is_le)%PED_SECTOR_SIZE_DEFAULT)
296 || (HFS_64_TO_CPU(jh->size, is_le)/PED_SECTOR_SIZE_DEFAULT
297 != (uint64_t)length) ) {
298 ped_exception_throw (
299 PED_EXCEPTION_ERROR,
300 PED_EXCEPTION_CANCEL,
301 _("Journal size mismatch between journal info block "
302 "and journal header."));
303 return 0;
304 }
305
306 if ( (HFS_64_TO_CPU(jh->start, is_le) % PED_SECTOR_SIZE_DEFAULT)
307 || (HFS_64_TO_CPU(jh->end, is_le) % PED_SECTOR_SIZE_DEFAULT)
308 || (HFS_32_TO_CPU(jh->blhdr_size, is_le) % PED_SECTOR_SIZE_DEFAULT)
309 || (HFS_32_TO_CPU(jh->jhdr_size, is_le) % PED_SECTOR_SIZE_DEFAULT) ) {
310 ped_exception_throw (
311 PED_EXCEPTION_ERROR,
312 PED_EXCEPTION_CANCEL,
313 _("Some header fields are not multiple of the sector "
314 "size."));
315 return 0;
316 }
317
318 if (HFS_32_TO_CPU(jh->jhdr_size, is_le) != PED_SECTOR_SIZE_DEFAULT) {
319 ped_exception_throw (
320 PED_EXCEPTION_ERROR,
321 PED_EXCEPTION_CANCEL,
322 _("The sector size stored in the journal is not 512 "
323 "bytes. Parted only supports 512 bytes length "
324 "sectors."));
325 return 0;
326 }
327
328 cksum = HFS_32_TO_CPU(jh->checksum, is_le);
329 jh->checksum = 0;
330 if (cksum != hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh))) {
331 ped_exception_throw (
332 PED_EXCEPTION_ERROR,
333 PED_EXCEPTION_CANCEL,
334 _("Bad journal checksum."));
335 return 0;
336 }
337 jh->checksum = HFS_CPU_TO_32(cksum, is_le);
338
339 /* The 2 following test are in the XNU Darwin source code */
340 /* so I assume they're needed */
341 if (jh->start == jh->size)
342 jh->start = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le);
343 if (jh->end == jh->size)
344 jh->start = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le);
345
346 if (jh->start == jh->end)
347 return 1;
348
349 if (ped_exception_throw (
350 PED_EXCEPTION_WARNING,
351 PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL,
352 _("The journal is not empty. Parted must replay the "
353 "transactions before opening the file system. This will "
354 "modify the file system."))
355 != PED_EXCEPTION_FIX)
356 return 0;
357
358 while (jh->start != jh->end) {
359 /* Replay one complete transaction */
360 if (!hfsj_replay_transaction(fs, jh, sector, length))
361 return 0;
362
363 /* Recalculate cksum of the journal header */
364 jh->checksum = 0; /* need to be 0 while calculating the cksum */
365 cksum = hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh));
366 jh->checksum = HFS_CPU_TO_32(cksum, is_le);
367
368 /* Update the Journal Header */
369 if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1)
370 || !ped_geometry_sync(priv_data->plus_geom))
371 return 0;
372 }
373
374 if (hfsj_vh_replayed) {
375 /* probe could have reported incorrect info ! */
376 /* is there a way to ask parted to quit ? */
377 ped_exception_throw(
378 PED_EXCEPTION_WARNING,
379 PED_EXCEPTION_OK,
380 _("The volume header or the master directory block has "
381 "changed while replaying the journal. You should "
382 "restart Parted."));
383 return 0;
384 }
385
386 return 1;
387 }
388
389 #endif /* DISCOVER_ONLY */