+
+/*
+ * Writes a WIM dentry to an output buffer.
+ *
+ * @dentry: The dentry structure.
+ * @p: The memory location to write the data to.
+ * @return: Pointer to the byte after the last byte we wrote as part of the
+ * dentry.
+ */
+static u8 *write_dentry(const struct dentry *dentry, u8 *p)
+{
+ u8 *orig_p = p;
+ const u8 *hash;
+ const struct inode *inode = dentry->d_inode;
+
+ /* We calculate the correct length of the dentry ourselves because the
+ * dentry->length field may been set to an unexpected value from when we
+ * read the dentry in (for example, there may have been unknown data
+ * appended to the end of the dentry...) */
+ u64 length = dentry_correct_length(dentry);
+
+ p = put_u64(p, length);
+ p = put_u32(p, inode->attributes);
+ p = put_u32(p, inode->security_id);
+ p = put_u64(p, dentry->subdir_offset);
+ p = put_u64(p, 0); /* unused1 */
+ p = put_u64(p, 0); /* unused2 */
+ p = put_u64(p, inode->creation_time);
+ p = put_u64(p, inode->last_access_time);
+ p = put_u64(p, inode->last_write_time);
+ hash = inode_stream_hash(inode, 0);
+ p = put_bytes(p, SHA1_HASH_SIZE, hash);
+ if (inode->attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+ p = put_zeroes(p, 4);
+ p = put_u32(p, inode->reparse_tag);
+ p = put_zeroes(p, 4);
+ } else {
+ u64 link_group_id;
+ p = put_u32(p, 0);
+ if (inode->link_count == 1)
+ link_group_id = 0;
+ else
+ link_group_id = inode->ino;
+ p = put_u64(p, link_group_id);
+ }
+ p = put_u16(p, inode->num_ads);
+ p = put_u16(p, dentry->short_name_len);
+ p = put_u16(p, dentry->file_name_len);
+ if (dentry->file_name_len) {
+ p = put_bytes(p, dentry->file_name_len, (u8*)dentry->file_name);
+ p = put_u16(p, 0); /* filename padding, 2 bytes. */
+ }
+ if (dentry->short_name) {
+ p = put_bytes(p, dentry->short_name_len, (u8*)dentry->short_name);
+ p = put_u16(p, 0); /* short name padding, 2 bytes */
+ }
+
+ /* Align to 8-byte boundary */
+ wimlib_assert(length >= (p - orig_p) && length - (p - orig_p) <= 7);
+ p = put_zeroes(p, length - (p - orig_p));
+
+ /* Write the alternate data streams, if there are any. Please see
+ * read_ads_entries() for comments about the format of the on-disk
+ * alternate data stream entries. */
+ for (u16 i = 0; i < inode->num_ads; i++) {
+ p = put_u64(p, ads_entry_total_length(&inode->ads_entries[i]));
+ p = put_u64(p, 0); /* Unused */
+ hash = inode_stream_hash(inode, i + 1);
+ p = put_bytes(p, SHA1_HASH_SIZE, hash);
+ p = put_u16(p, inode->ads_entries[i].stream_name_len);
+ if (inode->ads_entries[i].stream_name_len) {
+ p = put_bytes(p, inode->ads_entries[i].stream_name_len,
+ (u8*)inode->ads_entries[i].stream_name);
+ p = put_u16(p, 0);
+ }
+ p = put_zeroes(p, (8 - (p - orig_p) % 8) % 8);
+ }
+ wimlib_assert(p - orig_p == __dentry_total_length(dentry, length));
+ return p;
+}
+
+/* Recursive function that writes a dentry tree rooted at @parent, not including
+ * @parent itself, which has already been written. */
+static u8 *write_dentry_tree_recursive(const struct dentry *parent, u8 *p)
+{
+ const struct dentry *child, *children;
+
+ /* Nothing to do if this dentry has no children. */
+ if (parent->subdir_offset == 0)
+ return p;
+
+ /* Write child dentries and end-of-directory entry.
+ *
+ * Note: we need to write all of this dentry's children before
+ * recursively writing the directory trees rooted at each of the child
+ * dentries, since the on-disk dentries for a dentry's children are
+ * always located at consecutive positions in the metadata resource! */
+ children = parent->d_inode->children;
+ child = children;
+ if (child) {
+ do {
+ p = write_dentry(child, p);
+ child = child->next;
+ } while (child != children);
+ }
+
+ /* write end of directory entry */
+ p = put_u64(p, 0);
+
+ /* Recurse on children. */
+ if (child) {
+ do {
+ p = write_dentry_tree_recursive(child, p);
+ child = child->next;
+ } while (child != children);
+ }
+ return p;
+}
+
+/* Writes a directory tree to the metadata resource.
+ *
+ * @root: Root of the dentry tree.
+ * @p: Pointer to a buffer with enough space for the dentry tree.
+ *
+ * Returns pointer to the byte after the last byte we wrote.
+ */
+u8 *write_dentry_tree(const struct dentry *root, u8 *p)
+{
+ DEBUG("Writing dentry tree.");
+ wimlib_assert(dentry_is_root(root));
+
+ /* If we're the root dentry, we have no parent that already
+ * wrote us, so we need to write ourselves. */
+ p = write_dentry(root, p);
+
+ /* Write end of directory entry after the root dentry just to be safe;
+ * however the root dentry obviously cannot have any siblings. */
+ p = put_u64(p, 0);
+
+ /* Recursively write the rest of the dentry tree. */
+ return write_dentry_tree_recursive(root, p);
+}
+