- if (unix_data->version != 0)
- return BAD_UNIX_DATA;
- return 0;
-}
-
-int
-inode_set_unix_data(struct wim_inode *inode, uid_t uid, gid_t gid, mode_t mode,
- struct wim_lookup_table *lookup_table, int which)
-{
- struct wimlib_unix_data unix_data;
- int ret;
- bool have_good_unix_data = false;
- bool have_unix_data = false;
- u16 stream_idx;
-
- if (!(which & UNIX_DATA_CREATE)) {
- ret = inode_get_unix_data(inode, &unix_data, &stream_idx);
- if (ret == 0 || ret == BAD_UNIX_DATA || ret > 0)
- have_unix_data = true;
- if (ret == 0)
- have_good_unix_data = true;
- }
- unix_data.version = 0;
- if (which & UNIX_DATA_UID || !have_good_unix_data)
- unix_data.uid = uid;
- if (which & UNIX_DATA_GID || !have_good_unix_data)
- unix_data.gid = gid;
- if (which & UNIX_DATA_MODE || !have_good_unix_data)
- unix_data.mode = mode;
- ret = inode_add_ads_with_data(inode, WIMLIB_UNIX_DATA_TAG,
- &unix_data,
- sizeof(struct wimlib_unix_data),
- lookup_table);
- if (ret == 0 && have_unix_data)
- inode_remove_ads(inode, stream_idx, lookup_table);
- return ret;
-}
-#endif /* !__WIN32__ */
-
-/*
- * Reads the alternate data stream entries of a WIM dentry.
- *
- * @p:
- * Pointer to buffer that starts with the first alternate stream entry.
- *
- * @inode:
- * Inode to load the alternate data streams into. @inode->i_num_ads must
- * have been set to the number of alternate data streams that are expected.
- *
- * @remaining_size:
- * Number of bytes of data remaining in the buffer pointed to by @p.
- *
- * On success, inode->i_ads_entries is set to an array of `struct
- * wim_ads_entry's of length inode->i_num_ads. On failure, @inode is not
- * modified.
- *
- * Return values:
- * WIMLIB_ERR_SUCCESS (0)
- * WIMLIB_ERR_INVALID_METADATA_RESOURCE
- * WIMLIB_ERR_NOMEM
- */
-static int
-read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode,
- size_t nbytes_remaining)
-{
- u16 num_ads;
- struct wim_ads_entry *ads_entries;
- int ret;
-
- BUILD_BUG_ON(sizeof(struct wim_ads_entry_on_disk) != WIM_ADS_ENTRY_DISK_SIZE);
-
- /* Allocate an array for our in-memory representation of the alternate
- * data stream entries. */
- num_ads = inode->i_num_ads;
- ads_entries = CALLOC(num_ads, sizeof(inode->i_ads_entries[0]));
- if (ads_entries == NULL)
- goto out_of_memory;
-
- /* Read the entries into our newly allocated buffer. */
- for (u16 i = 0; i < num_ads; i++) {
- u64 length;
- struct wim_ads_entry *cur_entry;
- const struct wim_ads_entry_on_disk *disk_entry =
- (const struct wim_ads_entry_on_disk*)p;
-
- cur_entry = &ads_entries[i];
- ads_entries[i].stream_id = i + 1;
-
- /* Do we have at least the size of the fixed-length data we know
- * need? */
- if (nbytes_remaining < sizeof(struct wim_ads_entry_on_disk))
- goto out_invalid;
-
- /* Read the length field */
- length = le64_to_cpu(disk_entry->length);
-
- /* Make sure the length field is neither so small it doesn't
- * include all the fixed-length data nor so large it overflows
- * the metadata resource buffer. */
- if (length < sizeof(struct wim_ads_entry_on_disk) ||
- length > nbytes_remaining)
- goto out_invalid;
-
- /* Read the rest of the fixed-length data. */
-
- cur_entry->reserved = le64_to_cpu(disk_entry->reserved);
- copy_hash(cur_entry->hash, disk_entry->hash);
- cur_entry->stream_name_nbytes = le16_to_cpu(disk_entry->stream_name_nbytes);
-
- /* If stream_name_nbytes != 0, this is a named stream.
- * Otherwise this is an unnamed stream, or in some cases (bugs
- * in Microsoft's software I guess) a meaningless entry
- * distinguished from the real unnamed stream entry, if any, by
- * the fact that the real unnamed stream entry has a nonzero
- * hash field. */
- if (cur_entry->stream_name_nbytes) {
- /* The name is encoded in UTF16-LE, which uses 2-byte
- * coding units, so the length of the name had better be
- * an even number of bytes... */
- if (cur_entry->stream_name_nbytes & 1)
- goto out_invalid;
-
- /* Add the length of the stream name to get the length
- * we actually need to read. Make sure this isn't more
- * than the specified length of the entry. */
- if (sizeof(struct wim_ads_entry_on_disk) +
- cur_entry->stream_name_nbytes > length)
- goto out_invalid;
-
- cur_entry->stream_name = MALLOC(cur_entry->stream_name_nbytes + 2);
- if (cur_entry->stream_name == NULL)
- goto out_of_memory;
-
- memcpy(cur_entry->stream_name,
- disk_entry->stream_name,
- cur_entry->stream_name_nbytes);
- cur_entry->stream_name[cur_entry->stream_name_nbytes / 2] = cpu_to_le16(0);
- } else {
- /* Mark inode as having weird stream entries. */
- inode->i_canonical_streams = 0;
- }
-
- /* It's expected that the size of every ADS entry is a multiple
- * of 8. However, to be safe, I'm allowing the possibility of
- * an ADS entry at the very end of the metadata resource ending
- * un-aligned. So although we still need to increment the input
- * pointer by @length to reach the next ADS entry, it's possible
- * that less than @length is actually remaining in the metadata
- * resource. We should set the remaining bytes to 0 if this
- * happens. */
- length = (length + 7) & ~(u64)7;
- p += length;
- if (nbytes_remaining < length)
- nbytes_remaining = 0;
- else
- nbytes_remaining -= length;
- }
- inode->i_ads_entries = ads_entries;
- inode->i_next_stream_id = inode->i_num_ads + 1;
- ret = 0;
- goto out;
-out_of_memory:
- ret = WIMLIB_ERR_NOMEM;
- goto out_free_ads_entries;
-out_invalid:
- ERROR("An alternate data stream entry is invalid");
- ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
-out_free_ads_entries:
- if (ads_entries) {
- for (u16 i = 0; i < num_ads; i++)
- destroy_ads_entry(&ads_entries[i]);
- FREE(ads_entries);
- }
-out:
- return ret;
-}
-
-/*
- * Reads a WIM directory entry, including all alternate data stream entries that
- * follow it, from the WIM image's metadata resource.
- *
- * @metadata_resource:
- * Pointer to the metadata resource buffer.
- *
- * @metadata_resource_len:
- * Length of the metadata resource buffer, in bytes.
- *
- * @offset: Offset of the dentry within the metadata resource.
- *
- * @dentry: A `struct wim_dentry' that will be filled in by this function.
- *
- * Return 0 on success or nonzero on failure. On failure, @dentry will have
- * been modified, but it will not be left with pointers to any allocated
- * buffers. On success, the dentry->length field must be examined. If zero,
- * this was a special "end of directory" dentry and not a real dentry. If
- * nonzero, this was a real dentry.
- *
- * Return values:
- * WIMLIB_ERR_SUCCESS (0)
- * WIMLIB_ERR_INVALID_METADATA_RESOURCE
- * WIMLIB_ERR_NOMEM
- */
-int
-read_dentry(const u8 * restrict metadata_resource, u64 metadata_resource_len,
- u64 offset, struct wim_dentry * restrict dentry)
-{
-
- u64 calculated_size;
- utf16lechar *file_name;
- utf16lechar *short_name;
- u16 short_name_nbytes;
- u16 file_name_nbytes;
- int ret;
- struct wim_inode *inode;
- const u8 *p = &metadata_resource[offset];
- const struct wim_dentry_on_disk *disk_dentry =
- (const struct wim_dentry_on_disk*)p;
-
- BUILD_BUG_ON(sizeof(struct wim_dentry_on_disk) != WIM_DENTRY_DISK_SIZE);
-
- if ((uintptr_t)p & 7)
- WARNING("WIM dentry is not 8-byte aligned");
-
- dentry_common_init(dentry);
-
- /* Before reading the whole dentry, we need to read just the length.
- * This is because a dentry of length 8 (that is, just the length field)
- * terminates the list of sibling directory entries. */
- if (offset + sizeof(u64) > metadata_resource_len ||
- offset + sizeof(u64) < offset)
- {
- ERROR("Directory entry starting at %"PRIu64" ends past the "
- "end of the metadata resource (size %"PRIu64")",
- offset, metadata_resource_len);
- return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
- }
- dentry->length = le64_to_cpu(disk_dentry->length);
-
- /* A zero length field (really a length of 8, since that's how big the
- * directory entry is...) indicates that this is the end of directory
- * dentry. We do not read it into memory as an actual dentry, so just
- * return successfully in this case. */
- if (dentry->length == 8)
- dentry->length = 0;
- if (dentry->length == 0)
- return 0;
-
- /* Now that we have the actual length provided in the on-disk structure,
- * again make sure it doesn't overflow the metadata resource buffer. */
- if (offset + dentry->length > metadata_resource_len ||
- offset + dentry->length < offset)
- {
- ERROR("Directory entry at offset %"PRIu64" and with size "
- "%"PRIu64" ends past the end of the metadata resource "
- "(size %"PRIu64")",
- offset, dentry->length, metadata_resource_len);
- return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
- }
-
- /* Make sure the dentry length is at least as large as the number of
- * fixed-length fields */
- if (dentry->length < sizeof(struct wim_dentry_on_disk)) {
- ERROR("Directory entry has invalid length of %"PRIu64" bytes",
- dentry->length);
- return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
- }
-
- /* Allocate a `struct wim_inode' for this `struct wim_dentry'. */
- inode = new_timeless_inode();
- if (inode == NULL)
- return WIMLIB_ERR_NOMEM;
-
- /* Read more fields; some into the dentry, and some into the inode. */