+#include "wimlib/unix_data.h"
+#include "wimlib/xattr.h"
+
+#ifdef HAVE_FDOPENDIR
+# define my_fdopendir(dirfd_p) fdopendir(*(dirfd_p))
+#else
+static DIR *
+my_fdopendir(int *dirfd_p)
+{
+ DIR *dir = NULL;
+ int old_pwd;
+
+ old_pwd = open(".", O_RDONLY);
+ if (old_pwd >= 0) {
+ if (!fchdir(*dirfd_p)) {
+ dir = opendir(".");
+ if (dir) {
+ close(*dirfd_p);
+ *dirfd_p = dirfd(dir);
+ }
+ fchdir(old_pwd);
+ }
+ close(old_pwd);
+ }
+ return dir;
+}
+#endif
+
+#ifdef HAVE_OPENAT
+# define my_openat(full_path, dirfd, relpath, flags) \
+ openat((dirfd), (relpath), (flags))
+#else
+# define my_openat(full_path, dirfd, relpath, flags) \
+ open((full_path), (flags))
+#endif
+
+#ifdef HAVE_READLINKAT
+# define my_readlinkat(full_path, dirfd, relpath, buf, bufsize) \
+ readlinkat((dirfd), (relpath), (buf), (bufsize))
+#else
+# define my_readlinkat(full_path, dirfd, relpath, buf, bufsize) \
+ readlink((full_path), (buf), (bufsize))
+#endif
+
+#ifdef HAVE_FSTATAT
+# define my_fstatat(full_path, dirfd, relpath, stbuf, flags) \
+ fstatat((dirfd), (relpath), (stbuf), (flags))
+#else
+# define my_fstatat(full_path, dirfd, relpath, stbuf, flags) \
+ ((flags) & AT_SYMLINK_NOFOLLOW) ? \
+ lstat((full_path), (stbuf)) : \
+ stat((full_path), (stbuf))
+#endif
+
+#ifndef AT_FDCWD
+# define AT_FDCWD -100
+#endif
+
+#ifndef AT_SYMLINK_NOFOLLOW
+# define AT_SYMLINK_NOFOLLOW 0x100
+#endif
+
+#ifdef HAVE_XATTR_SUPPORT
+/*
+ * Retrieves the values of the xattrs named by the null-terminated @names of the
+ * file at @path and serializes the xattr names and values into @entries. If
+ * successful, returns the number of bytes used in @entries. If unsuccessful,
+ * returns -1 and sets errno (ERANGE if @entries was too small).
+ */
+static ssize_t
+gather_xattr_entries(const char *path, const char *names, size_t names_size,
+ void *entries, size_t entries_size)
+{
+ const char * const names_end = names + names_size;
+ void * const entries_end = entries + entries_size;
+ const char *name = names;
+ struct wimlib_xattr_entry *entry = entries;
+
+ wimlib_assert((uintptr_t)entries % 4 == 0 &&
+ entries_size % 4 == 0 && names_size != 0);
+ do {
+ size_t name_len = strnlen(name, names_end - name);
+ void *value;
+ ssize_t value_len;
+
+ if (name_len == 0 || name_len >= names_end - name ||
+ (u16)name_len != name_len) {
+ ERROR("\"%s\": malformed extended attribute names list",
+ path);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * Note: we take care to always call lgetxattr() with a nonzero
+ * size, since zero size means to return the value length only.
+ */
+ if (entries_end - (void *)entry <= sizeof(*entry) + name_len) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ entry->name_len = cpu_to_le16(name_len);
+ entry->reserved = 0;
+ value = mempcpy(entry->name, name, name_len);
+
+ value_len = lgetxattr(path, name, value, entries_end - value);
+ if (value_len < 0) {
+ if (errno != ERANGE) {
+ ERROR_WITH_ERRNO("\"%s\": unable to read extended attribute \"%s\"",
+ path, name);
+ }
+ return -1;
+ }
+ if ((u32)value_len != value_len) {
+ ERROR("\"%s\": value of extended attribute \"%s\" is too large",
+ path, name);
+ errno = EINVAL;
+ return -1;
+ }
+ entry->value_len = cpu_to_le32(value_len);
+
+ /*
+ * Zero-pad the entry to the next 4-byte boundary.
+ * Note: because we've guaranteed that @entries_size is a
+ * multiple of 4, this cannot overflow the @entries buffer.
+ */
+ value += value_len;
+ while ((uintptr_t)value & 3) {
+ *(u8 *)value = 0;
+ value++;
+ }
+
+ entry = value;
+ name += name_len + 1;
+ } while (name < names_end);
+
+ return (void *)entry - entries;
+}