diff options
author | Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> | 2006-04-11 01:53:30 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-04-11 09:18:35 -0400 |
commit | f2ea394082c5d1682e6a131c5981085b8752c6e9 (patch) | |
tree | b7789cee4e6484d5063a28a59dcf320ee4676017 /arch | |
parent | cda402b283c34a24b091f78eee116963e9494762 (diff) |
[PATCH] uml: safe migration path to the correct V3 COW format
- Correct the layout of all header versions - make all them well-specified
for any external event. As we don't have 1-byte or 2-byte wide fields, the
32-bit layout (historical one) has no extra padding, so we can safely add
__attribute__((packed)).
- Add detection and reading of the broken 64-bit COW format which has been
around for a while - to allow safe migration to the correct 32-bit format.
Safe detection is possible, thanks to some luck with the existing format,
and it works in practice.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/um/drivers/cow_user.c | 91 |
1 files changed, 76 insertions, 15 deletions
diff --git a/arch/um/drivers/cow_user.c b/arch/um/drivers/cow_user.c index afdf1ea0f557..a9afccf68e38 100644 --- a/arch/um/drivers/cow_user.c +++ b/arch/um/drivers/cow_user.c | |||
@@ -17,30 +17,34 @@ | |||
17 | 17 | ||
18 | #define PATH_LEN_V1 256 | 18 | #define PATH_LEN_V1 256 |
19 | 19 | ||
20 | typedef __u32 time32_t; | ||
21 | |||
20 | struct cow_header_v1 { | 22 | struct cow_header_v1 { |
21 | int magic; | 23 | __s32 magic; |
22 | int version; | 24 | __s32 version; |
23 | char backing_file[PATH_LEN_V1]; | 25 | char backing_file[PATH_LEN_V1]; |
24 | time_t mtime; | 26 | time32_t mtime; |
25 | __u64 size; | 27 | __u64 size; |
26 | int sectorsize; | 28 | __s32 sectorsize; |
27 | }; | 29 | } __attribute__((packed)); |
28 | 30 | ||
29 | #define PATH_LEN_V2 MAXPATHLEN | 31 | /* Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in |
32 | * case other systems have different values for MAXPATHLEN. | ||
33 | * | ||
34 | * The same must hold for V2 - we want file format compatibility, not anything | ||
35 | * else. | ||
36 | */ | ||
37 | #define PATH_LEN_V3 4096 | ||
38 | #define PATH_LEN_V2 PATH_LEN_V3 | ||
30 | 39 | ||
31 | struct cow_header_v2 { | 40 | struct cow_header_v2 { |
32 | __u32 magic; | 41 | __u32 magic; |
33 | __u32 version; | 42 | __u32 version; |
34 | char backing_file[PATH_LEN_V2]; | 43 | char backing_file[PATH_LEN_V2]; |
35 | time_t mtime; | 44 | time32_t mtime; |
36 | __u64 size; | 45 | __u64 size; |
37 | int sectorsize; | 46 | __s32 sectorsize; |
38 | }; | 47 | } __attribute__((packed)); |
39 | |||
40 | /* Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in | ||
41 | * case other systems have different values for MAXPATHLEN | ||
42 | */ | ||
43 | #define PATH_LEN_V3 4096 | ||
44 | 48 | ||
45 | /* Changes from V2 - | 49 | /* Changes from V2 - |
46 | * PATH_LEN_V3 as described above | 50 | * PATH_LEN_V3 as described above |
@@ -66,6 +70,15 @@ struct cow_header_v2 { | |||
66 | * Fixed (finally!) the rounding bug | 70 | * Fixed (finally!) the rounding bug |
67 | */ | 71 | */ |
68 | 72 | ||
73 | /* Until Dec2005, __attribute__((packed)) was left out from the below | ||
74 | * definition, leading on 64-bit systems to 4 bytes of padding after mtime, to | ||
75 | * align size to 8-byte alignment. This shifted all fields above (no padding | ||
76 | * was present on 32-bit, no other padding was added). | ||
77 | * | ||
78 | * However, this _can be detected_: it means that cow_format (always 0 until | ||
79 | * now) is shifted onto the first 4 bytes of backing_file, where it is otherwise | ||
80 | * impossible to find 4 zeros. -bb */ | ||
81 | |||
69 | struct cow_header_v3 { | 82 | struct cow_header_v3 { |
70 | __u32 magic; | 83 | __u32 magic; |
71 | __u32 version; | 84 | __u32 version; |
@@ -77,6 +90,18 @@ struct cow_header_v3 { | |||
77 | char backing_file[PATH_LEN_V3]; | 90 | char backing_file[PATH_LEN_V3]; |
78 | } __attribute__((packed)); | 91 | } __attribute__((packed)); |
79 | 92 | ||
93 | /* This is the broken layout used by some 64-bit binaries. */ | ||
94 | struct cow_header_v3_broken { | ||
95 | __u32 magic; | ||
96 | __u32 version; | ||
97 | __s64 mtime; | ||
98 | __u64 size; | ||
99 | __u32 sectorsize; | ||
100 | __u32 alignment; | ||
101 | __u32 cow_format; | ||
102 | char backing_file[PATH_LEN_V3]; | ||
103 | }; | ||
104 | |||
80 | /* COW format definitions - for now, we have only the usual COW bitmap */ | 105 | /* COW format definitions - for now, we have only the usual COW bitmap */ |
81 | #define COW_BITMAP 0 | 106 | #define COW_BITMAP 0 |
82 | 107 | ||
@@ -84,6 +109,7 @@ union cow_header { | |||
84 | struct cow_header_v1 v1; | 109 | struct cow_header_v1 v1; |
85 | struct cow_header_v2 v2; | 110 | struct cow_header_v2 v2; |
86 | struct cow_header_v3 v3; | 111 | struct cow_header_v3 v3; |
112 | struct cow_header_v3_broken v3_b; | ||
87 | }; | 113 | }; |
88 | 114 | ||
89 | #define COW_MAGIC 0x4f4f4f4d /* MOOO */ | 115 | #define COW_MAGIC 0x4f4f4f4d /* MOOO */ |
@@ -300,7 +326,8 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, | |||
300 | *align_out = *sectorsize_out; | 326 | *align_out = *sectorsize_out; |
301 | file = header->v2.backing_file; | 327 | file = header->v2.backing_file; |
302 | } | 328 | } |
303 | else if(version == 3){ | 329 | /* This is very subtle - see above at union cow_header definition */ |
330 | else if(version == 3 && (*((int*)header->v3.backing_file) != 0)){ | ||
304 | if(n < sizeof(header->v3)){ | 331 | if(n < sizeof(header->v3)){ |
305 | cow_printf("read_cow_header - failed to read V3 " | 332 | cow_printf("read_cow_header - failed to read V3 " |
306 | "header\n"); | 333 | "header\n"); |
@@ -310,9 +337,43 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, | |||
310 | *size_out = ntohll(header->v3.size); | 337 | *size_out = ntohll(header->v3.size); |
311 | *sectorsize_out = ntohl(header->v3.sectorsize); | 338 | *sectorsize_out = ntohl(header->v3.sectorsize); |
312 | *align_out = ntohl(header->v3.alignment); | 339 | *align_out = ntohl(header->v3.alignment); |
340 | if (*align_out == 0) { | ||
341 | cow_printf("read_cow_header - invalid COW header, " | ||
342 | "align == 0\n"); | ||
343 | } | ||
313 | *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out); | 344 | *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out); |
314 | file = header->v3.backing_file; | 345 | file = header->v3.backing_file; |
315 | } | 346 | } |
347 | else if(version == 3){ | ||
348 | cow_printf("read_cow_header - broken V3 file with" | ||
349 | " 64-bit layout - recovering content.\n"); | ||
350 | |||
351 | if(n < sizeof(header->v3_b)){ | ||
352 | cow_printf("read_cow_header - failed to read V3 " | ||
353 | "header\n"); | ||
354 | goto out; | ||
355 | } | ||
356 | |||
357 | /* this was used until Dec2005 - 64bits are needed to represent | ||
358 | * 2038+. I.e. we can safely do this truncating cast. | ||
359 | * | ||
360 | * Additionally, we must use ntohl() instead of ntohll(), since | ||
361 | * the program used to use the former (tested - I got mtime | ||
362 | * mismatch "0 vs whatever"). | ||
363 | * | ||
364 | * Ever heard about bug-to-bug-compatibility ? ;-) */ | ||
365 | *mtime_out = (time32_t) ntohl(header->v3_b.mtime); | ||
366 | |||
367 | *size_out = ntohll(header->v3_b.size); | ||
368 | *sectorsize_out = ntohl(header->v3_b.sectorsize); | ||
369 | *align_out = ntohl(header->v3_b.alignment); | ||
370 | if (*align_out == 0) { | ||
371 | cow_printf("read_cow_header - invalid COW header, " | ||
372 | "align == 0\n"); | ||
373 | } | ||
374 | *bitmap_offset_out = ROUND_UP(sizeof(header->v3_b), *align_out); | ||
375 | file = header->v3_b.backing_file; | ||
376 | } | ||
316 | else { | 377 | else { |
317 | cow_printf("read_cow_header - invalid COW version\n"); | 378 | cow_printf("read_cow_header - invalid COW version\n"); |
318 | goto out; | 379 | goto out; |