| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com) | 
|  | 3 | * Licensed under the GPL | 
|  | 4 | */ | 
|  | 5 |  | 
|  | 6 | /* | 
|  | 7 | * _XOPEN_SOURCE is needed for pread, but we define _GNU_SOURCE, which defines | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 8 | * that. | 
|  | 9 | */ | 
|  | 10 | #include <unistd.h> | 
|  | 11 | #include <byteswap.h> | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 12 | #include <errno.h> | 
|  | 13 | #include <string.h> | 
|  | 14 | #include <arpa/inet.h> | 
|  | 15 | #include <asm/types.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 16 | #include "cow.h" | 
|  | 17 | #include "cow_sys.h" | 
|  | 18 |  | 
|  | 19 | #define PATH_LEN_V1 256 | 
|  | 20 |  | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 21 | typedef __u32 time32_t; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 22 |  | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 23 | struct cow_header_v1 { | 
|  | 24 | __s32 magic; | 
|  | 25 | __s32 version; | 
|  | 26 | char backing_file[PATH_LEN_V1]; | 
|  | 27 | time32_t mtime; | 
|  | 28 | __u64 size; | 
|  | 29 | __s32 sectorsize; | 
|  | 30 | } __attribute__((packed)); | 
|  | 31 |  | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 32 | /* | 
|  | 33 | * Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 34 | * case other systems have different values for MAXPATHLEN. | 
|  | 35 | * | 
|  | 36 | * The same must hold for V2 - we want file format compatibility, not anything | 
|  | 37 | * else. | 
|  | 38 | */ | 
|  | 39 | #define PATH_LEN_V3 4096 | 
|  | 40 | #define PATH_LEN_V2 PATH_LEN_V3 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 41 |  | 
|  | 42 | struct cow_header_v2 { | 
|  | 43 | __u32 magic; | 
|  | 44 | __u32 version; | 
|  | 45 | char backing_file[PATH_LEN_V2]; | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 46 | time32_t mtime; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 47 | __u64 size; | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 48 | __s32 sectorsize; | 
|  | 49 | } __attribute__((packed)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 50 |  | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 51 | /* | 
|  | 52 | * Changes from V2 - | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 53 | *	PATH_LEN_V3 as described above | 
|  | 54 | *	Explicitly specify field bit lengths for systems with different | 
|  | 55 | *		lengths for the usual C types.  Not sure whether char or | 
|  | 56 | *		time_t should be changed, this can be changed later without | 
|  | 57 | *		breaking compatibility | 
|  | 58 | *	Add alignment field so that different alignments can be used for the | 
|  | 59 | *		bitmap and data | 
|  | 60 | * 	Add cow_format field to allow for the possibility of different ways | 
|  | 61 | *		of specifying the COW blocks.  For now, the only value is 0, | 
|  | 62 | * 		for the traditional COW bitmap. | 
|  | 63 | *	Move the backing_file field to the end of the header.  This allows | 
|  | 64 | *		for the possibility of expanding it into the padding required | 
|  | 65 | *		by the bitmap alignment. | 
|  | 66 | * 	The bitmap and data portions of the file will be aligned as specified | 
|  | 67 | * 		by the alignment field.  This is to allow COW files to be | 
|  | 68 | *		put on devices with restrictions on access alignments, such as | 
|  | 69 | *		/dev/raw, with a 512 byte alignment restriction.  This also | 
|  | 70 | *		allows the data to be more aligned more strictly than on | 
|  | 71 | *		sector boundaries.  This is needed for ubd-mmap, which needs | 
|  | 72 | *		the data to be page aligned. | 
|  | 73 | *	Fixed (finally!) the rounding bug | 
|  | 74 | */ | 
|  | 75 |  | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 76 | /* | 
|  | 77 | * Until Dec2005, __attribute__((packed)) was left out from the below | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 78 | * definition, leading on 64-bit systems to 4 bytes of padding after mtime, to | 
|  | 79 | * align size to 8-byte alignment.  This shifted all fields above (no padding | 
|  | 80 | * was present on 32-bit, no other padding was added). | 
|  | 81 | * | 
|  | 82 | * However, this _can be detected_: it means that cow_format (always 0 until | 
|  | 83 | * now) is shifted onto the first 4 bytes of backing_file, where it is otherwise | 
|  | 84 | * impossible to find 4 zeros. -bb */ | 
|  | 85 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 86 | struct cow_header_v3 { | 
|  | 87 | __u32 magic; | 
|  | 88 | __u32 version; | 
|  | 89 | __u32 mtime; | 
|  | 90 | __u64 size; | 
|  | 91 | __u32 sectorsize; | 
|  | 92 | __u32 alignment; | 
|  | 93 | __u32 cow_format; | 
|  | 94 | char backing_file[PATH_LEN_V3]; | 
| Paolo 'Blaisorblade' Giarrusso | cda402b | 2006-04-10 22:53:29 -0700 | [diff] [blame] | 95 | } __attribute__((packed)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 96 |  | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 97 | /* This is the broken layout used by some 64-bit binaries. */ | 
|  | 98 | struct cow_header_v3_broken { | 
|  | 99 | __u32 magic; | 
|  | 100 | __u32 version; | 
|  | 101 | __s64 mtime; | 
|  | 102 | __u64 size; | 
|  | 103 | __u32 sectorsize; | 
|  | 104 | __u32 alignment; | 
|  | 105 | __u32 cow_format; | 
|  | 106 | char backing_file[PATH_LEN_V3]; | 
| Paolo 'Blaisorblade' Giarrusso | b15fb6b | 2006-05-01 12:16:01 -0700 | [diff] [blame] | 107 | }; | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 108 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 109 | /* COW format definitions - for now, we have only the usual COW bitmap */ | 
|  | 110 | #define COW_BITMAP 0 | 
|  | 111 |  | 
|  | 112 | union cow_header { | 
|  | 113 | struct cow_header_v1 v1; | 
|  | 114 | struct cow_header_v2 v2; | 
|  | 115 | struct cow_header_v3 v3; | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 116 | struct cow_header_v3_broken v3_b; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 117 | }; | 
|  | 118 |  | 
|  | 119 | #define COW_MAGIC 0x4f4f4f4d  /* MOOO */ | 
|  | 120 | #define COW_VERSION 3 | 
|  | 121 |  | 
|  | 122 | #define DIV_ROUND(x, len) (((x) + (len) - 1) / (len)) | 
|  | 123 | #define ROUND_UP(x, align) DIV_ROUND(x, align) * (align) | 
|  | 124 |  | 
|  | 125 | void cow_sizes(int version, __u64 size, int sectorsize, int align, | 
|  | 126 | int bitmap_offset, unsigned long *bitmap_len_out, | 
|  | 127 | int *data_offset_out) | 
|  | 128 | { | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 129 | if (version < 3) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 130 | *bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize); | 
|  | 131 |  | 
|  | 132 | *data_offset_out = bitmap_offset + *bitmap_len_out; | 
|  | 133 | *data_offset_out = (*data_offset_out + sectorsize - 1) / | 
|  | 134 | sectorsize; | 
|  | 135 | *data_offset_out *= sectorsize; | 
|  | 136 | } | 
|  | 137 | else { | 
|  | 138 | *bitmap_len_out = DIV_ROUND(size, sectorsize); | 
|  | 139 | *bitmap_len_out = DIV_ROUND(*bitmap_len_out, 8); | 
|  | 140 |  | 
|  | 141 | *data_offset_out = bitmap_offset + *bitmap_len_out; | 
|  | 142 | *data_offset_out = ROUND_UP(*data_offset_out, align); | 
|  | 143 | } | 
|  | 144 | } | 
|  | 145 |  | 
|  | 146 | static int absolutize(char *to, int size, char *from) | 
|  | 147 | { | 
|  | 148 | char save_cwd[256], *slash; | 
|  | 149 | int remaining; | 
|  | 150 |  | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 151 | if (getcwd(save_cwd, sizeof(save_cwd)) == NULL) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 152 | cow_printf("absolutize : unable to get cwd - errno = %d\n", | 
|  | 153 | errno); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 154 | return -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 155 | } | 
|  | 156 | slash = strrchr(from, '/'); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 157 | if (slash != NULL) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 158 | *slash = '\0'; | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 159 | if (chdir(from)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 160 | *slash = '/'; | 
|  | 161 | cow_printf("absolutize : Can't cd to '%s' - " | 
|  | 162 | "errno = %d\n", from, errno); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 163 | return -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 164 | } | 
|  | 165 | *slash = '/'; | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 166 | if (getcwd(to, size) == NULL) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 167 | cow_printf("absolutize : unable to get cwd of '%s' - " | 
|  | 168 | "errno = %d\n", from, errno); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 169 | return -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 170 | } | 
|  | 171 | remaining = size - strlen(to); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 172 | if (strlen(slash) + 1 > remaining) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 173 | cow_printf("absolutize : unable to fit '%s' into %d " | 
|  | 174 | "chars\n", from, size); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 175 | return -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 176 | } | 
|  | 177 | strcat(to, slash); | 
|  | 178 | } | 
|  | 179 | else { | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 180 | if (strlen(save_cwd) + 1 + strlen(from) + 1 > size) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 181 | cow_printf("absolutize : unable to fit '%s' into %d " | 
|  | 182 | "chars\n", from, size); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 183 | return -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 184 | } | 
|  | 185 | strcpy(to, save_cwd); | 
|  | 186 | strcat(to, "/"); | 
|  | 187 | strcat(to, from); | 
|  | 188 | } | 
|  | 189 | chdir(save_cwd); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 190 | return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 191 | } | 
|  | 192 |  | 
|  | 193 | int write_cow_header(char *cow_file, int fd, char *backing_file, | 
|  | 194 | int sectorsize, int alignment, unsigned long long *size) | 
|  | 195 | { | 
|  | 196 | struct cow_header_v3 *header; | 
|  | 197 | unsigned long modtime; | 
|  | 198 | int err; | 
|  | 199 |  | 
|  | 200 | err = cow_seek_file(fd, 0); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 201 | if (err < 0) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 202 | cow_printf("write_cow_header - lseek failed, err = %d\n", -err); | 
|  | 203 | goto out; | 
|  | 204 | } | 
|  | 205 |  | 
|  | 206 | err = -ENOMEM; | 
|  | 207 | header = cow_malloc(sizeof(*header)); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 208 | if (header == NULL) { | 
|  | 209 | cow_printf("write_cow_header - failed to allocate COW V3 " | 
|  | 210 | "header\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 211 | goto out; | 
|  | 212 | } | 
|  | 213 | header->magic = htonl(COW_MAGIC); | 
|  | 214 | header->version = htonl(COW_VERSION); | 
|  | 215 |  | 
|  | 216 | err = -EINVAL; | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 217 | if (strlen(backing_file) > sizeof(header->backing_file) - 1) { | 
| Paolo 'Blaisorblade' Giarrusso | 6dad2d3 | 2006-04-10 22:53:31 -0700 | [diff] [blame] | 218 | /* Below, %zd is for a size_t value */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 219 | cow_printf("Backing file name \"%s\" is too long - names are " | 
| Paolo 'Blaisorblade' Giarrusso | 6dad2d3 | 2006-04-10 22:53:31 -0700 | [diff] [blame] | 220 | "limited to %zd characters\n", backing_file, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 221 | sizeof(header->backing_file) - 1); | 
|  | 222 | goto out_free; | 
|  | 223 | } | 
|  | 224 |  | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 225 | if (absolutize(header->backing_file, sizeof(header->backing_file), | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 226 | backing_file)) | 
|  | 227 | goto out_free; | 
|  | 228 |  | 
|  | 229 | err = os_file_modtime(header->backing_file, &modtime); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 230 | if (err < 0) { | 
| Paolo 'Blaisorblade' Giarrusso | 31bc5a33 | 2006-02-24 13:03:53 -0800 | [diff] [blame] | 231 | cow_printf("write_cow_header - backing file '%s' mtime " | 
|  | 232 | "request failed, err = %d\n", header->backing_file, | 
|  | 233 | -err); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 234 | goto out_free; | 
|  | 235 | } | 
|  | 236 |  | 
|  | 237 | err = cow_file_size(header->backing_file, size); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 238 | if (err < 0) { | 
| Paolo 'Blaisorblade' Giarrusso | 31bc5a33 | 2006-02-24 13:03:53 -0800 | [diff] [blame] | 239 | cow_printf("write_cow_header - couldn't get size of " | 
|  | 240 | "backing file '%s', err = %d\n", | 
|  | 241 | header->backing_file, -err); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 242 | goto out_free; | 
|  | 243 | } | 
|  | 244 |  | 
|  | 245 | header->mtime = htonl(modtime); | 
|  | 246 | header->size = htonll(*size); | 
|  | 247 | header->sectorsize = htonl(sectorsize); | 
|  | 248 | header->alignment = htonl(alignment); | 
|  | 249 | header->cow_format = COW_BITMAP; | 
|  | 250 |  | 
| Paolo 'Blaisorblade' Giarrusso | 31bc5a33 | 2006-02-24 13:03:53 -0800 | [diff] [blame] | 251 | err = cow_write_file(fd, header, sizeof(*header)); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 252 | if (err != sizeof(*header)) { | 
| Paolo 'Blaisorblade' Giarrusso | 31bc5a33 | 2006-02-24 13:03:53 -0800 | [diff] [blame] | 253 | cow_printf("write_cow_header - write of header to " | 
|  | 254 | "new COW file '%s' failed, err = %d\n", cow_file, | 
|  | 255 | -err); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 256 | goto out_free; | 
|  | 257 | } | 
|  | 258 | err = 0; | 
|  | 259 | out_free: | 
|  | 260 | cow_free(header); | 
|  | 261 | out: | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 262 | return err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 263 | } | 
|  | 264 |  | 
|  | 265 | int file_reader(__u64 offset, char *buf, int len, void *arg) | 
|  | 266 | { | 
|  | 267 | int fd = *((int *) arg); | 
|  | 268 |  | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 269 | return pread(fd, buf, len, offset); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 270 | } | 
|  | 271 |  | 
|  | 272 | /* XXX Need to sanity-check the values read from the header */ | 
|  | 273 |  | 
|  | 274 | int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, | 
|  | 275 | __u32 *version_out, char **backing_file_out, | 
|  | 276 | time_t *mtime_out, unsigned long long *size_out, | 
|  | 277 | int *sectorsize_out, __u32 *align_out, | 
|  | 278 | int *bitmap_offset_out) | 
|  | 279 | { | 
|  | 280 | union cow_header *header; | 
|  | 281 | char *file; | 
|  | 282 | int err, n; | 
|  | 283 | unsigned long version, magic; | 
|  | 284 |  | 
|  | 285 | header = cow_malloc(sizeof(*header)); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 286 | if (header == NULL) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 287 | cow_printf("read_cow_header - Failed to allocate header\n"); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 288 | return -ENOMEM; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 289 | } | 
|  | 290 | err = -EINVAL; | 
|  | 291 | n = (*reader)(0, (char *) header, sizeof(*header), arg); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 292 | if (n < offsetof(typeof(header->v1), backing_file)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 293 | cow_printf("read_cow_header - short header\n"); | 
|  | 294 | goto out; | 
|  | 295 | } | 
|  | 296 |  | 
|  | 297 | magic = header->v1.magic; | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 298 | if (magic == COW_MAGIC) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 299 | version = header->v1.version; | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 300 | else if (magic == ntohl(COW_MAGIC)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 301 | version = ntohl(header->v1.version); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 302 | /* No error printed because the non-COW case comes through here */ | 
|  | 303 | else goto out; | 
|  | 304 |  | 
|  | 305 | *version_out = version; | 
|  | 306 |  | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 307 | if (version == 1) { | 
|  | 308 | if (n < sizeof(header->v1)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 309 | cow_printf("read_cow_header - failed to read V1 " | 
|  | 310 | "header\n"); | 
|  | 311 | goto out; | 
|  | 312 | } | 
|  | 313 | *mtime_out = header->v1.mtime; | 
|  | 314 | *size_out = header->v1.size; | 
|  | 315 | *sectorsize_out = header->v1.sectorsize; | 
|  | 316 | *bitmap_offset_out = sizeof(header->v1); | 
|  | 317 | *align_out = *sectorsize_out; | 
|  | 318 | file = header->v1.backing_file; | 
|  | 319 | } | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 320 | else if (version == 2) { | 
|  | 321 | if (n < sizeof(header->v2)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 322 | cow_printf("read_cow_header - failed to read V2 " | 
|  | 323 | "header\n"); | 
|  | 324 | goto out; | 
|  | 325 | } | 
|  | 326 | *mtime_out = ntohl(header->v2.mtime); | 
|  | 327 | *size_out = ntohll(header->v2.size); | 
|  | 328 | *sectorsize_out = ntohl(header->v2.sectorsize); | 
|  | 329 | *bitmap_offset_out = sizeof(header->v2); | 
|  | 330 | *align_out = *sectorsize_out; | 
|  | 331 | file = header->v2.backing_file; | 
|  | 332 | } | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 333 | /* This is very subtle - see above at union cow_header definition */ | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 334 | else if (version == 3 && (*((int*)header->v3.backing_file) != 0)) { | 
|  | 335 | if (n < sizeof(header->v3)) { | 
| Paolo 'Blaisorblade' Giarrusso | 31bc5a33 | 2006-02-24 13:03:53 -0800 | [diff] [blame] | 336 | cow_printf("read_cow_header - failed to read V3 " | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 337 | "header\n"); | 
|  | 338 | goto out; | 
|  | 339 | } | 
|  | 340 | *mtime_out = ntohl(header->v3.mtime); | 
|  | 341 | *size_out = ntohll(header->v3.size); | 
|  | 342 | *sectorsize_out = ntohl(header->v3.sectorsize); | 
|  | 343 | *align_out = ntohl(header->v3.alignment); | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 344 | if (*align_out == 0) { | 
|  | 345 | cow_printf("read_cow_header - invalid COW header, " | 
|  | 346 | "align == 0\n"); | 
|  | 347 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 348 | *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out); | 
|  | 349 | file = header->v3.backing_file; | 
|  | 350 | } | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 351 | else if (version == 3) { | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 352 | cow_printf("read_cow_header - broken V3 file with" | 
|  | 353 | " 64-bit layout - recovering content.\n"); | 
|  | 354 |  | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 355 | if (n < sizeof(header->v3_b)) { | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 356 | cow_printf("read_cow_header - failed to read V3 " | 
|  | 357 | "header\n"); | 
|  | 358 | goto out; | 
|  | 359 | } | 
|  | 360 |  | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 361 | /* | 
|  | 362 | * this was used until Dec2005 - 64bits are needed to represent | 
| Paolo 'Blaisorblade' Giarrusso | f2ea394 | 2006-04-10 22:53:30 -0700 | [diff] [blame] | 363 | * 2038+. I.e. we can safely do this truncating cast. | 
|  | 364 | * | 
|  | 365 | * Additionally, we must use ntohl() instead of ntohll(), since | 
|  | 366 | * the program used to use the former (tested - I got mtime | 
|  | 367 | * mismatch "0 vs whatever"). | 
|  | 368 | * | 
|  | 369 | * Ever heard about bug-to-bug-compatibility ? ;-) */ | 
|  | 370 | *mtime_out = (time32_t) ntohl(header->v3_b.mtime); | 
|  | 371 |  | 
|  | 372 | *size_out = ntohll(header->v3_b.size); | 
|  | 373 | *sectorsize_out = ntohl(header->v3_b.sectorsize); | 
|  | 374 | *align_out = ntohl(header->v3_b.alignment); | 
|  | 375 | if (*align_out == 0) { | 
|  | 376 | cow_printf("read_cow_header - invalid COW header, " | 
|  | 377 | "align == 0\n"); | 
|  | 378 | } | 
|  | 379 | *bitmap_offset_out = ROUND_UP(sizeof(header->v3_b), *align_out); | 
|  | 380 | file = header->v3_b.backing_file; | 
|  | 381 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 382 | else { | 
|  | 383 | cow_printf("read_cow_header - invalid COW version\n"); | 
|  | 384 | goto out; | 
|  | 385 | } | 
|  | 386 | err = -ENOMEM; | 
|  | 387 | *backing_file_out = cow_strdup(file); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 388 | if (*backing_file_out == NULL) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 389 | cow_printf("read_cow_header - failed to allocate backing " | 
|  | 390 | "file\n"); | 
|  | 391 | goto out; | 
|  | 392 | } | 
|  | 393 | err = 0; | 
|  | 394 | out: | 
|  | 395 | cow_free(header); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 396 | return err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 397 | } | 
|  | 398 |  | 
|  | 399 | int init_cow_file(int fd, char *cow_file, char *backing_file, int sectorsize, | 
|  | 400 | int alignment, int *bitmap_offset_out, | 
|  | 401 | unsigned long *bitmap_len_out, int *data_offset_out) | 
|  | 402 | { | 
|  | 403 | unsigned long long size, offset; | 
|  | 404 | char zero = 0; | 
|  | 405 | int err; | 
|  | 406 |  | 
|  | 407 | err = write_cow_header(cow_file, fd, backing_file, sectorsize, | 
|  | 408 | alignment, &size); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 409 | if (err) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 410 | goto out; | 
|  | 411 |  | 
|  | 412 | *bitmap_offset_out = ROUND_UP(sizeof(struct cow_header_v3), alignment); | 
|  | 413 | cow_sizes(COW_VERSION, size, sectorsize, alignment, *bitmap_offset_out, | 
|  | 414 | bitmap_len_out, data_offset_out); | 
|  | 415 |  | 
|  | 416 | offset = *data_offset_out + size - sizeof(zero); | 
|  | 417 | err = cow_seek_file(fd, offset); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 418 | if (err < 0) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 419 | cow_printf("cow bitmap lseek failed : err = %d\n", -err); | 
|  | 420 | goto out; | 
|  | 421 | } | 
|  | 422 |  | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 423 | /* | 
|  | 424 | * does not really matter how much we write it is just to set EOF | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 425 | * this also sets the entire COW bitmap | 
|  | 426 | * to zero without having to allocate it | 
|  | 427 | */ | 
|  | 428 | err = cow_write_file(fd, &zero, sizeof(zero)); | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 429 | if (err != sizeof(zero)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 430 | cow_printf("Write of bitmap to new COW file '%s' failed, " | 
|  | 431 | "err = %d\n", cow_file, -err); | 
| Paolo 'Blaisorblade' Giarrusso | fe1db50 | 2006-02-24 13:03:58 -0800 | [diff] [blame] | 432 | if (err >= 0) | 
|  | 433 | err = -EINVAL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 434 | goto out; | 
|  | 435 | } | 
|  | 436 |  | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 437 | return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 438 | out: | 
| Jeff Dike | cb8fa61 | 2007-10-16 01:27:34 -0700 | [diff] [blame] | 439 | return err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 440 | } |