long reserved;
};
-/** Set or unset the WIM header flag that marks it read-only
- * (WIM_HDR_FLAG_READONLY in Microsoft's documentation), based on the
- * ::wimlib_wim_info.is_marked_readonly member of the @p info parameter. This
- * is distinct from basic file permissions; this flag can be set on a WIM file
- * that is physically writable. If this flag is set, all further operations to
- * modify the WIM will fail, except calling wimlib_overwrite() with
- * ::WIMLIB_WRITE_FLAG_IGNORE_READONLY_FLAG specified, which is a loophole that
- * allows you to set this flag persistently on the underlying WIM file.
+/** Set or unset the "readonly" WIM header flag (WIM_HDR_FLAG_READONLY in
+ * Microsoft's documentation), based on the ::wimlib_wim_info.is_marked_readonly
+ * member of the @p info parameter. This is distinct from basic file
+ * permissions; this flag can be set on a WIM file that is physically writable.
+ *
+ * wimlib disallows modifying on-disk WIM files with the readonly flag set.
+ * However, wimlib_overwrite() with ::WIMLIB_WRITE_FLAG_IGNORE_READONLY_FLAG
+ * will override this --- and in fact, this is necessary to set the readonly
+ * flag persistently on an existing WIM file.
*/
#define WIMLIB_CHANGE_READONLY_FLAG 0x00000001
* only if it is writable at the filesystem level, does not have the
* WIM_HDR_FLAG_READONLY flag set in its header, and is not part of a spanned
* set. It is not required to provide this flag before attempting to make
- * changes to the WIM, but with this flag you get an error sooner rather than
- * later. */
+ * changes to the WIM, but with this flag you get an error immediately rather
+ * than potentially much later, when wimlib_overwrite() is finally called. */
#define WIMLIB_OPEN_FLAG_WRITE_ACCESS 0x00000004
/** @} */
* There is already an image in @p wim named @p name.
* @retval ::WIMLIB_ERR_NOMEM
* Failed to allocate the memory needed to add the new image.
- * @retval ::WIMLIB_ERR_WIM_IS_READONLY
- * The WIM file is considered read-only because of any of the reasons
- * mentioned in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS
- * flag.
*/
extern int
wimlib_add_empty_image(WIMStruct *wim,
*
* @retval ::WIMLIB_ERR_INVALID_IMAGE
* @p image does not exist in the WIM and is not ::WIMLIB_ALL_IMAGES.
- * @retval ::WIMLIB_ERR_WIM_IS_READONLY
- * The WIM file is considered read-only because of any of the reasons
- * mentioned in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS
- * flag.
*
* This function can additionally return ::WIMLIB_ERR_DECOMPRESSION,
* ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND,
* WIM parts were not referenced with wimlib_reference_resources() or
* wimlib_reference_resource_files() before the call to
* wimlib_export_image().
- * @retval ::WIMLIB_ERR_WIM_IS_READONLY
- * @p dest_wim is considered read-only because of any of the reasons
- * mentioned in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS
- * flag.
*
* This function can additionally return ::WIMLIB_ERR_DECOMPRESSION,
* ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND,
* @retval ::WIMLIB_ERR_NOMEM
* Failed to allocate the memory needed to duplicate the @p description
* string.
- * @retval ::WIMLIB_ERR_WIM_IS_READONLY
- * @p wim is considered read-only because of any of the reasons mentioned
- * in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag.
*/
extern int
wimlib_set_image_descripton(WIMStruct *wim, int image,
* ::WIMLIB_CHANGE_BOOT_INDEX, and/or ::WIMLIB_CHANGE_RPFIX_FLAG.
*
* @return 0 on success; nonzero on failure.
- * @retval ::WIMLIB_ERR_WIM_IS_READONLY
- * The WIM file is considered read-only because of any of the reasons
- * mentioned in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS
- * flag. However, as a special case, if you are using
- * ::WIMLIB_CHANGE_READONLY_FLAG to unset the readonly flag, then this
- * function will not fail due to the readonly flag being previously set.
* @retval ::WIMLIB_ERR_IMAGE_COUNT
* ::WIMLIB_CHANGE_BOOT_INDEX was specified, but
* ::wimlib_wim_info.boot_index did not specify 0 or a valid 1-based image
* @p image does not specify a single existing image in @p wim.
* @retval ::WIMLIB_ERR_NOMEM
* Failed to allocate the memory needed to duplicate the @p flags string.
- * @retval ::WIMLIB_ERR_WIM_IS_READONLY
- * @p wim is considered read-only because of any of the reasons mentioned
- * in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag.
*/
extern int
wimlib_set_image_flags(WIMStruct *wim, int image, const wimlib_tchar *flags);
* @p image does not specify a single existing image in @p wim.
* @retval ::WIMLIB_ERR_NOMEM
* Failed to allocate the memory needed to duplicate the @p name string.
- * @retval ::WIMLIB_ERR_WIM_IS_READONLY
- * @p wim is considered read-only because of any of the reasons mentioned
- * in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag.
*/
extern int
wimlib_set_image_name(WIMStruct *wim, int image, const wimlib_tchar *name);
* a supported file type (e.g. a device file). Only if
* ::WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE specified in @p the add_flags
* for an update command.
- * @retval ::WIMLIB_ERR_WIM_IS_READONLY
- * The WIM file is considered read-only because of any of the reasons
- * mentioned in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS
- * flag.
*
* This function can additionally return ::WIMLIB_ERR_DECOMPRESSION,
* ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND,
/* Number of times this lookup table entry is referenced by dentries in
* the WIM. When a WIM's lookup table is read, this field is
- * initialized from a corresponding entry; while it should be correct,
- * in general it may not be. wim_recalculate_refcnts() recalculates the
- * reference counts for all streams and is run before doing any
- * deletions. */
+ * initialized from a corresponding entry.
+ *
+ * However, see lte_decrement_refcnt() for information about the
+ * limitations of this field. */
u32 refcnt;
/* When a WIM file is written, this is set to the number of references
u32 alloc_stream_owners;
};
};
-
- /* Actual reference count to this stream (only used while
- * verifying an image). */
- u32 real_refcnt;
};
/* Temporary list fields. */
extern int
lte_zero_out_refcnt(struct wim_lookup_table_entry *lte, void *ignore);
-extern int
-lte_zero_real_refcnt(struct wim_lookup_table_entry *lte, void *ignore);
-
static inline bool
lte_is_partial(const struct wim_lookup_table_entry * lte)
{
/* Have any images been deleted? */
u8 deletion_occurred : 1;
- /* Do we know that all the stream reference counts in the WIM are
- * correct? If so, this is set to 1 and deletions are safe; otherwise
- * this is set to 0 and deletions are not safe until reference counts
- * are recalculated. (This is due to a bug in M$'s software that
- * generates WIMs with invalid reference counts.) */
- u8 refcnts_ok : 1;
-
/* Has the underlying WIM file been locked for appending? */
u8 locked_for_append : 1;
return (wim->image_metadata != NULL || wim->hdr.image_count == 0);
}
-extern int
-wim_recalculate_refcnts(WIMStruct *wim);
-
extern int
set_wim_hdr_cflags(int ctype, struct wim_header *hdr);
extern int
can_modify_wim(WIMStruct *wim);
-extern int
-can_delete_from_wim(WIMStruct *wim);
-
#endif /* _WIMLIB_WIM_H */
imagex_capture_or_append(int argc, tchar **argv, int cmd)
{
int c;
- int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
+ int open_flags = 0;
int add_image_flags = WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE |
WIMLIB_ADD_IMAGE_FLAG_WINCONFIG |
WIMLIB_ADD_IMAGE_FLAG_VERBOSE;
/* Open the existing WIM, or create a new one. */
if (cmd == CMD_APPEND) {
- ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
- imagex_progress_func, NULL);
+ ret = wimlib_open_wim_with_progress(wimfile,
+ open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
+ &wim,
+ imagex_progress_func,
+ NULL);
if (ret)
goto out_free_capture_sources;
} else {
for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
ret = wimlib_open_wim_with_progress(
- base_wimfiles.strings[i],
- open_flags & ~WIMLIB_OPEN_FLAG_WRITE_ACCESS,
+ base_wimfiles.strings[i], open_flags,
&base_wims[i], imagex_progress_func, NULL);
if (ret)
goto out_free_base_wims;
template_wim = wim;
} else {
ret = wimlib_open_wim_with_progress(template_wimfile,
- open_flags & ~WIMLIB_OPEN_FLAG_WRITE_ACCESS,
+ open_flags,
&template_wim,
imagex_progress_func,
NULL);
{
int ret;
- ret = can_modify_wim(wim);
- if (ret)
- return ret;
-
if (!name)
name = T("");
int ret;
int first, last;
- ret = can_delete_from_wim(wim);
- if (ret)
- return ret;
-
if (image == WIMLIB_ALL_IMAGES) {
last = wim->hdr.image_count;
first = 1;
for (image = last; image >= first; image--) {
DEBUG("Deleting image %d", image);
- /* Even if the dentry tree is not allocated, we must select it (and
- * therefore allocate it) so that we can decrement the reference counts
- * in the lookup table. */
+ /* Even if the dentry tree is not allocated, we must select it
+ * (and therefore allocate it) so that we can decrement stream
+ * reference counts. */
ret = select_wim_image(wim, image);
if (ret)
return ret;
- /* Unless the image metadata is shared by another WIMStruct, free the
- * dentry tree, any lookup table entries that have their refcnt
- * decremented to 0, and the security data. */
+ /* Unless the image metadata is shared by another WIMStruct,
+ * free the dentry tree, free the security data, and decrement
+ * stream reference counts. */
put_image_metadata(wim->image_metadata[image - 1], wim->lookup_table);
/* Get rid of the empty slot in the image metadata array. */
if (!wim_has_metadata(dest_wim))
return WIMLIB_ERR_METADATA_NOT_FOUND;
- /* Destination WIM must be writable. */
- ret = can_modify_wim(dest_wim);
- if (ret)
- return ret;
-
if (src_image == WIMLIB_ALL_IMAGES) {
/* Multi-image export. */
if ((!(export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) &&
}
/*
- * Decrements the reference count for the lookup table entry @lte, which must be
- * inserted in the stream lookup table @table.
+ * Decrements the reference count of the single-instance stream @lte, which must
+ * be inserted in the stream lookup table @table.
*
- * If the reference count reaches 0, this may cause @lte to be destroyed.
- * However, we may retain entries with 0 reference count. This does not affect
- * correctness, but it prevents the entries for valid streams in a WIM archive,
- * which will continue to be present after appending to the file, from being
- * lost merely because we dropped all references to them.
+ * If the stream's reference count reaches 0, we may unlink it from @table and
+ * free it. However, we retain streams with 0 reference count that originated
+ * from WIM files (RESOURCE_IN_WIM). We do this for two reasons:
+ *
+ * 1. This prevents information about valid streams in a WIM file --- streams
+ * which will continue to be present after appending to the WIM file --- from
+ * being lost merely because we dropped all references to them.
+ *
+ * 2. Stream reference counts we read from WIM files can't be trusted. It's
+ * possible that a WIM has reference counts that are too low; WIMGAPI
+ * sometimes creates WIMs where this is the case. It's also possible that
+ * streams have been referenced from an external WIM; those streams can
+ * potentially have any reference count at all, either lower or higher than
+ * would be expected for this WIM ("this WIM" meaning the owner of @table) if
+ * it were a standalone WIM.
+ *
+ * So we can't take the reference counts too seriously. But at least, we do
+ * recalculate by default when writing a new WIM file.
*/
void
lte_decrement_refcnt(struct wim_lookup_table_entry *lte,
struct wim_lookup_table *table)
{
- wimlib_assert(lte->refcnt != 0);
+ if (unlikely(lte->refcnt == 0)) /* See comment above */
+ return;
if (--lte->refcnt == 0) {
if (lte->unhashed) {
/* Which part of the split WIM this stream is in; indexed from 1. */
le16 part_number;
- /* Reference count of this stream over all WIM images. */
+ /* Reference count of this stream over all WIM images. (But see comment
+ * above lte_decrement_refcnt().) */
le32 refcnt;
/* SHA1 message digest of the uncompressed data of this stream, or
return ret;
}
-int
-lte_zero_real_refcnt(struct wim_lookup_table_entry *lte, void *_ignore)
-{
- lte->real_refcnt = 0;
- return 0;
-}
-
int
lte_zero_out_refcnt(struct wim_lookup_table_entry *lte, void *_ignore)
{
if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
if (!wim->filename)
return WIMLIB_ERR_NO_FILENAME;
- ret = can_delete_from_wim(wim);
+ ret = can_modify_wim(wim);
if (ret)
return ret;
}
DEBUG("Updating image %d with %zu commands", image, num_cmds);
- if (have_command_type(cmds, num_cmds, WIMLIB_UPDATE_OP_DELETE))
- ret = can_delete_from_wim(wim);
- else
- ret = can_modify_wim(wim);
-
- if (ret)
- goto out;
-
/* Load the metadata for the image to modify (if not loaded already) */
ret = select_wim_image(wim, image);
if (ret)
#include "wimlib/progress.h"
#include "wimlib/security.h"
-static int
-lte_fix_refcnt(struct wim_lookup_table_entry *lte, void *ctr)
-{
- if (lte->refcnt != lte->real_refcnt) {
- lte->refcnt = lte->real_refcnt;
- ++*(unsigned long *)ctr;
- }
- return 0;
-}
-
-static void
-tally_inode_refcnts(const struct wim_inode *inode,
- const struct wim_lookup_table *lookup_table)
-{
- for (unsigned i = 0; i <= inode->i_num_ads; i++) {
- struct wim_lookup_table_entry *lte;
- lte = inode_stream_lte(inode, i, lookup_table);
- if (lte)
- lte->real_refcnt += inode->i_nlink;
- }
-}
-
-
-static int
-tally_image_refcnts(WIMStruct *wim)
-{
- const struct wim_image_metadata *imd;
- const struct wim_inode *inode;
-
- imd = wim_get_current_image_metadata(wim);
- image_for_each_inode(inode, imd)
- tally_inode_refcnts(inode, wim->lookup_table);
- return 0;
-}
-
-
-/* Ideally this would be unnecessary... however, the WIMs for Windows 8 are
- * screwed up because some lookup table entries are referenced more times than
- * their stated reference counts. So theoretically, if we delete all the
- * references to a stream and then remove it, it might still be referenced
- * somewhere else, making a file be missing from the WIM... So, work around this
- * problem by looking at ALL the images to re-calculate the reference count of
- * EVERY lookup table entry. This only absolutely has to be done before an image
- * is deleted or before an image is mounted read-write. */
-int
-wim_recalculate_refcnts(WIMStruct *wim)
-{
- unsigned long num_ltes_with_bogus_refcnt = 0;
- int ret;
-
- for_lookup_table_entry(wim->lookup_table, lte_zero_real_refcnt, NULL);
- ret = for_image(wim, WIMLIB_ALL_IMAGES, tally_image_refcnts);
- if (ret)
- return ret;
- num_ltes_with_bogus_refcnt = 0;
- for_lookup_table_entry(wim->lookup_table, lte_fix_refcnt,
- &num_ltes_with_bogus_refcnt);
- if (num_ltes_with_bogus_refcnt != 0) {
- WARNING("%lu stream(s) had incorrect reference count.",
- num_ltes_with_bogus_refcnt);
- }
- wim->refcnts_ok = 1;
- return 0;
-}
-
static int
append_lte_to_list(struct wim_lookup_table_entry *lte, void *_list)
{
goto out_free_wim;
}
wim->lookup_table = table;
- wim->refcnts_ok = 1;
wim->compression_type = ctype;
wim->out_compression_type = ctype;
wim->chunk_size = wim->hdr.chunk_size;
WIMLIBAPI int
wimlib_set_wim_info(WIMStruct *wim, const struct wimlib_wim_info *info, int which)
{
- int ret;
-
if (which & ~(WIMLIB_CHANGE_READONLY_FLAG |
WIMLIB_CHANGE_GUID |
WIMLIB_CHANGE_BOOT_INDEX |
WIMLIB_CHANGE_RPFIX_FLAG))
return WIMLIB_ERR_INVALID_PARAM;
+ if ((which & WIMLIB_CHANGE_BOOT_INDEX) &&
+ info->boot_index > wim->hdr.image_count)
+ return WIMLIB_ERR_INVALID_IMAGE;
+
if (which & WIMLIB_CHANGE_READONLY_FLAG) {
if (info->is_marked_readonly)
wim->hdr.flags |= WIM_HDR_FLAG_READONLY;
wim->hdr.flags &= ~WIM_HDR_FLAG_READONLY;
}
- if ((which & ~WIMLIB_CHANGE_READONLY_FLAG) == 0)
- return 0;
-
- ret = can_modify_wim(wim);
- if (ret)
- return ret;
-
if (which & WIMLIB_CHANGE_GUID)
memcpy(wim->hdr.guid, info->guid, WIM_GUID_LEN);
- if (which & WIMLIB_CHANGE_BOOT_INDEX) {
- if (info->boot_index > wim->hdr.image_count)
- return WIMLIB_ERR_INVALID_IMAGE;
+ if (which & WIMLIB_CHANGE_BOOT_INDEX)
wim->hdr.boot_idx = info->boot_index;
- }
if (which & WIMLIB_CHANGE_RPFIX_FLAG) {
if (info->has_rpfix)
return 0;
}
-/*
- * can_delete_from_wim - Check if files or images can be deleted from a given
- * WIM file.
- *
- * This theoretically should be exactly the same as can_modify_wim(), but
- * unfortunately, due to bugs in Microsoft's software that generate incorrect
- * reference counts for some WIM resources, we need to run expensive
- * verifications to make sure the reference counts are correct on all WIM
- * resources. Otherwise we might delete a WIM resource whose reference count
- * has fallen to 0, but is actually still referenced somewhere.
- */
-int
-can_delete_from_wim(WIMStruct *wim)
-{
- int ret;
-
- ret = can_modify_wim(wim);
- if (ret)
- return ret;
- if (!wim->refcnts_ok) {
- ret = wim_recalculate_refcnts(wim);
- if (ret)
- return ret;
- }
- return 0;
-}
-
/* API function documented in wimlib.h */
WIMLIBAPI void
wimlib_free(WIMStruct *wim)
{
tchar *p;
int i;
- int ret;
DEBUG("Setting the name of image %d to %"TS, image, name);
- ret = can_modify_wim(wim);
- if (ret)
- return ret;
-
if (name == NULL)
name = T("");
{
tchar *tstr_copy;
tchar **dest_tstr_p;
- int ret;
-
- ret = can_modify_wim(wim);
- if (ret)
- return ret;
if (image < 1 || image > wim->hdr.image_count) {
ERROR("%d is not a valid image", image);