~ubuntu-branches/ubuntu/vivid/parted/vivid

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Colin Watson
  • Date: 2014-07-21 10:23:16 UTC
  • mfrom: (7.2.32 sid)
  • Revision ID: package-import@ubuntu.com-20140721102316-jsyv3yzmbo8vlde5
Tags: 3.1-3
Upload to unstable.

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