Commit | Line | Data |
---|---|---|
cb8fa61c JD |
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 | |
1da177e4 LT |
8 | * that. |
9 | */ | |
10 | #include <unistd.h> | |
11 | #include <byteswap.h> | |
cb8fa61c JD |
12 | #include <errno.h> |
13 | #include <string.h> | |
14 | #include <arpa/inet.h> | |
15 | #include <asm/types.h> | |
1da177e4 LT |
16 | #include "cow.h" |
17 | #include "cow_sys.h" | |
18 | ||
19 | #define PATH_LEN_V1 256 | |
20 | ||
f2ea3940 PBG |
21 | typedef __u32 time32_t; |
22 | ||
1da177e4 | 23 | struct cow_header_v1 { |
f2ea3940 PBG |
24 | __s32 magic; |
25 | __s32 version; | |
1da177e4 | 26 | char backing_file[PATH_LEN_V1]; |
f2ea3940 | 27 | time32_t mtime; |
1da177e4 | 28 | __u64 size; |
f2ea3940 PBG |
29 | __s32 sectorsize; |
30 | } __attribute__((packed)); | |
1da177e4 | 31 | |
cb8fa61c JD |
32 | /* |
33 | * Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in | |
f2ea3940 PBG |
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 | |
1da177e4 LT |
41 | |
42 | struct cow_header_v2 { | |
43 | __u32 magic; | |
44 | __u32 version; | |
45 | char backing_file[PATH_LEN_V2]; | |
f2ea3940 | 46 | time32_t mtime; |
1da177e4 | 47 | __u64 size; |
f2ea3940 PBG |
48 | __s32 sectorsize; |
49 | } __attribute__((packed)); | |
1da177e4 | 50 | |
cb8fa61c JD |
51 | /* |
52 | * Changes from V2 - | |
1da177e4 LT |
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 | ||
cb8fa61c JD |
76 | /* |
77 | * Until Dec2005, __attribute__((packed)) was left out from the below | |
f2ea3940 PBG |
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 | ||
1da177e4 LT |
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]; | |
cda402b2 | 95 | } __attribute__((packed)); |
1da177e4 | 96 | |
f2ea3940 PBG |
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]; | |
b15fb6b1 | 107 | }; |
f2ea3940 | 108 | |
1da177e4 LT |
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; | |
f2ea3940 | 116 | struct cow_header_v3_broken v3_b; |
1da177e4 LT |
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 | { | |
cb8fa61c | 129 | if (version < 3) { |
1da177e4 LT |
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 | ||
cb8fa61c | 151 | if (getcwd(save_cwd, sizeof(save_cwd)) == NULL) { |
1da177e4 LT |
152 | cow_printf("absolutize : unable to get cwd - errno = %d\n", |
153 | errno); | |
cb8fa61c | 154 | return -1; |
1da177e4 LT |
155 | } |
156 | slash = strrchr(from, '/'); | |
cb8fa61c | 157 | if (slash != NULL) { |
1da177e4 | 158 | *slash = '\0'; |
cb8fa61c | 159 | if (chdir(from)) { |
1da177e4 LT |
160 | *slash = '/'; |
161 | cow_printf("absolutize : Can't cd to '%s' - " | |
162 | "errno = %d\n", from, errno); | |
cb8fa61c | 163 | return -1; |
1da177e4 LT |
164 | } |
165 | *slash = '/'; | |
cb8fa61c | 166 | if (getcwd(to, size) == NULL) { |
1da177e4 LT |
167 | cow_printf("absolutize : unable to get cwd of '%s' - " |
168 | "errno = %d\n", from, errno); | |
cb8fa61c | 169 | return -1; |
1da177e4 LT |
170 | } |
171 | remaining = size - strlen(to); | |
cb8fa61c | 172 | if (strlen(slash) + 1 > remaining) { |
1da177e4 LT |
173 | cow_printf("absolutize : unable to fit '%s' into %d " |
174 | "chars\n", from, size); | |
cb8fa61c | 175 | return -1; |
1da177e4 LT |
176 | } |
177 | strcat(to, slash); | |
178 | } | |
179 | else { | |
cb8fa61c | 180 | if (strlen(save_cwd) + 1 + strlen(from) + 1 > size) { |
1da177e4 LT |
181 | cow_printf("absolutize : unable to fit '%s' into %d " |
182 | "chars\n", from, size); | |
cb8fa61c | 183 | return -1; |
1da177e4 LT |
184 | } |
185 | strcpy(to, save_cwd); | |
186 | strcat(to, "/"); | |
187 | strcat(to, from); | |
188 | } | |
189 | chdir(save_cwd); | |
cb8fa61c | 190 | return 0; |
1da177e4 LT |
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); | |
cb8fa61c | 201 | if (err < 0) { |
1da177e4 LT |
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)); | |
cb8fa61c JD |
208 | if (header == NULL) { |
209 | cow_printf("write_cow_header - failed to allocate COW V3 " | |
210 | "header\n"); | |
1da177e4 LT |
211 | goto out; |
212 | } | |
213 | header->magic = htonl(COW_MAGIC); | |
214 | header->version = htonl(COW_VERSION); | |
215 | ||
216 | err = -EINVAL; | |
cb8fa61c | 217 | if (strlen(backing_file) > sizeof(header->backing_file) - 1) { |
6dad2d3f | 218 | /* Below, %zd is for a size_t value */ |
1da177e4 | 219 | cow_printf("Backing file name \"%s\" is too long - names are " |
6dad2d3f | 220 | "limited to %zd characters\n", backing_file, |
1da177e4 LT |
221 | sizeof(header->backing_file) - 1); |
222 | goto out_free; | |
223 | } | |
224 | ||
cb8fa61c | 225 | if (absolutize(header->backing_file, sizeof(header->backing_file), |
1da177e4 LT |
226 | backing_file)) |
227 | goto out_free; | |
228 | ||
229 | err = os_file_modtime(header->backing_file, &modtime); | |
cb8fa61c | 230 | if (err < 0) { |
31bc5a33 PBG |
231 | cow_printf("write_cow_header - backing file '%s' mtime " |
232 | "request failed, err = %d\n", header->backing_file, | |
233 | -err); | |
1da177e4 LT |
234 | goto out_free; |
235 | } | |
236 | ||
237 | err = cow_file_size(header->backing_file, size); | |
cb8fa61c | 238 | if (err < 0) { |
31bc5a33 PBG |
239 | cow_printf("write_cow_header - couldn't get size of " |
240 | "backing file '%s', err = %d\n", | |
241 | header->backing_file, -err); | |
1da177e4 LT |
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 | ||
31bc5a33 | 251 | err = cow_write_file(fd, header, sizeof(*header)); |
cb8fa61c | 252 | if (err != sizeof(*header)) { |
31bc5a33 PBG |
253 | cow_printf("write_cow_header - write of header to " |
254 | "new COW file '%s' failed, err = %d\n", cow_file, | |
255 | -err); | |
1da177e4 LT |
256 | goto out_free; |
257 | } | |
258 | err = 0; | |
259 | out_free: | |
260 | cow_free(header); | |
261 | out: | |
cb8fa61c | 262 | return err; |
1da177e4 LT |
263 | } |
264 | ||
265 | int file_reader(__u64 offset, char *buf, int len, void *arg) | |
266 | { | |
267 | int fd = *((int *) arg); | |
268 | ||
cb8fa61c | 269 | return pread(fd, buf, len, offset); |
1da177e4 LT |
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)); | |
cb8fa61c | 286 | if (header == NULL) { |
1da177e4 | 287 | cow_printf("read_cow_header - Failed to allocate header\n"); |
cb8fa61c | 288 | return -ENOMEM; |
1da177e4 LT |
289 | } |
290 | err = -EINVAL; | |
291 | n = (*reader)(0, (char *) header, sizeof(*header), arg); | |
cb8fa61c | 292 | if (n < offsetof(typeof(header->v1), backing_file)) { |
1da177e4 LT |
293 | cow_printf("read_cow_header - short header\n"); |
294 | goto out; | |
295 | } | |
296 | ||
297 | magic = header->v1.magic; | |
cb8fa61c | 298 | if (magic == COW_MAGIC) |
1da177e4 | 299 | version = header->v1.version; |
cb8fa61c | 300 | else if (magic == ntohl(COW_MAGIC)) |
1da177e4 | 301 | version = ntohl(header->v1.version); |
1da177e4 LT |
302 | /* No error printed because the non-COW case comes through here */ |
303 | else goto out; | |
304 | ||
305 | *version_out = version; | |
306 | ||
cb8fa61c JD |
307 | if (version == 1) { |
308 | if (n < sizeof(header->v1)) { | |
1da177e4 LT |
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 | } | |
cb8fa61c JD |
320 | else if (version == 2) { |
321 | if (n < sizeof(header->v2)) { | |
1da177e4 LT |
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 | } | |
f2ea3940 | 333 | /* This is very subtle - see above at union cow_header definition */ |
cb8fa61c JD |
334 | else if (version == 3 && (*((int*)header->v3.backing_file) != 0)) { |
335 | if (n < sizeof(header->v3)) { | |
31bc5a33 | 336 | cow_printf("read_cow_header - failed to read V3 " |
1da177e4 LT |
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); | |
f2ea3940 PBG |
344 | if (*align_out == 0) { |
345 | cow_printf("read_cow_header - invalid COW header, " | |
346 | "align == 0\n"); | |
347 | } | |
1da177e4 LT |
348 | *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out); |
349 | file = header->v3.backing_file; | |
350 | } | |
cb8fa61c | 351 | else if (version == 3) { |
f2ea3940 PBG |
352 | cow_printf("read_cow_header - broken V3 file with" |
353 | " 64-bit layout - recovering content.\n"); | |
354 | ||
cb8fa61c | 355 | if (n < sizeof(header->v3_b)) { |
f2ea3940 PBG |
356 | cow_printf("read_cow_header - failed to read V3 " |
357 | "header\n"); | |
358 | goto out; | |
359 | } | |
360 | ||
cb8fa61c JD |
361 | /* |
362 | * this was used until Dec2005 - 64bits are needed to represent | |
f2ea3940 PBG |
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 | } | |
1da177e4 LT |
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); | |
cb8fa61c | 388 | if (*backing_file_out == NULL) { |
1da177e4 LT |
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); | |
cb8fa61c | 396 | return err; |
1da177e4 LT |
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); | |
cb8fa61c | 409 | if (err) |
1da177e4 LT |
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); | |
cb8fa61c | 418 | if (err < 0) { |
1da177e4 LT |
419 | cow_printf("cow bitmap lseek failed : err = %d\n", -err); |
420 | goto out; | |
421 | } | |
422 | ||
cb8fa61c JD |
423 | /* |
424 | * does not really matter how much we write it is just to set EOF | |
1da177e4 LT |
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)); | |
cb8fa61c | 429 | if (err != sizeof(zero)) { |
1da177e4 LT |
430 | cow_printf("Write of bitmap to new COW file '%s' failed, " |
431 | "err = %d\n", cow_file, -err); | |
fe1db50c PBG |
432 | if (err >= 0) |
433 | err = -EINVAL; | |
1da177e4 LT |
434 | goto out; |
435 | } | |
436 | ||
cb8fa61c | 437 | return 0; |
1da177e4 | 438 | out: |
cb8fa61c | 439 | return err; |
1da177e4 | 440 | } |