+/* Load a file's extended attributes into the corresponding WIM inode. */
+static noinline_for_stack int
+winnt_load_xattrs(HANDLE h, struct wim_inode *inode,
+ struct winnt_scan_ctx *ctx, u32 ea_size)
+{
+ IO_STATUS_BLOCK iosb;
+ NTSTATUS status;
+ u8 _buf[1024] _aligned_attribute(4);
+ u8 *buf = _buf;
+ const FILE_FULL_EA_INFORMATION *ea;
+ struct wim_xattr_entry *entry;
+ int ret;
+
+
+ /*
+ * EaSize from FILE_EA_INFORMATION is apparently supposed to give the
+ * size of the buffer required for NtQueryEaFile(), but it doesn't
+ * actually work correctly; it can be off by about 4 bytes per xattr.
+ *
+ * So just start out by doubling the advertised size, and also handle
+ * STATUS_BUFFER_OVERFLOW just in case.
+ */
+retry:
+ if (unlikely(ea_size * 2 < ea_size))
+ ea_size = UINT32_MAX;
+ else
+ ea_size *= 2;
+ if (unlikely(ea_size > sizeof(_buf))) {
+ buf = MALLOC(ea_size);
+ if (!buf) {
+ if (ea_size >= (1 << 20)) {
+ WARNING("\"%ls\": EaSize was extremely large (%u)",
+ printable_path(ctx), ea_size);
+ }
+ return WIMLIB_ERR_NOMEM;
+ }
+ }
+
+ status = NtQueryEaFile(h, &iosb, buf, ea_size,
+ FALSE, NULL, 0, NULL, TRUE);
+
+ if (unlikely(!NT_SUCCESS(status))) {
+ if (status == STATUS_BUFFER_OVERFLOW) {
+ if (buf != _buf) {
+ FREE(buf);
+ buf = NULL;
+ }
+ goto retry;
+ }
+ if (status == STATUS_NO_EAS_ON_FILE) {
+ /*
+ * FILE_EA_INFORMATION.EaSize was nonzero so this
+ * shouldn't happen, but just in case...
+ */
+ ret = 0;
+ goto out;
+ }
+ winnt_error(status, L"\"%ls\": Can't read extended attributes",
+ printable_path(ctx));
+ ret = WIMLIB_ERR_STAT;
+ goto out;
+ }
+
+ ea = (const FILE_FULL_EA_INFORMATION *)buf;
+ entry = (struct wim_xattr_entry *)buf;
+ for (;;) {
+ /*
+ * wim_xattr_entry is not larger than FILE_FULL_EA_INFORMATION,
+ * so we can reuse the same buffer by overwriting the
+ * FILE_FULL_EA_INFORMATION with the wim_xattr_entry in-place.
+ */
+ FILE_FULL_EA_INFORMATION _ea;
+
+ STATIC_ASSERT(offsetof(struct wim_xattr_entry, name) <=
+ offsetof(FILE_FULL_EA_INFORMATION, EaName));
+ wimlib_assert((u8 *)entry <= (const u8 *)ea);
+
+ memcpy(&_ea, ea, sizeof(_ea));
+
+ entry->value_len = cpu_to_le16(_ea.EaValueLength);
+ entry->name_len = _ea.EaNameLength;
+ entry->flags = _ea.Flags;
+ memmove(entry->name, ea->EaName, _ea.EaNameLength);
+ entry->name[_ea.EaNameLength] = '\0';
+ memmove(&entry->name[_ea.EaNameLength + 1],
+ &ea->EaName[_ea.EaNameLength + 1], _ea.EaValueLength);
+ entry = (struct wim_xattr_entry *)
+ &entry->name[_ea.EaNameLength + 1 + _ea.EaValueLength];
+ if (_ea.NextEntryOffset == 0)
+ break;
+ ea = (const FILE_FULL_EA_INFORMATION *)
+ ((const u8 *)ea + _ea.NextEntryOffset);
+ }
+ wimlib_assert((u8 *)entry - buf <= ea_size);
+
+ ret = WIMLIB_ERR_NOMEM;
+ if (!inode_set_xattrs(inode, buf, (u8 *)entry - buf))
+ goto out;
+ ret = 0;
+out:
+ if (unlikely(buf != _buf))
+ FREE(buf);
+ return ret;
+}
+