+ lte_decrement_num_opened_fds(lte, w->lookup_table);
+ return 0;
+}
+
+static int close_wimlib_fd(struct wimlib_fd *fd)
+{
+ int ret;
+ DEBUG("Closing fd (inode = %lu, opened = %u, allocated = %u)",
+ fd->inode->ino, fd->inode->num_opened_fds,
+ fd->inode->num_allocated_fds);
+ ret = lte_put_fd(fd->lte, fd);
+ if (ret != 0)
+ return ret;
+
+ inode_put_fd(fd->inode, fd);
+ return 0;
+}
+
+/* Remove a dentry; i.e. remove a reference to the inode.
+ *
+ * If there are no remaining references to the inode either through detnries or
+ * open file descriptors, the inode is freed.
+ *
+ * All lookup table entries referenced by the inode have their reference count
+ * decremented. If a lookup table entry has no open file descriptors and no
+ * references remaining, it is freed, and the staging file is unlinked.
+ *
+ * Otherwise, the inode is not removed, but the dentry is unlinked and freed. */
+static void remove_dentry(struct dentry *dentry,
+ struct lookup_table *lookup_table)
+{
+ struct inode *inode = dentry->inode;
+ struct lookup_table_entry *lte;
+ unsigned i;
+
+ for (i = 0; i <= inode->num_ads; i++) {
+ lte = inode_stream_lte_resolved(inode, i);
+ if (lte)
+ lte_decrement_refcnt(lte, lookup_table);
+ }
+ unlink_dentry(dentry);
+ put_dentry(dentry);
+}
+
+/* Remove an alternate data stream from the inode */
+static void inode_remove_ads(struct inode *inode, u16 idx,
+ struct lookup_table *lookup_table)
+{
+ struct ads_entry *ads_entry;
+ struct lookup_table_entry *lte;
+
+ ads_entry = inode->ads_entries[idx];
+
+ wimlib_assert(ads_entry);
+
+ lte = ads_entry->lte;
+
+ if (lte)
+ lte_decrement_refcnt(lte, lookup_table);
+
+ free_ads_entry(ads_entry);
+
+ wimlib_assert(inode->num_ads);
+ memcpy(&inode->ads_entries[idx],
+ &inode->ads_entries[idx + 1],
+ (inode->num_ads - idx - 1) * sizeof(inode->ads_entries[0]));
+ inode->num_ads--;
+}
+
+/* Transfers file attributes from a struct inode to a `stat' buffer.
+ *
+ * The lookup table entry tells us which stream in the inode we are statting.
+ * For a named data stream, everything returned is the same as the unnamed data
+ * stream except possibly the size and block count. */
+int inode_to_stbuf(const struct inode *inode, struct lookup_table_entry *lte,
+ struct stat *stbuf)
+{
+ if (inode_is_symlink(inode))
+ stbuf->st_mode = S_IFLNK | 0777;
+ else if (inode_is_directory(inode))
+ stbuf->st_mode = S_IFDIR | 0755;
+ else
+ stbuf->st_mode = S_IFREG | 0644;
+
+ stbuf->st_ino = (ino_t)inode->ino;
+ stbuf->st_nlink = inode->link_count;
+ stbuf->st_uid = getuid();
+ stbuf->st_gid = getgid();
+
+ if (lte) {
+ if (lte->resource_location == RESOURCE_IN_STAGING_FILE) {
+ wimlib_assert(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE);
+ wimlib_assert(lte->staging_file_name);
+ struct stat native_stat;
+ if (stat(lte->staging_file_name, &native_stat) != 0) {
+ DEBUG("Failed to stat `%s': %m",
+ lte->staging_file_name);
+ return -errno;
+ }
+ stbuf->st_size = native_stat.st_size;
+ } else {
+ stbuf->st_size = lte->resource_entry.original_size;
+ }
+ } else {
+ stbuf->st_size = 0;
+ }
+
+ stbuf->st_atime = wim_timestamp_to_unix(inode->last_access_time);
+ stbuf->st_mtime = wim_timestamp_to_unix(inode->last_write_time);
+ stbuf->st_ctime = wim_timestamp_to_unix(inode->creation_time);
+ stbuf->st_blocks = (stbuf->st_size + 511) / 512;