]> wimlib.net Git - wimlib/blob - src/modify.c
Use public domain SHA1 code
[wimlib] / src / modify.c
1 /*
2  * modify.c
3  *
4  * Support for modifying WIM files with image-level operations (delete an image,
5  * add an image, export an imagex from one WIM to another.)  There is nothing
6  * here that lets you change individual files in the WIM; for that you will need
7  * to look at the filesystem implementation in mount.c.
8  *
9  * Copyright (C) 2012 Eric Biggers
10  *
11  * wimlib - Library for working with WIM files 
12  *
13  * This library is free software; you can redistribute it and/or modify it under
14  * the terms of the GNU Lesser General Public License as published by the Free
15  * Software Foundation; either version 2.1 of the License, or (at your option) any
16  * later version.
17  *
18  * This library is distributed in the hope that it will be useful, but WITHOUT ANY
19  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
20  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License along
23  * with this library; if not, write to the Free Software Foundation, Inc., 59
24  * Temple Place, Suite 330, Boston, MA 02111-1307 USA 
25  */
26
27 #include "wimlib_internal.h"
28 #include "util.h"
29 #include "sha1.h"
30 #include "dentry.h"
31 #include "xml.h"
32 #include "lookup_table.h"
33 #include <sys/stat.h>
34 #include <dirent.h>
35 #include <string.h>
36 #include <errno.h>
37
38 /* 
39  * Recursively builds a dentry tree from a directory tree on disk, outside the
40  * WIM file.
41  *
42  * @root:  A dentry that has already been created for the root of the dentry
43  *      tree.
44  * @source_path:  The path to the root of the tree on disk. 
45  * @root_stat:   A pointer to a `struct stat' that contains the metadata for the
46  *      root of the tree on disk. 
47  * @lookup_table: The lookup table for the WIM file.  For each file added to the
48  *              dentry tree being built, an entry is added to the lookup table, 
49  *              unless an identical file is already in the lookup table.  These
50  *              lookup table entries that are added point to the file on disk.
51  *
52  * @return:     0 on success, nonzero on failure.  It is a failure if any of
53  *              the files cannot be `stat'ed, or if any of the needed
54  *              directories cannot be opened or read.  Failure to add the files
55  *              to the WIM may still occur later when trying to actually read 
56  *              the regular files in the tree into the WIM as file resources.
57  */
58 static int build_dentry_tree(struct dentry *root, const char *source_path, 
59                         struct stat *root_stat, struct lookup_table* lookup_table)
60 {
61         int ret = 0;
62
63         stbuf_to_dentry(root_stat, root);
64         if (dentry_is_directory(root)) {
65                 /* Open the directory on disk */
66                 DIR *dir;
67                 struct dirent *p;
68                 struct stat child_stat;
69                 struct dentry *child;
70
71                 dir = opendir(source_path);
72                 if (!dir) {
73                         ERROR("Failed to open the directory `%s': %m\n",
74                                         source_path);
75                         return WIMLIB_ERR_OPEN;
76                 }
77
78                 /* Buffer for names of files in directory. */
79                 size_t len = strlen(source_path);
80                 char name[len + 1 + FILENAME_MAX + 1];
81                 memcpy(name, source_path, len);
82                 name[len] = '/';
83                 errno = 0;
84
85                 /* Create a dentry for each entry in the directory on disk, and recurse
86                  * to any subdirectories. */
87                 while ((p = readdir(dir)) != NULL) {
88                         if (p->d_name[0] == '.' && (p->d_name[1] == '\0'
89                                 || (p->d_name[1] == '.' && p->d_name[2] == '\0')))
90                                         continue;
91                         strcpy(name + len + 1, p->d_name);
92                         if (stat(name, &child_stat) != 0) {
93                                 ERROR("cannot stat `%s': %m\n", name);
94                                 ret = WIMLIB_ERR_STAT;
95                                 break;
96                         }
97                         child = new_dentry(p->d_name);
98                         ret = build_dentry_tree(child, name, &child_stat, 
99                                                 lookup_table);
100                         if (ret != 0)
101                                 break;
102                         link_dentry(child, root);
103                 }
104                 closedir(dir);
105         } else {
106                 struct lookup_table_entry *lte;
107
108                 /* For each non-directory, we must check to see if the file is
109                  * in the lookup table already; if it is, we increment its
110                  * refcnt; otherwise, we create a new lookup table entry and
111                  * insert it. */
112                 ret = sha1sum(source_path, root->hash);
113                 if (ret != 0) {
114                         ERROR("Failed to calculate sha1sum for file `%s'\n", 
115                                                 source_path);
116                         return ret;
117                 }
118
119                 lte = lookup_resource(lookup_table, root->hash);
120                 if (lte) {
121                         lte->refcnt++;
122                 } else {
123                         char *file_on_disk = STRDUP(source_path);
124                         if (!file_on_disk) {
125                                 ERROR("Failed to allocate memory for file "
126                                                 "path!\n");
127                                 return WIMLIB_ERR_NOMEM;
128                         }
129                         lte = new_lookup_table_entry();
130                         if (!lte) {
131                                 ERROR("Failed to allocate memory for new "
132                                                 "lookup table entry!\n");
133                                 FREE(file_on_disk);
134                                 return WIMLIB_ERR_NOMEM;
135                         }
136                         lte->file_on_disk = file_on_disk;
137                         lte->resource_entry.flags = 0;
138                         lte->refcnt       = 1;
139                         lte->part_number  = 1;
140                         lte->resource_entry.original_size = root_stat->st_size;
141                         lte->resource_entry.size = root_stat->st_size;
142                         memcpy(lte->hash, root->hash, WIM_HASH_SIZE);
143                         lookup_table_insert(lookup_table, lte);
144                 }
145         }
146         return ret;
147 }
148
149 struct wim_pair {
150         WIMStruct *src_wim;
151         WIMStruct *dest_wim;
152 };
153
154 /* 
155  * This function takes in a dentry that was previously located only in image(s)
156  * in @src_wim, but now is being added to @dest_wim. If there is in fact already a
157  * lookup table entry for this file in the lookup table of the destination WIM
158  * file, we simply increment its reference count.  Otherwise, a new lookup table
159  * entry is created that references the location of the file resource in the
160  * source WIM file through the other_wim_fp field of the lookup table entry.
161  */
162 static int add_lookup_table_entry_to_dest_wim(struct dentry *dentry, void *arg)
163 {
164         WIMStruct *src_wim, *dest_wim;
165         struct lookup_table_entry *src_table_entry;
166         struct lookup_table_entry *dest_table_entry;
167
168         src_wim = ((struct wim_pair*)arg)->src_wim;
169         dest_wim = ((struct wim_pair*)arg)->dest_wim;
170
171         if (dentry_is_directory(dentry))
172                 return 0;
173
174         src_table_entry = wim_lookup_resource(src_wim, dentry);
175         if (!src_table_entry)
176                 return 0;
177
178         dest_table_entry = wim_lookup_resource(dest_wim, dentry);
179         if (dest_table_entry) {
180                 dest_table_entry->refcnt++;
181         } else {
182                 dest_table_entry = new_lookup_table_entry();
183                 if (!dest_table_entry) {
184                         ERROR("Could not allocate lookup table entry!\n");
185                         return WIMLIB_ERR_NOMEM;
186                 }
187                 dest_table_entry->other_wim_fp = src_wim->fp;
188                 dest_table_entry->other_wim_ctype = 
189                                 wimlib_get_compression_type(src_wim);
190                 dest_table_entry->refcnt = 1;
191                 memcpy(&dest_table_entry->resource_entry, 
192                        &src_table_entry->resource_entry, 
193                        sizeof(struct resource_entry));
194                 memcpy(dest_table_entry->hash, dentry->hash, WIM_HASH_SIZE);
195                 lookup_table_insert(dest_wim->lookup_table, dest_table_entry);
196         }
197         return 0;
198 }
199
200 /*
201  * Adds an image (given by its dentry tree) to the image metadata array of a WIM
202  * file, adds an entry to the lookup table for the image metadata, updates the
203  * image count in the header, and selects the new image. 
204  *
205  * Does not update the XML data.
206  *
207  * @w:            The WIMStruct for the WIM file.
208  * @root_dentry:  The root of the directory tree for the image.
209  */
210 static int add_new_dentry_tree(WIMStruct *w, struct dentry *root_dentry)
211 {
212         struct lookup_table_entry *imd_lookup_entry;
213         struct image_metadata *imd;
214         struct image_metadata *new_imd;
215
216         DEBUG("Reallocing image metadata array for image_count = %u\n",
217                         w->hdr.image_count + 1);
218         imd = CALLOC((w->hdr.image_count + 1), sizeof(struct image_metadata));
219
220         if (!imd) {
221                 ERROR("Failed to allocate memory for new image metadata "
222                                 "array!\n");
223                 return WIMLIB_ERR_NOMEM;
224         }
225
226         memcpy(imd, w->image_metadata, 
227                w->hdr.image_count * sizeof(struct image_metadata));
228         
229         imd_lookup_entry = new_lookup_table_entry();
230         if (!imd_lookup_entry) {
231                 ERROR("Failed to allocate new lookup table entry!\n");
232                 FREE(imd);
233                 return WIMLIB_ERR_NOMEM;
234         }
235
236         imd_lookup_entry->resource_entry.flags = WIM_RESHDR_FLAG_METADATA;
237         randomize_byte_array(imd_lookup_entry->hash, WIM_HASH_SIZE);
238         lookup_table_insert(w->lookup_table, imd_lookup_entry);
239
240         w->hdr.image_count++;
241
242         new_imd = &imd[w->hdr.image_count - 1];
243         new_imd->lookup_table_entry = imd_lookup_entry;
244         new_imd->modified           = true;
245         new_imd->root_dentry        = root_dentry;
246         w->image_metadata = imd;
247
248         /* Change the current image to the new one. */
249         return wimlib_select_image(w, w->hdr.image_count);
250 }
251
252 /*
253  * Copies an image, or all the images, from a WIM file, into another WIM file.
254  */
255 WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, 
256                                   int src_image, 
257                                   WIMStruct *dest_wim, 
258                                   const char *dest_name, 
259                                   const char *dest_description, 
260                                   int flags)
261 {
262         int boot_idx;
263         int i;
264         int ret;
265         struct dentry *root;
266         struct wim_pair wims;
267
268         if (src_image == WIM_ALL_IMAGES) {
269                 if (src_wim->hdr.image_count > 1) {
270
271                         /* multi-image export. */
272
273                         if (flags & WIMLIB_EXPORT_FLAG_BOOT) {
274
275                                 /* Specifying the boot flag on a multi-image
276                                  * source WIM makes the boot index default to
277                                  * the bootable image in the source WIM.  It is
278                                  * an error if there is no such bootable image.
279                                  * */
280
281                                 if (src_wim->hdr.boot_idx == 0) {
282                                         ERROR("Cannot specify `boot' flag "
283                                                         "when exporting multiple "
284                                                         "images from a WIM with no "
285                                                         "bootable images!\n");
286                                         return WIMLIB_ERR_INVALID_PARAM;
287                                 } else {
288                                         boot_idx = src_wim->hdr.boot_idx;
289                                 }
290                         }
291                         if (dest_name || dest_description) {
292                                 ERROR("Image name or image description "
293                                                 "was specified, but we are exporting "
294                                                 "multiple images!\n");
295                                 return WIMLIB_ERR_INVALID_PARAM;
296                         }
297                         for (i = 1; i <= src_wim->hdr.image_count; i++) {
298                                 int export_flags = flags;
299
300                                 if (i != boot_idx)
301                                         export_flags &= ~WIMLIB_EXPORT_FLAG_BOOT;
302
303                                 ret = wimlib_export_image(src_wim, i, dest_wim, 
304                                                         NULL, dest_description,
305                                                         export_flags);
306                                 if (ret != 0)
307                                         return ret;
308                         }
309                         return 0;
310                 } else {
311                         src_image = 1; 
312                 }
313         }
314
315         ret = wimlib_select_image(src_wim, src_image);
316         if (ret != 0) {
317                 ERROR("Could not select image %d from the WIM `%s' "
318                         "to export it!\n", src_image, src_wim->filename);
319                 return ret;
320         }
321
322         if (!dest_name) {
323                 dest_name = wimlib_get_image_name(src_wim, src_image);
324                 DEBUG("Using name `%s' for source image %d\n", 
325                                 dest_name, src_image);
326         }
327
328         DEBUG("Exporting image %d from `%s'\n", src_image, src_wim->filename);
329
330         if (wimlib_image_name_in_use(dest_wim, dest_name)) {
331                 ERROR("There is already an image named `%s' "
332                         "in the destination WIM!\n", dest_name);
333                 return WIMLIB_ERR_IMAGE_NAME_COLLISION;
334         }
335
336
337         root = wim_root_dentry(src_wim);
338         for_dentry_in_tree(root, increment_dentry_refcnt, NULL);
339         wims.src_wim = src_wim;
340         wims.dest_wim = dest_wim;
341         for_dentry_in_tree(root, add_lookup_table_entry_to_dest_wim, &wims);
342         ret = add_new_dentry_tree(dest_wim, root);
343 #ifdef ENABLE_SECURITY_DATA
344         struct wim_security_data *sd = wim_security_data(src_wim);
345         struct image_metadata *new_imd = wim_get_current_image_metadata(dest_wim);
346         new_imd->security_data = sd;
347         if (sd)
348                 sd->refcnt++;
349 #endif
350         if (ret != 0)
351                 return ret;
352
353         if (flags & WIMLIB_EXPORT_FLAG_BOOT) {
354                 DEBUG("Setting boot_idx to %d\n", dest_wim->hdr.image_count);
355                 dest_wim->hdr.boot_idx = dest_wim->hdr.image_count;
356         }
357
358         return xml_export_image(src_wim->wim_info, src_image, &dest_wim->wim_info,
359                         dest_name, dest_description);
360 }
361
362 /* 
363  * Deletes an image from the WIM. 
364  */
365 WIMLIBAPI int wimlib_delete_image(WIMStruct *w, int image)
366 {
367         int num_images;
368         int i;
369         int ret;
370         struct image_metadata *imd;
371
372         if (image == WIM_ALL_IMAGES) {
373                 num_images = w->hdr.image_count;
374                 for (i = 1; i <= num_images; i++) {
375                         /* Always delete the first image, since by the end
376                          * there won't be any more than that!  */
377                         ret = wimlib_delete_image(w, 1);
378                         if (ret != 0)
379                                 return ret;
380                 }
381                 return 0;
382         }
383
384         DEBUG("Deleting image %d\n", image);
385
386         /* Even if the dentry tree is not allocated, we must select it (and
387          * therefore allocate it) so that we can decrement the reference counts
388          * in the lookup table.  */
389         ret = wimlib_select_image(w, image);
390         if (ret != 0)
391                 return ret;
392
393         /* Free the dentry tree, any lookup table entries that have their
394          * refcnt decremented to 0, and the security data. */
395         imd = wim_get_current_image_metadata(w);
396         free_dentry_tree(imd->root_dentry, w->lookup_table, true);
397 #ifdef ENABLE_SECURITY_DATA
398         free_security_data(imd->security_data);
399 #endif
400
401         /* Get rid of the lookup table entry for this image's metadata resource
402          * */
403         lookup_table_remove(w->lookup_table, imd->lookup_table_entry);
404
405         /* Get rid of the empty slot in the image metadata array. */
406         for (i = image - 1; i < w->hdr.image_count - 1; i++)
407                 memcpy(&w->image_metadata[i], &w->image_metadata[i + 1],
408                                 sizeof(struct image_metadata));
409
410         /* Decrement the image count. */
411         w->hdr.image_count--;
412         if (w->hdr.image_count == 0) {
413                 FREE(w->image_metadata);
414                 w->image_metadata = NULL;
415         }
416
417         /* Fix the boot index. */
418         if (w->hdr.boot_idx == image)
419                 w->hdr.boot_idx = 0;
420         else if (w->hdr.boot_idx > image)
421                 w->hdr.boot_idx--;
422
423         w->current_image = WIM_NO_IMAGE;
424
425         /* Remove the image from the XML information. */
426         xml_delete_image(&w->wim_info, image);
427         return 0;
428 }
429
430 /*
431  * Adds an image to a WIM file from a directory tree on disk.
432  */
433 WIMLIBAPI int wimlib_add_image(WIMStruct *w, const char *dir, 
434                                const char *name, const char *description, 
435                                const char *flags_element, int flags)
436 {
437         struct dentry *root_dentry;
438         struct stat root_stat;
439         int ret;
440
441         DEBUG("Adding dentry tree from dir `%s'\n", dir);
442
443         if (!name || !*name) {
444                 ERROR("Must specify a non-empty string for the image name!\n");
445                 return WIMLIB_ERR_INVALID_PARAM;
446         }
447         if (!dir) {
448                 ERROR("Must specify the name of a directory!\n");
449                 return WIMLIB_ERR_INVALID_PARAM;
450         }
451
452         if (wimlib_image_name_in_use(w, name)) {
453                 ERROR("There is already an image named `%s' in %s!\n",
454                                 name, w->filename);
455                 return WIMLIB_ERR_IMAGE_NAME_COLLISION;
456         }
457
458         DEBUG("Creating root dentry.\n");
459
460         root_dentry = new_dentry("");
461         ret = calculate_dentry_full_path(root_dentry, NULL);
462         if (ret != 0)
463                 return ret;
464         root_dentry->attributes |= WIM_FILE_ATTRIBUTE_DIRECTORY;
465
466         /* Construct the dentry tree from the outside filesystem. */
467         if (stat(dir, &root_stat) != 0) {
468                 ERROR("Failed to stat `%s': %m\n", dir);
469                 return WIMLIB_ERR_STAT;
470         }
471         if (!S_ISDIR(root_stat.st_mode)) {
472                 ERROR("`%s' is not a directory!\n", dir);
473                 return WIMLIB_ERR_NOTDIR;
474         }
475         DEBUG("Building dentry tree.\n");
476         ret = build_dentry_tree(root_dentry, dir, &root_stat, 
477                                 w->lookup_table);
478
479         if (ret != 0) {
480                 ERROR("Failed to build dentry tree for `%s'!\n", dir);
481                 goto err1;
482         }
483
484         DEBUG("Recalculating full paths of dentries.\n");
485         ret = for_dentry_in_tree(root_dentry, 
486                                  calculate_dentry_full_path, NULL);
487         if (ret != 0) {
488                 ERROR("Failed to calculate full paths of dentry tree.\n");
489                 goto err1;
490         }
491
492         ret = add_new_dentry_tree(w, root_dentry);
493         if (ret != 0)
494                 goto err1;
495
496         if (flags & WIMLIB_ADD_IMAGE_FLAG_BOOT) {
497                 /* Call wimlib_set_boot_idx rather than set boot_idx directly so
498                  * that the boot metadata resource entry in the header gets
499                  * updated. */
500                 wimlib_set_boot_idx(w, w->hdr.image_count);
501         }
502
503         ret = xml_add_image(w, root_dentry, name, description, flags_element);
504         if (ret != 0)
505                 goto err1;
506
507         return 0;
508 err1:
509         free_dentry_tree(root_dentry, w->lookup_table, true);
510         return ret;
511 }