diff options
Diffstat (limited to 'arch/um/drivers/cow_user.c')
| -rw-r--r-- | arch/um/drivers/cow_user.c | 94 |
1 files changed, 78 insertions, 16 deletions
diff --git a/arch/um/drivers/cow_user.c b/arch/um/drivers/cow_user.c index 61951b721268..0ec4052db9c5 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; |
| @@ -75,6 +88,18 @@ struct cow_header_v3 { | |||
| 75 | __u32 alignment; | 88 | __u32 alignment; |
| 76 | __u32 cow_format; | 89 | __u32 cow_format; |
| 77 | char backing_file[PATH_LEN_V3]; | 90 | char backing_file[PATH_LEN_V3]; |
| 91 | } __attribute__((packed)); | ||
| 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]; | ||
| 78 | }; | 103 | }; |
| 79 | 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 */ |
| @@ -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 */ |
| @@ -184,8 +210,9 @@ int write_cow_header(char *cow_file, int fd, char *backing_file, | |||
| 184 | 210 | ||
| 185 | err = -EINVAL; | 211 | err = -EINVAL; |
| 186 | if(strlen(backing_file) > sizeof(header->backing_file) - 1){ | 212 | if(strlen(backing_file) > sizeof(header->backing_file) - 1){ |
| 213 | /* Below, %zd is for a size_t value */ | ||
| 187 | cow_printf("Backing file name \"%s\" is too long - names are " | 214 | cow_printf("Backing file name \"%s\" is too long - names are " |
| 188 | "limited to %d characters\n", backing_file, | 215 | "limited to %zd characters\n", backing_file, |
| 189 | sizeof(header->backing_file) - 1); | 216 | sizeof(header->backing_file) - 1); |
| 190 | goto out_free; | 217 | goto out_free; |
| 191 | } | 218 | } |
| @@ -300,7 +327,8 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, | |||
| 300 | *align_out = *sectorsize_out; | 327 | *align_out = *sectorsize_out; |
| 301 | file = header->v2.backing_file; | 328 | file = header->v2.backing_file; |
| 302 | } | 329 | } |
| 303 | else if(version == 3){ | 330 | /* This is very subtle - see above at union cow_header definition */ |
| 331 | else if(version == 3 && (*((int*)header->v3.backing_file) != 0)){ | ||
| 304 | if(n < sizeof(header->v3)){ | 332 | if(n < sizeof(header->v3)){ |
| 305 | cow_printf("read_cow_header - failed to read V3 " | 333 | cow_printf("read_cow_header - failed to read V3 " |
| 306 | "header\n"); | 334 | "header\n"); |
| @@ -310,9 +338,43 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, | |||
| 310 | *size_out = ntohll(header->v3.size); | 338 | *size_out = ntohll(header->v3.size); |
| 311 | *sectorsize_out = ntohl(header->v3.sectorsize); | 339 | *sectorsize_out = ntohl(header->v3.sectorsize); |
| 312 | *align_out = ntohl(header->v3.alignment); | 340 | *align_out = ntohl(header->v3.alignment); |
| 341 | if (*align_out == 0) { | ||
| 342 | cow_printf("read_cow_header - invalid COW header, " | ||
| 343 | "align == 0\n"); | ||
| 344 | } | ||
| 313 | *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out); | 345 | *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out); |
| 314 | file = header->v3.backing_file; | 346 | file = header->v3.backing_file; |
| 315 | } | 347 | } |
| 348 | else if(version == 3){ | ||
| 349 | cow_printf("read_cow_header - broken V3 file with" | ||
| 350 | " 64-bit layout - recovering content.\n"); | ||
| 351 | |||
| 352 | if(n < sizeof(header->v3_b)){ | ||
| 353 | cow_printf("read_cow_header - failed to read V3 " | ||
| 354 | "header\n"); | ||
| 355 | goto out; | ||
| 356 | } | ||
| 357 | |||
| 358 | /* this was used until Dec2005 - 64bits are needed to represent | ||
| 359 | * 2038+. I.e. we can safely do this truncating cast. | ||
| 360 | * | ||
| 361 | * Additionally, we must use ntohl() instead of ntohll(), since | ||
| 362 | * the program used to use the former (tested - I got mtime | ||
| 363 | * mismatch "0 vs whatever"). | ||
| 364 | * | ||
| 365 | * Ever heard about bug-to-bug-compatibility ? ;-) */ | ||
| 366 | *mtime_out = (time32_t) ntohl(header->v3_b.mtime); | ||
| 367 | |||
| 368 | *size_out = ntohll(header->v3_b.size); | ||
| 369 | *sectorsize_out = ntohl(header->v3_b.sectorsize); | ||
| 370 | *align_out = ntohl(header->v3_b.alignment); | ||
| 371 | if (*align_out == 0) { | ||
| 372 | cow_printf("read_cow_header - invalid COW header, " | ||
| 373 | "align == 0\n"); | ||
| 374 | } | ||
| 375 | *bitmap_offset_out = ROUND_UP(sizeof(header->v3_b), *align_out); | ||
| 376 | file = header->v3_b.backing_file; | ||
| 377 | } | ||
| 316 | else { | 378 | else { |
| 317 | cow_printf("read_cow_header - invalid COW version\n"); | 379 | cow_printf("read_cow_header - invalid COW version\n"); |
| 318 | goto out; | 380 | goto out; |
