-#ifndef __WIN32__
-int
-inode_get_unix_data(const struct wim_inode *inode,
- struct wimlib_unix_data *unix_data,
- u16 *stream_idx_ret)
-{
- const struct wim_ads_entry *ads_entry;
- const struct wim_lookup_table_entry *lte;
- size_t size;
- int ret;
-
- wimlib_assert(inode->i_resolved);
-
- ads_entry = inode_get_ads_entry((struct wim_inode*)inode,
- WIMLIB_UNIX_DATA_TAG, NULL);
- if (!ads_entry)
- return NO_UNIX_DATA;
-
- if (stream_idx_ret)
- *stream_idx_ret = ads_entry - inode->i_ads_entries;
-
- lte = ads_entry->lte;
- if (!lte)
- return NO_UNIX_DATA;
-
- size = wim_resource_size(lte);
- if (size != sizeof(struct wimlib_unix_data))
- return BAD_UNIX_DATA;
-
- ret = read_full_resource_into_buf(lte, unix_data);
- if (ret)
- return ret;
-
- 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)
- 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)
- 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);
- }
-
- /* 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)
- return WIMLIB_ERR_NOMEM;
-
- /* Read more fields; some into the dentry, and some into the inode. */
-
- inode->i_attributes = le32_to_cpu(disk_dentry->attributes);
- inode->i_security_id = le32_to_cpu(disk_dentry->security_id);
- dentry->subdir_offset = le64_to_cpu(disk_dentry->subdir_offset);
- dentry->d_unused_1 = le64_to_cpu(disk_dentry->unused_1);
- dentry->d_unused_2 = le64_to_cpu(disk_dentry->unused_2);
- inode->i_creation_time = le64_to_cpu(disk_dentry->creation_time);
- inode->i_last_access_time = le64_to_cpu(disk_dentry->last_access_time);
- inode->i_last_write_time = le64_to_cpu(disk_dentry->last_write_time);
- copy_hash(inode->i_hash, disk_dentry->unnamed_stream_hash);
-
- /* I don't know what's going on here. It seems like M$ screwed up the
- * reparse points, then put the fields in the same place and didn't
- * document it. So we have some fields we read for reparse points, and
- * some fields in the same place for non-reparse-point.s */
- if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
- inode->i_rp_unknown_1 = le32_to_cpu(disk_dentry->reparse.rp_unknown_1);
- inode->i_reparse_tag = le32_to_cpu(disk_dentry->reparse.reparse_tag);
- inode->i_rp_unknown_2 = le16_to_cpu(disk_dentry->reparse.rp_unknown_2);
- inode->i_not_rpfixed = le16_to_cpu(disk_dentry->reparse.not_rpfixed);
- /* Leave inode->i_ino at 0. Note that this means the WIM file
- * cannot archive hard-linked reparse points. Such a thing
- * doesn't really make sense anyway, although I believe it's
- * theoretically possible to have them on NTFS. */
- } else {
- inode->i_rp_unknown_1 = le32_to_cpu(disk_dentry->nonreparse.rp_unknown_1);
- inode->i_ino = le64_to_cpu(disk_dentry->nonreparse.hard_link_group_id);
- }
-
- inode->i_num_ads = le16_to_cpu(disk_dentry->num_alternate_data_streams);
-
- short_name_nbytes = le16_to_cpu(disk_dentry->short_name_nbytes);
- file_name_nbytes = le16_to_cpu(disk_dentry->file_name_nbytes);
-
- if ((short_name_nbytes & 1) | (file_name_nbytes & 1))
- {
- ERROR("Dentry name is not valid UTF-16LE (odd number of bytes)!");
- ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
- goto out_free_inode;
- }
-
- /* We now know the length of the file name and short name. Make sure
- * the length of the dentry is large enough to actually hold them.
- *
- * The calculated length here is unaligned to allow for the possibility
- * that the dentry->length names an unaligned length, although this
- * would be unexpected. */
- calculated_size = _dentry_correct_length_unaligned(file_name_nbytes,
- short_name_nbytes);
-
- if (dentry->length < calculated_size) {