]> wimlib.net Git - wimlib/blob - src/write.c
Empty file fix
[wimlib] / src / write.c
1 /*
2  * write.c
3  *
4  * Support for writing WIM files; write a WIM file, overwrite a WIM file, write
5  * compressed file resources, etc.
6  *
7  * Copyright (C) 2010 Carl Thijssen
8  * Copyright (C) 2012 Eric Biggers
9  *
10  * wimlib - Library for working with WIM files 
11  *
12  * This library is free software; you can redistribute it and/or modify it under
13  * the terms of the GNU Lesser General Public License as published by the Free
14  * Software Foundation; either version 2.1 of the License, or (at your option) any
15  * later version.
16  *
17  * This library is distributed in the hope that it will be useful, but WITHOUT ANY
18  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
19  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License along
22  * with this library; if not, write to the Free Software Foundation, Inc., 59
23  * Temple Place, Suite 330, Boston, MA 02111-1307 USA 
24  */
25 #include "wimlib_internal.h"
26 #include "io.h"
27 #include "dentry.h"
28 #include "lookup_table.h"
29 #include "xml.h"
30 #include <unistd.h>
31
32 /* Reopens the FILE* for a WIM read-write. */
33 static int reopen_rw(WIMStruct *w)
34 {
35         FILE *fp;
36
37         if (fclose(w->fp) != 0)
38                 ERROR("Failed to close the file `%s': %m\n", w->filename);
39         fp = fopen(w->filename, "r+b");
40         if (!fp) {
41                 ERROR("Failed to open `%s' for reading and writing: "
42                                 "%m\n", w->filename);
43                 return WIMLIB_ERR_OPEN;
44         }
45         w->fp = fp;
46         return 0;
47 }
48
49
50
51 /* 
52  * Writes a WIM file to the original file that it was read from, overwriting it.
53  */
54 WIMLIBAPI int wimlib_overwrite(WIMStruct *w, int flags)
55 {
56         const char *wimfile_name;
57         size_t wim_name_len;
58         int ret;
59         
60         wimfile_name = w->filename;
61
62         DEBUG("Replacing WIM file `%s'\n", wimfile_name);
63
64         if (!wimfile_name)
65                 return WIMLIB_ERR_NO_FILENAME;
66
67         /* Write the WIM to a temporary file. */
68         /* XXX should the temporary file be somewhere else? */
69         wim_name_len = strlen(wimfile_name);
70         char tmpfile[wim_name_len + 10];
71         memcpy(tmpfile, wimfile_name, wim_name_len);
72         randomize_char_array_with_alnum(tmpfile + wim_name_len, 9);
73         tmpfile[wim_name_len + 9] = '\0';
74
75         ret = wimlib_write(w, tmpfile, WIM_ALL_IMAGES, flags);
76         if (ret != 0) {
77                 ERROR("Failed to write the WIM file `%s'!\n", tmpfile);
78                 return ret;
79         }
80
81         DEBUG("Closing original WIM file.\n");
82         /* Close the original WIM file that was opened for reading. */
83         if (w->fp) {
84                 if (fclose(w->fp) != 0) {
85                         WARNING("Failed to close the file `%s'\n",
86                                         wimfile_name);
87                 }
88                 w->fp = NULL;
89         }
90
91         DEBUG("Renaming `%s' to `%s'\n", tmpfile, wimfile_name);
92
93         /* Rename the new file to the old file .*/
94         if (rename(tmpfile, wimfile_name) != 0) {
95                 ERROR("Failed to rename `%s' to `%s': %m\n", tmpfile, 
96                                                                 wimfile_name);
97                 /* Remove temporary file. */
98                 if (unlink(tmpfile) != 0)
99                         ERROR("Failed to remove `%s': %m\n", tmpfile);
100                 return WIMLIB_ERR_RENAME;
101         }
102
103         return 0;
104 }
105
106
107 WIMLIBAPI int wimlib_overwrite_xml_and_header(WIMStruct *w, int flags)
108 {
109         int ret;
110         FILE *fp;
111         u8 *integrity_table = NULL;
112         off_t xml_end;
113         off_t xml_size;
114         size_t bytes_written;
115
116         DEBUG("Overwriting XML and header of `%s', flags = %d\n", 
117                                 w->filename, flags);
118         if (!w->filename)
119                 return WIMLIB_ERR_NO_FILENAME;
120
121         ret = reopen_rw(w);
122         if (ret != 0)
123                 return ret;
124
125         fp = w->fp;
126
127         /* The old integrity table is still OK, as the SHA1 message digests in
128          * the integrity table include neither the header nor the XML data.
129          * Save it for later if it exists and an integrity table was required.
130          * */
131         if (flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY && 
132                         w->hdr.integrity.offset != 0) {
133                 DEBUG("Reading existing integrity table.\n");
134                 integrity_table = MALLOC(w->hdr.integrity.size);
135                 if (!integrity_table)
136                         return WIMLIB_ERR_NOMEM;
137
138                 ret = read_uncompressed_resource(fp, w->hdr.integrity.offset,
139                                                  w->hdr.integrity.original_size,
140                                                  integrity_table);
141                 if (ret != 0)
142                         goto err;
143                 DEBUG("Done reading existing integrity table.\n");
144         }
145
146         DEBUG("Overwriting XML data.\n");
147         /* Overwrite the XML data. */
148         if (fseeko(fp, w->hdr.xml_res_entry.offset, SEEK_SET) != 0) {
149                 ERROR("Failed to seek to byte %"PRIu64" for XML data: "
150                                 "%m\n", w->hdr.xml_res_entry.offset);
151                 ret = WIMLIB_ERR_WRITE;
152                 goto err;
153         }
154         ret = write_xml_data(w->wim_info, WIM_ALL_IMAGES, fp, 0);
155         if (ret != 0)
156                 goto err;
157
158         DEBUG("Updating XML resource entry.\n");
159         /* Update the XML resource entry in the WIM header. */
160         xml_end = ftello(fp);
161         if (xml_end == -1) {
162                 ret = WIMLIB_ERR_WRITE;
163                 goto err;
164         }
165         xml_size = xml_end - w->hdr.xml_res_entry.offset;
166         w->hdr.xml_res_entry.size = xml_size;
167         w->hdr.xml_res_entry.original_size = xml_size;
168
169         if (flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) {
170                 DEBUG("Writing integrity table.\n");
171                 w->hdr.integrity.offset        = xml_end;
172                 if (integrity_table) {
173                         /* The existing integrity table was saved. */
174                         bytes_written = fwrite(integrity_table, 1, 
175                                                w->hdr.integrity.size, fp);
176                         if (bytes_written != w->hdr.integrity.size) {
177                                 ERROR("Failed to write integrity table: %m\n");
178                                 ret = WIMLIB_ERR_WRITE;
179                                 goto err;
180                         }
181                         FREE(integrity_table);
182                 } else {
183                         /* There was no existing integrity table, so a new one
184                          * must be calculated. */
185                         ret = write_integrity_table(fp, WIM_HEADER_DISK_SIZE,
186                                         w->hdr.lookup_table_res_entry.offset + 
187                                         w->hdr.lookup_table_res_entry.size,
188                                         flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS);
189                         if (ret != 0)
190                                 goto err;
191
192                         off_t integrity_size           = ftello(fp) - xml_end;
193                         w->hdr.integrity.size          = integrity_size;
194                         w->hdr.integrity.original_size = integrity_size;
195                         w->hdr.integrity.flags         = 0;
196                 }
197         } else {
198                 DEBUG("Truncating file to end of XML data.\n");
199                 /* No integrity table to write.  The file should be truncated
200                  * because it's possible that the old file was longer (due to it
201                  * including an integrity table, or due to its XML data being
202                  * longer) */
203                 if (fflush(fp) != 0) {
204                         ERROR("Failed to flush stream for file `%s': %m\n",
205                                         w->filename);
206                         return WIMLIB_ERR_WRITE;
207                 }
208                 if (ftruncate(fileno(fp), xml_end) != 0) {
209                         ERROR("Failed to truncate `%s' to %"PRIu64" "
210                                         "bytes: %m\n", 
211                                         w->filename, xml_end);
212                         return WIMLIB_ERR_WRITE;
213                 }
214                 memset(&w->hdr.integrity, 0, sizeof(struct resource_entry));
215         }
216
217         DEBUG("Overwriting header.\n");
218         /* Overwrite the header. */
219         if (fseeko(fp, 0, SEEK_SET) != 0) {
220                 ERROR("Failed to seek to beginning of `%s': %m\n",
221                                 w->filename);
222                 return WIMLIB_ERR_WRITE;
223         }
224
225         ret = write_header(&w->hdr, fp);
226         if (ret != 0)
227                 return ret;;
228
229         DEBUG("Closing file.\n");
230         if (fclose(fp) != 0) {
231                 ERROR("Failed to close `%s': %m\n", w->filename);
232                 return WIMLIB_ERR_WRITE;
233         }
234         w->fp = NULL;
235         DEBUG("Done.\n");
236         return 0;
237 err:
238         FREE(integrity_table);
239         return ret;
240 }
241
242
243 /* Write the file resources for the current image. */
244 static int write_file_resources(WIMStruct *w)
245 {
246
247         DEBUG("Writing file resources for image %u\n", w->current_image);
248         return for_dentry_in_tree(wim_root_dentry(w), write_file_resource, w);
249 }
250
251 /* Write lookup table, xml data, lookup table, and rewrite header 
252  *
253  * write_lt is zero iff the lookup table is not to be written; i.e. it is
254  * handled elsewhere. */
255 int finish_write(WIMStruct *w, int image, int flags, int write_lt)
256 {
257         off_t lookup_table_offset;
258         off_t xml_data_offset;
259         off_t lookup_table_size;
260         off_t integrity_offset;
261         off_t xml_data_size;
262         off_t end_offset;
263         off_t integrity_size;
264         int ret;
265         int i;
266         struct wim_header hdr;
267         FILE *out = w->out_fp;
268
269         if (write_lt) {
270                 lookup_table_offset = ftello(out);
271                 if (lookup_table_offset == -1)
272                         return WIMLIB_ERR_WRITE;
273
274                 DEBUG("Writing lookup table (offset %"PRIu64")\n", lookup_table_offset);
275                 /* Write the lookup table. */
276                 ret = write_lookup_table(w->lookup_table, out);
277                 if (ret != 0)
278                         return ret;
279         }
280
281
282         xml_data_offset = ftello(out);
283         if (xml_data_offset == -1)
284                 return WIMLIB_ERR_WRITE;
285         DEBUG("Writing XML data (offset %"PRIu64")\n", xml_data_offset);
286
287         /* @hdr will be the header for the new WIM.  First copy all the data
288          * from the header in the WIMStruct; then set all the fields that may
289          * have changed, including the resource entries, boot index, and image
290          * count.  */
291         memcpy(&hdr, &w->hdr, sizeof(struct wim_header));
292         if (write_lt) {
293                 lookup_table_size = xml_data_offset - lookup_table_offset;
294                 hdr.lookup_table_res_entry.offset        = lookup_table_offset;
295                 hdr.lookup_table_res_entry.size          = lookup_table_size;
296         }
297         hdr.lookup_table_res_entry.original_size = hdr.lookup_table_res_entry.size;
298         hdr.lookup_table_res_entry.flags         = WIM_RESHDR_FLAG_METADATA;
299
300         ret = write_xml_data(w->wim_info, image, out, 
301                              write_lt ? 0 : wim_info_get_total_bytes(w->wim_info));
302         if (ret != 0)
303                 return ret;
304
305         integrity_offset = ftello(out);
306         if (integrity_offset == -1)
307                 return WIMLIB_ERR_WRITE;
308         xml_data_size = integrity_offset - xml_data_offset;
309
310         hdr.xml_res_entry.offset                 = xml_data_offset;
311         hdr.xml_res_entry.size                   = xml_data_size;
312         hdr.xml_res_entry.original_size          = xml_data_size;
313         hdr.xml_res_entry.flags                  = 0;
314
315         if (flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) {
316                 ret = write_integrity_table(out, WIM_HEADER_DISK_SIZE, 
317                                             xml_data_offset, 
318                                             flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS);
319                 if (ret != 0)
320                         return ret;
321                 end_offset = ftello(out);
322                 if (end_offset == -1)
323                         return WIMLIB_ERR_WRITE;
324                 integrity_size = end_offset - integrity_offset;
325                 hdr.integrity.offset = integrity_offset;
326                 hdr.integrity.size   = integrity_size;
327                 hdr.integrity.original_size = integrity_size;
328         } else {
329                 hdr.integrity.offset        = 0;
330                 hdr.integrity.size          = 0;
331                 hdr.integrity.original_size = 0;
332         }
333         hdr.integrity.flags = 0;
334
335         DEBUG("Updating WIM header.\n");
336
337         /* 
338          * In the WIM header, there is room for the resource entry for a
339          * metadata resource labeled as the "boot metadata".  This entry should
340          * be zeroed out if there is no bootable image (boot_idx 0).  Otherwise,
341          * it should be a copy of the resource entry for the image that is
342          * marked as bootable.  This is not well documented...
343          */
344         if (hdr.boot_idx == 0 || !w->image_metadata
345                         || (image != WIM_ALL_IMAGES && image != hdr.boot_idx)) {
346                 memset(&hdr.boot_metadata_res_entry, 0, 
347                        sizeof(struct resource_entry));
348         } else {
349                 memcpy(&hdr.boot_metadata_res_entry, 
350                        &w->image_metadata[hdr.boot_idx - 1].lookup_table_entry->
351                                         output_resource_entry,
352                                         sizeof(struct resource_entry));
353         }
354
355         /* Set image count and boot index correctly for single image writes */
356         if (image != WIM_ALL_IMAGES) {
357                 hdr.image_count = 1;
358                 if (hdr.boot_idx == image)
359                         hdr.boot_idx = 1;
360                 else
361                         hdr.boot_idx = 0;
362         }
363
364
365         if (fseeko(out, 0, SEEK_SET) != 0)
366                 return WIMLIB_ERR_WRITE;
367
368         return write_header(&hdr, out);
369 }
370
371 /* Open file stream and write dummy header for WIM. */
372 int begin_write(WIMStruct *w, const char *path, int flags)
373 {
374         const char *mode;
375         DEBUG("Opening `%s' for new WIM\n", path);
376
377         /* checking the integrity requires going back over the file to read it.
378          * XXX 
379          * (It also would be possible to keep a running sha1sum as the file
380          * as written-- this would be faster, but a bit more complicated) */
381         if (flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) 
382                 mode = "w+b";
383         else
384                 mode = "wb";
385
386         w->out_fp = fopen(path, mode);
387         if (!w->out_fp) {
388                 ERROR("Failed to open the file `%s' for writing!\n", path);
389                 return WIMLIB_ERR_OPEN;
390         }
391
392         /* Write dummy header. It will be overwritten later. */
393         return write_header(&w->hdr, w->out_fp);
394 }
395
396 /* Writes the WIM to a file.  */
397 WIMLIBAPI int wimlib_write(WIMStruct *w, const char *path, int image, int flags)
398 {
399         int ret;
400
401         if (image != WIM_ALL_IMAGES && 
402                         (image < 1 || image > w->hdr.image_count))
403                 return WIMLIB_ERR_INVALID_IMAGE;
404
405         if (image == WIM_ALL_IMAGES)
406                 DEBUG("Writing all images to `%s'\n", path);
407         else
408                 DEBUG("Writing image %d to `%s'\n", image, path);
409
410         ret = begin_write(w, path, flags);
411         if (ret != 0)
412                 goto done;
413
414         for_lookup_table_entry(w->lookup_table, zero_out_refcnts, NULL);
415
416         ret = for_image(w, image, write_file_resources);
417         if (ret != 0) {
418                 ERROR("Failed to write file resources!\n");
419                 goto done;
420         }
421
422         ret = for_image(w, image, write_metadata_resource);
423
424         if (ret != 0) {
425                 ERROR("Failed to write image metadata!\n");
426                 goto done;
427         }
428
429         ret = finish_write(w, image, flags, 1);
430
431 done:
432         DEBUG("Closing output file.\n");
433         if (w->out_fp != NULL) {
434                 if (fclose(w->out_fp) != 0) {
435                         ERROR("Failed to close the file `%s': %m\n", path);
436                         ret = WIMLIB_ERR_WRITE;
437                 }
438                 w->out_fp = NULL;
439         }
440         return ret;
441 }