~youscribe/parted/3.1

« back to all changes in this revision

Viewing changes to libparted/fs/r/hfs/journal.c

  • Committer: Guilhem Lettron
  • Date: 2012-10-22 14:37:59 UTC
  • Revision ID: guilhem+ubuntu@lettron.fr-20121022143759-m403kecgz13sknvp
3.1 from tarball

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    libparted - a library for manipulating disk partitions
 
3
    Copyright (C) 2004-2005, 2007, 2009-2012 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
                                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
                        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
        free (blhdr);
 
216
        return (ped_geometry_sync (fs->geom));
 
217
 
 
218
err_replay:
 
219
        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 */