diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/um/drivers/cow_user.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/um/drivers/cow_user.c')
-rw-r--r-- | arch/um/drivers/cow_user.c | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/arch/um/drivers/cow_user.c b/arch/um/drivers/cow_user.c new file mode 100644 index 000000000000..a8ce6fc3ef26 --- /dev/null +++ b/arch/um/drivers/cow_user.c | |||
@@ -0,0 +1,378 @@ | |||
1 | #include <stddef.h> | ||
2 | #include <string.h> | ||
3 | #include <errno.h> | ||
4 | /* _XOPEN_SOURCE is needed for pread, but we define _GNU_SOURCE, which defines | ||
5 | * that. | ||
6 | */ | ||
7 | #include <unistd.h> | ||
8 | #include <byteswap.h> | ||
9 | #include <sys/time.h> | ||
10 | #include <sys/param.h> | ||
11 | #include <sys/user.h> | ||
12 | #include <netinet/in.h> | ||
13 | |||
14 | #include "os.h" | ||
15 | |||
16 | #include "cow.h" | ||
17 | #include "cow_sys.h" | ||
18 | |||
19 | #define PATH_LEN_V1 256 | ||
20 | |||
21 | struct cow_header_v1 { | ||
22 | int magic; | ||
23 | int version; | ||
24 | char backing_file[PATH_LEN_V1]; | ||
25 | time_t mtime; | ||
26 | __u64 size; | ||
27 | int sectorsize; | ||
28 | }; | ||
29 | |||
30 | #define PATH_LEN_V2 MAXPATHLEN | ||
31 | |||
32 | struct cow_header_v2 { | ||
33 | __u32 magic; | ||
34 | __u32 version; | ||
35 | char backing_file[PATH_LEN_V2]; | ||
36 | time_t mtime; | ||
37 | __u64 size; | ||
38 | int sectorsize; | ||
39 | }; | ||
40 | |||
41 | /* Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in | ||
42 | * case other systems have different values for MAXPATHLEN | ||
43 | */ | ||
44 | #define PATH_LEN_V3 4096 | ||
45 | |||
46 | /* Changes from V2 - | ||
47 | * PATH_LEN_V3 as described above | ||
48 | * Explicitly specify field bit lengths for systems with different | ||
49 | * lengths for the usual C types. Not sure whether char or | ||
50 | * time_t should be changed, this can be changed later without | ||
51 | * breaking compatibility | ||
52 | * Add alignment field so that different alignments can be used for the | ||
53 | * bitmap and data | ||
54 | * Add cow_format field to allow for the possibility of different ways | ||
55 | * of specifying the COW blocks. For now, the only value is 0, | ||
56 | * for the traditional COW bitmap. | ||
57 | * Move the backing_file field to the end of the header. This allows | ||
58 | * for the possibility of expanding it into the padding required | ||
59 | * by the bitmap alignment. | ||
60 | * The bitmap and data portions of the file will be aligned as specified | ||
61 | * by the alignment field. This is to allow COW files to be | ||
62 | * put on devices with restrictions on access alignments, such as | ||
63 | * /dev/raw, with a 512 byte alignment restriction. This also | ||
64 | * allows the data to be more aligned more strictly than on | ||
65 | * sector boundaries. This is needed for ubd-mmap, which needs | ||
66 | * the data to be page aligned. | ||
67 | * Fixed (finally!) the rounding bug | ||
68 | */ | ||
69 | |||
70 | struct cow_header_v3 { | ||
71 | __u32 magic; | ||
72 | __u32 version; | ||
73 | __u32 mtime; | ||
74 | __u64 size; | ||
75 | __u32 sectorsize; | ||
76 | __u32 alignment; | ||
77 | __u32 cow_format; | ||
78 | char backing_file[PATH_LEN_V3]; | ||
79 | }; | ||
80 | |||
81 | /* COW format definitions - for now, we have only the usual COW bitmap */ | ||
82 | #define COW_BITMAP 0 | ||
83 | |||
84 | union cow_header { | ||
85 | struct cow_header_v1 v1; | ||
86 | struct cow_header_v2 v2; | ||
87 | struct cow_header_v3 v3; | ||
88 | }; | ||
89 | |||
90 | #define COW_MAGIC 0x4f4f4f4d /* MOOO */ | ||
91 | #define COW_VERSION 3 | ||
92 | |||
93 | #define DIV_ROUND(x, len) (((x) + (len) - 1) / (len)) | ||
94 | #define ROUND_UP(x, align) DIV_ROUND(x, align) * (align) | ||
95 | |||
96 | void cow_sizes(int version, __u64 size, int sectorsize, int align, | ||
97 | int bitmap_offset, unsigned long *bitmap_len_out, | ||
98 | int *data_offset_out) | ||
99 | { | ||
100 | if(version < 3){ | ||
101 | *bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize); | ||
102 | |||
103 | *data_offset_out = bitmap_offset + *bitmap_len_out; | ||
104 | *data_offset_out = (*data_offset_out + sectorsize - 1) / | ||
105 | sectorsize; | ||
106 | *data_offset_out *= sectorsize; | ||
107 | } | ||
108 | else { | ||
109 | *bitmap_len_out = DIV_ROUND(size, sectorsize); | ||
110 | *bitmap_len_out = DIV_ROUND(*bitmap_len_out, 8); | ||
111 | |||
112 | *data_offset_out = bitmap_offset + *bitmap_len_out; | ||
113 | *data_offset_out = ROUND_UP(*data_offset_out, align); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | static int absolutize(char *to, int size, char *from) | ||
118 | { | ||
119 | char save_cwd[256], *slash; | ||
120 | int remaining; | ||
121 | |||
122 | if(getcwd(save_cwd, sizeof(save_cwd)) == NULL) { | ||
123 | cow_printf("absolutize : unable to get cwd - errno = %d\n", | ||
124 | errno); | ||
125 | return(-1); | ||
126 | } | ||
127 | slash = strrchr(from, '/'); | ||
128 | if(slash != NULL){ | ||
129 | *slash = '\0'; | ||
130 | if(chdir(from)){ | ||
131 | *slash = '/'; | ||
132 | cow_printf("absolutize : Can't cd to '%s' - " | ||
133 | "errno = %d\n", from, errno); | ||
134 | return(-1); | ||
135 | } | ||
136 | *slash = '/'; | ||
137 | if(getcwd(to, size) == NULL){ | ||
138 | cow_printf("absolutize : unable to get cwd of '%s' - " | ||
139 | "errno = %d\n", from, errno); | ||
140 | return(-1); | ||
141 | } | ||
142 | remaining = size - strlen(to); | ||
143 | if(strlen(slash) + 1 > remaining){ | ||
144 | cow_printf("absolutize : unable to fit '%s' into %d " | ||
145 | "chars\n", from, size); | ||
146 | return(-1); | ||
147 | } | ||
148 | strcat(to, slash); | ||
149 | } | ||
150 | else { | ||
151 | if(strlen(save_cwd) + 1 + strlen(from) + 1 > size){ | ||
152 | cow_printf("absolutize : unable to fit '%s' into %d " | ||
153 | "chars\n", from, size); | ||
154 | return(-1); | ||
155 | } | ||
156 | strcpy(to, save_cwd); | ||
157 | strcat(to, "/"); | ||
158 | strcat(to, from); | ||
159 | } | ||
160 | chdir(save_cwd); | ||
161 | return(0); | ||
162 | } | ||
163 | |||
164 | int write_cow_header(char *cow_file, int fd, char *backing_file, | ||
165 | int sectorsize, int alignment, unsigned long long *size) | ||
166 | { | ||
167 | struct cow_header_v3 *header; | ||
168 | unsigned long modtime; | ||
169 | int err; | ||
170 | |||
171 | err = cow_seek_file(fd, 0); | ||
172 | if(err < 0){ | ||
173 | cow_printf("write_cow_header - lseek failed, err = %d\n", -err); | ||
174 | goto out; | ||
175 | } | ||
176 | |||
177 | err = -ENOMEM; | ||
178 | header = cow_malloc(sizeof(*header)); | ||
179 | if(header == NULL){ | ||
180 | cow_printf("Failed to allocate COW V3 header\n"); | ||
181 | goto out; | ||
182 | } | ||
183 | header->magic = htonl(COW_MAGIC); | ||
184 | header->version = htonl(COW_VERSION); | ||
185 | |||
186 | err = -EINVAL; | ||
187 | if(strlen(backing_file) > sizeof(header->backing_file) - 1){ | ||
188 | cow_printf("Backing file name \"%s\" is too long - names are " | ||
189 | "limited to %d characters\n", backing_file, | ||
190 | sizeof(header->backing_file) - 1); | ||
191 | goto out_free; | ||
192 | } | ||
193 | |||
194 | if(absolutize(header->backing_file, sizeof(header->backing_file), | ||
195 | backing_file)) | ||
196 | goto out_free; | ||
197 | |||
198 | err = os_file_modtime(header->backing_file, &modtime); | ||
199 | if(err < 0){ | ||
200 | cow_printf("Backing file '%s' mtime request failed, " | ||
201 | "err = %d\n", header->backing_file, -err); | ||
202 | goto out_free; | ||
203 | } | ||
204 | |||
205 | err = cow_file_size(header->backing_file, size); | ||
206 | if(err < 0){ | ||
207 | cow_printf("Couldn't get size of backing file '%s', " | ||
208 | "err = %d\n", header->backing_file, -err); | ||
209 | goto out_free; | ||
210 | } | ||
211 | |||
212 | header->mtime = htonl(modtime); | ||
213 | header->size = htonll(*size); | ||
214 | header->sectorsize = htonl(sectorsize); | ||
215 | header->alignment = htonl(alignment); | ||
216 | header->cow_format = COW_BITMAP; | ||
217 | |||
218 | err = os_write_file(fd, header, sizeof(*header)); | ||
219 | if(err != sizeof(*header)){ | ||
220 | cow_printf("Write of header to new COW file '%s' failed, " | ||
221 | "err = %d\n", cow_file, -err); | ||
222 | goto out_free; | ||
223 | } | ||
224 | err = 0; | ||
225 | out_free: | ||
226 | cow_free(header); | ||
227 | out: | ||
228 | return(err); | ||
229 | } | ||
230 | |||
231 | int file_reader(__u64 offset, char *buf, int len, void *arg) | ||
232 | { | ||
233 | int fd = *((int *) arg); | ||
234 | |||
235 | return(pread(fd, buf, len, offset)); | ||
236 | } | ||
237 | |||
238 | /* XXX Need to sanity-check the values read from the header */ | ||
239 | |||
240 | int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, | ||
241 | __u32 *version_out, char **backing_file_out, | ||
242 | time_t *mtime_out, unsigned long long *size_out, | ||
243 | int *sectorsize_out, __u32 *align_out, | ||
244 | int *bitmap_offset_out) | ||
245 | { | ||
246 | union cow_header *header; | ||
247 | char *file; | ||
248 | int err, n; | ||
249 | unsigned long version, magic; | ||
250 | |||
251 | header = cow_malloc(sizeof(*header)); | ||
252 | if(header == NULL){ | ||
253 | cow_printf("read_cow_header - Failed to allocate header\n"); | ||
254 | return(-ENOMEM); | ||
255 | } | ||
256 | err = -EINVAL; | ||
257 | n = (*reader)(0, (char *) header, sizeof(*header), arg); | ||
258 | if(n < offsetof(typeof(header->v1), backing_file)){ | ||
259 | cow_printf("read_cow_header - short header\n"); | ||
260 | goto out; | ||
261 | } | ||
262 | |||
263 | magic = header->v1.magic; | ||
264 | if(magic == COW_MAGIC) { | ||
265 | version = header->v1.version; | ||
266 | } | ||
267 | else if(magic == ntohl(COW_MAGIC)){ | ||
268 | version = ntohl(header->v1.version); | ||
269 | } | ||
270 | /* No error printed because the non-COW case comes through here */ | ||
271 | else goto out; | ||
272 | |||
273 | *version_out = version; | ||
274 | |||
275 | if(version == 1){ | ||
276 | if(n < sizeof(header->v1)){ | ||
277 | cow_printf("read_cow_header - failed to read V1 " | ||
278 | "header\n"); | ||
279 | goto out; | ||
280 | } | ||
281 | *mtime_out = header->v1.mtime; | ||
282 | *size_out = header->v1.size; | ||
283 | *sectorsize_out = header->v1.sectorsize; | ||
284 | *bitmap_offset_out = sizeof(header->v1); | ||
285 | *align_out = *sectorsize_out; | ||
286 | file = header->v1.backing_file; | ||
287 | } | ||
288 | else if(version == 2){ | ||
289 | if(n < sizeof(header->v2)){ | ||
290 | cow_printf("read_cow_header - failed to read V2 " | ||
291 | "header\n"); | ||
292 | goto out; | ||
293 | } | ||
294 | *mtime_out = ntohl(header->v2.mtime); | ||
295 | *size_out = ntohll(header->v2.size); | ||
296 | *sectorsize_out = ntohl(header->v2.sectorsize); | ||
297 | *bitmap_offset_out = sizeof(header->v2); | ||
298 | *align_out = *sectorsize_out; | ||
299 | file = header->v2.backing_file; | ||
300 | } | ||
301 | else if(version == 3){ | ||
302 | if(n < sizeof(header->v3)){ | ||
303 | cow_printf("read_cow_header - failed to read V2 " | ||
304 | "header\n"); | ||
305 | goto out; | ||
306 | } | ||
307 | *mtime_out = ntohl(header->v3.mtime); | ||
308 | *size_out = ntohll(header->v3.size); | ||
309 | *sectorsize_out = ntohl(header->v3.sectorsize); | ||
310 | *align_out = ntohl(header->v3.alignment); | ||
311 | *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out); | ||
312 | file = header->v3.backing_file; | ||
313 | } | ||
314 | else { | ||
315 | cow_printf("read_cow_header - invalid COW version\n"); | ||
316 | goto out; | ||
317 | } | ||
318 | err = -ENOMEM; | ||
319 | *backing_file_out = cow_strdup(file); | ||
320 | if(*backing_file_out == NULL){ | ||
321 | cow_printf("read_cow_header - failed to allocate backing " | ||
322 | "file\n"); | ||
323 | goto out; | ||
324 | } | ||
325 | err = 0; | ||
326 | out: | ||
327 | cow_free(header); | ||
328 | return(err); | ||
329 | } | ||
330 | |||
331 | int init_cow_file(int fd, char *cow_file, char *backing_file, int sectorsize, | ||
332 | int alignment, int *bitmap_offset_out, | ||
333 | unsigned long *bitmap_len_out, int *data_offset_out) | ||
334 | { | ||
335 | unsigned long long size, offset; | ||
336 | char zero = 0; | ||
337 | int err; | ||
338 | |||
339 | err = write_cow_header(cow_file, fd, backing_file, sectorsize, | ||
340 | alignment, &size); | ||
341 | if(err) | ||
342 | goto out; | ||
343 | |||
344 | *bitmap_offset_out = ROUND_UP(sizeof(struct cow_header_v3), alignment); | ||
345 | cow_sizes(COW_VERSION, size, sectorsize, alignment, *bitmap_offset_out, | ||
346 | bitmap_len_out, data_offset_out); | ||
347 | |||
348 | offset = *data_offset_out + size - sizeof(zero); | ||
349 | err = cow_seek_file(fd, offset); | ||
350 | if(err < 0){ | ||
351 | cow_printf("cow bitmap lseek failed : err = %d\n", -err); | ||
352 | goto out; | ||
353 | } | ||
354 | |||
355 | /* does not really matter how much we write it is just to set EOF | ||
356 | * this also sets the entire COW bitmap | ||
357 | * to zero without having to allocate it | ||
358 | */ | ||
359 | err = cow_write_file(fd, &zero, sizeof(zero)); | ||
360 | if(err != sizeof(zero)){ | ||
361 | cow_printf("Write of bitmap to new COW file '%s' failed, " | ||
362 | "err = %d\n", cow_file, -err); | ||
363 | err = -EINVAL; | ||
364 | goto out; | ||
365 | } | ||
366 | |||
367 | return(0); | ||
368 | |||
369 | out: | ||
370 | return(err); | ||
371 | } | ||
372 | |||
373 | /* | ||
374 | * --------------------------------------------------------------------------- | ||
375 | * Local variables: | ||
376 | * c-file-style: "linux" | ||
377 | * End: | ||
378 | */ | ||