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