]> wimlib.net Git - wimlib/blobdiff - src/dentry.c
Refactor headers
[wimlib] / src / dentry.c
index b0ebf64dab19a10d81350c10f3f0386eb849aa2c..d77603feeebcf23aecbfc4c02c90d55469839818 100644 (file)
  * wimlib; if not, see http://www.gnu.org/licenses/.
  */
 
-#include "buffer_io.h"
-#include "dentry.h"
-#include "lookup_table.h"
-#include "timestamp.h"
-#include "wimlib_internal.h"
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "wimlib.h"
+#include "wimlib/buffer_io.h"
+#include "wimlib/dentry.h"
+#include "wimlib/encoding.h"
+#include "wimlib/error.h"
+#include "wimlib/lookup_table.h"
+#include "wimlib/metadata.h"
+#include "wimlib/resource.h"
+#include "wimlib/timestamp.h"
+
 #include <errno.h>
 
 /* Calculates the unaligned length, in bytes, of an on-disk WIM dentry that has
@@ -230,13 +239,13 @@ for_dentry_tree_in_rbtree(struct rb_node *node,
        int ret;
        if (node) {
                ret = for_dentry_tree_in_rbtree(node->rb_left, visitor, arg);
-               if (ret != 0)
+               if (ret)
                        return ret;
                ret = for_dentry_in_tree(rbnode_dentry(node), visitor, arg);
-               if (ret != 0)
+               if (ret)
                        return ret;
                ret = for_dentry_tree_in_rbtree(node->rb_right, visitor, arg);
-               if (ret != 0)
+               if (ret)
                        return ret;
        }
        return 0;
@@ -250,13 +259,16 @@ int
 for_dentry_in_tree(struct wim_dentry *root,
                   int (*visitor)(struct wim_dentry*, void*), void *arg)
 {
-       int ret = visitor(root, arg);
-       if (ret == 0) {
-               ret = for_dentry_tree_in_rbtree(root->d_inode->i_children.rb_node,
-                                               visitor,
-                                               arg);
-       }
-       return ret;
+       int ret;
+
+       if (!root)
+               return 0;
+       ret = (*visitor)(root, arg);
+       if (ret)
+               return ret;
+       return for_dentry_tree_in_rbtree(root->d_inode->i_children.rb_node,
+                                        visitor,
+                                        arg);
 }
 
 /* Like for_dentry_in_tree(), but the visitor function is always called on a
@@ -266,11 +278,14 @@ for_dentry_in_tree_depth(struct wim_dentry *root,
                         int (*visitor)(struct wim_dentry*, void*), void *arg)
 {
        int ret;
+
+       if (!root)
+               return 0;
        ret = for_dentry_tree_in_rbtree_depth(root->d_inode->i_children.rb_node,
                                              visitor, arg);
-       if (ret == 0)
-               ret = visitor(root, arg);
-       return ret;
+       if (ret)
+               return ret;
+       return (*visitor)(root, arg);
 }
 
 /* Calculate the full path of @dentry.  The full path of its parent must have
@@ -493,6 +508,10 @@ get_dentry_utf16le(WIMStruct *w, const utf16lechar *path,
        const utf16lechar *p, *pp;
 
        cur_dentry = parent_dentry = wim_root_dentry(w);
+       if (!cur_dentry) {
+               errno = ENOENT;
+               return NULL;
+       }
        p = path;
        while (1) {
                while (*p == cpu_to_le16('/'))
@@ -589,9 +608,6 @@ print_dentry_full_path(struct wim_dentry *dentry, void *_ignore)
        if (ret)
                return ret;
        tprintf(T("%"TS"\n"), dentry->_full_path);
-       FREE(dentry->_full_path);
-       dentry->_full_path = NULL;
-       dentry->full_path_nbytes = 0;
        return 0;
 }
 
@@ -649,7 +665,15 @@ print_dentry(struct wim_dentry *dentry, void *lookup_table)
        wim_timestamp_to_str(inode->i_last_write_time, buf, sizeof(buf));
        tprintf(T("Last Write Time   = %"TS"\n"), buf);
 
-       tprintf(T("Reparse Tag       = 0x%"PRIx32"\n"), inode->i_reparse_tag);
+       if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+               tprintf(T("Reparse Tag       = 0x%"PRIx32"\n"), inode->i_reparse_tag);
+               tprintf(T("Reparse Point Flags = 0x%"PRIx16"\n"),
+                       inode->i_not_rpfixed);
+               tprintf(T("Reparse Point Unknown 2 = 0x%"PRIx32"\n"),
+                       inode->i_rp_unknown_2);
+       }
+       tprintf(T("Reparse Point Unknown 1 = 0x%"PRIx32"\n"),
+               inode->i_rp_unknown_1);
        tprintf(T("Hard Link Group   = 0x%"PRIx64"\n"), inode->i_ino);
        tprintf(T("Hard Link Group Size = %"PRIu32"\n"), inode->i_nlink);
        tprintf(T("Number of Alternate Data Streams = %hu\n"), inode->i_num_ads);
@@ -698,13 +722,15 @@ dentry_common_init(struct wim_dentry *dentry)
 }
 
 struct wim_inode *
-new_timeless_inode()
+new_timeless_inode(void)
 {
        struct wim_inode *inode = CALLOC(1, sizeof(struct wim_inode));
        if (inode) {
                inode->i_security_id = -1;
                inode->i_nlink = 1;
                inode->i_next_stream_id = 1;
+               inode->i_not_rpfixed = 1;
+               INIT_LIST_HEAD(&inode->i_list);
        #ifdef WITH_FUSE
                if (pthread_mutex_init(&inode->i_mutex, NULL) != 0) {
                        ERROR_WITH_ERRNO("Error initializing mutex");
@@ -718,7 +744,7 @@ new_timeless_inode()
 }
 
 static struct wim_inode *
-new_inode()
+new_inode(void)
 {
        struct wim_inode *inode = new_timeless_inode();
        if (inode) {
@@ -792,6 +818,25 @@ new_dentry_with_inode(const tchar *name, struct wim_dentry **dentry_ret)
        return __new_dentry_with_inode(name, dentry_ret, false);
 }
 
+int
+new_filler_directory(const tchar *name, struct wim_dentry **dentry_ret)
+{
+       int ret;
+       struct wim_dentry *dentry;
+
+       DEBUG("Creating filler directory \"%"TS"\"", name);
+       ret = new_dentry_with_inode(name, &dentry);
+       if (ret)
+               goto out;
+       /* Leave the inode number as 0; this is allowed for non
+        * hard-linked files. */
+       dentry->d_inode->i_resolved = 1;
+       dentry->d_inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY;
+       *dentry_ret = dentry;
+       ret = 0;
+out:
+       return ret;
+}
 
 static int
 init_ads_entry(struct wim_ads_entry *ads_entry, const void *name,
@@ -837,9 +882,10 @@ free_inode(struct wim_inode *inode)
                wimlib_assert(inode->i_num_opened_fds == 0);
                FREE(inode->i_fds);
                pthread_mutex_destroy(&inode->i_mutex);
-               if (inode->i_hlist.pprev)
-                       hlist_del(&inode->i_hlist);
        #endif
+               /* HACK: This may instead delete the inode from i_list, but the
+                * hlist_del() behaves the same as list_del(). */
+               hlist_del(&inode->i_hlist);
                FREE(inode->i_extracted_file);
                FREE(inode);
        }
@@ -911,8 +957,7 @@ do_free_dentry(struct wim_dentry *dentry, void *__lookup_table)
 void
 free_dentry_tree(struct wim_dentry *root, struct wim_lookup_table *lookup_table)
 {
-       if (root)
-               for_dentry_in_tree_depth(root, do_free_dentry, lookup_table);
+       for_dentry_in_tree_depth(root, do_free_dentry, lookup_table);
 }
 
 /*
@@ -921,7 +966,7 @@ free_dentry_tree(struct wim_dentry *root, struct wim_lookup_table *lookup_table)
  * @parent: The dentry that will be the parent of @dentry.
  * @dentry: The dentry to link.
  */
-bool
+struct wim_dentry *
 dentry_add_child(struct wim_dentry * restrict parent,
                 struct wim_dentry * restrict child)
 {
@@ -942,12 +987,12 @@ dentry_add_child(struct wim_dentry * restrict parent,
                else if (result > 0)
                        new = &((*new)->rb_right);
                else
-                       return false;
+                       return this;
        }
        child->parent = parent;
        rb_link_node(&child->rb_node, rb_parent, new);
        rb_insert_color(&child->rb_node, root);
-       return true;
+       return NULL;
 }
 
 /* Unlink a WIM dentry from the directory entry tree. */
@@ -1112,6 +1157,43 @@ out:
        return ret;
 }
 
+/* Set the unnamed stream of a WIM inode, given a data buffer containing the
+ * stream contents. */
+int
+inode_set_unnamed_stream(struct wim_inode *inode, const void *data, size_t len,
+                        struct wim_lookup_table *lookup_table)
+{
+       struct wim_lookup_table_entry *lte, *existing_lte;
+       u8 hash[SHA1_HASH_SIZE];
+       void *buf;
+
+       sha1_buffer(data, len, hash);
+       existing_lte = __lookup_resource(lookup_table, hash);
+       if (existing_lte) {
+               wimlib_assert(wim_resource_size(existing_lte) == len);
+               lte = existing_lte;
+               lte->refcnt++;
+       } else {
+               lte = new_lookup_table_entry();
+               if (!lte)
+                       return WIMLIB_ERR_NOMEM;
+               buf = MALLOC(len);
+               if (!buf) {
+                       free_lookup_table_entry(lte);
+                       return WIMLIB_ERR_NOMEM;
+               }
+               memcpy(buf, data, len);
+               lte->resource_location = RESOURCE_IN_ATTACHED_BUFFER;
+               lte->attached_buffer = buf;
+               lte->resource_entry.original_size = len;
+               copy_hash(lte->hash, hash);
+               lookup_table_insert(lookup_table, lte);
+       }
+       inode->i_lte = lte;
+       inode->i_resolved = 1;
+       return 0;
+}
+
 /* Remove an alternate data stream from a WIM inode  */
 void
 inode_remove_ads(struct wim_inode *inode, u16 idx,
@@ -1168,7 +1250,7 @@ inode_get_unix_data(const struct wim_inode *inode,
        if (size != sizeof(struct wimlib_unix_data))
                return BAD_UNIX_DATA;
 
-       ret = read_full_resource_into_buf(lte, unix_data, true);
+       ret = read_full_resource_into_buf(lte, unix_data);
        if (ret)
                return ret;
 
@@ -1211,6 +1293,39 @@ inode_set_unix_data(struct wim_inode *inode, uid_t uid, gid_t gid, mode_t mode,
 }
 #endif /* !__WIN32__ */
 
+/* Replace weird characters in filenames and alternate data stream names.
+ *
+ * In particular we do not want the path separator to appear in any names, as
+ * that would make it possible for a "malicious" WIM to extract itself to any
+ * location it wanted to. */
+static void
+replace_forbidden_characters(utf16lechar *name)
+{
+       utf16lechar *p;
+
+       for (p = name; *p; p++) {
+       #ifdef __WIN32__
+               if (wcschr(L"<>:\"/\\|?*", (wchar_t)*p))
+       #else
+               if (*p == cpu_to_le16('/'))
+       #endif
+               {
+                       if (name) {
+                               WARNING("File, directory, or stream name \"%"WS"\"\n"
+                                       "          contains forbidden characters; "
+                                       "substituting replacement characters.",
+                                       name);
+                               name = NULL;
+                       }
+               #ifdef __WIN32__
+                       *p = cpu_to_le16(0xfffd);
+               #else
+                       *p = cpu_to_le16('?');
+               #endif
+               }
+       }
+}
+
 /*
  * Reads the alternate data stream entries of a WIM dentry.
  *
@@ -1288,7 +1403,7 @@ read_ads_entries(const u8 *p, struct wim_inode *inode, u64 remaining_size)
                }
 
                p = get_u64(p, &length);
-               p += 8; /* Skip the reserved field */
+               p = get_u64(p, &cur_entry->unused);
                p = get_bytes(p, SHA1_HASH_SIZE, cur_entry->hash);
                p = get_u16(p, &cur_entry->stream_name_nbytes);
 
@@ -1338,6 +1453,7 @@ read_ads_entries(const u8 *p, struct wim_inode *inode, u64 remaining_size)
                        get_bytes(p, cur_entry->stream_name_nbytes,
                                  cur_entry->stream_name);
                        cur_entry->stream_name[cur_entry->stream_name_nbytes / 2] = 0;
+                       replace_forbidden_characters(cur_entry->stream_name);
                }
                /* 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
@@ -1444,10 +1560,8 @@ read_dentry(const u8 metadata_resource[], u64 metadata_resource_len,
        p = get_u32(p, (u32*)&inode->i_security_id);
        p = get_u64(p, &dentry->subdir_offset);
 
-       /* 2 unused fields */
-       p += 2 * sizeof(u64);
-       /*p = get_u64(p, &dentry->unused1);*/
-       /*p = get_u64(p, &dentry->unused2);*/
+       p = get_u64(p, &inode->i_unused_1);
+       p = get_u64(p, &inode->i_unused_2);
 
        p = get_u64(p, &inode->i_creation_time);
        p = get_u64(p, &inode->i_last_access_time);
@@ -1459,12 +1573,12 @@ read_dentry(const u8 metadata_resource[], u64 metadata_resource_len,
         * reparse points, then put the fields in the same place and didn't
         * document it.  */
        if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
-               p += 4;
+               p = get_u32(p, &inode->i_rp_unknown_1);
                p = get_u32(p, &inode->i_reparse_tag);
-               p += 4;
+               p = get_u16(p, &inode->i_rp_unknown_2);
+               p = get_u16(p, &inode->i_not_rpfixed);
        } else {
-               p += 4;
-               /* i_reparse_tag is irrelevant; just leave it at 0. */
+               p = get_u32(p, &inode->i_rp_unknown_1);
                p = get_u64(p, &inode->i_ino);
        }
 
@@ -1511,6 +1625,7 @@ read_dentry(const u8 metadata_resource[], u64 metadata_resource_len,
                        WARNING("File name in WIM dentry \"%"WS"\" is not "
                                "null-terminated!", file_name);
                }
+               replace_forbidden_characters(file_name);
        }
 
        /* Align the calculated size */
@@ -1561,6 +1676,7 @@ read_dentry(const u8 metadata_resource[], u64 metadata_resource_len,
                        WARNING("Short name in WIM dentry \"%"WS"\" is not "
                                "null-terminated!", file_name);
                }
+               replace_forbidden_characters(short_name);
        }
 
        /*
@@ -1723,20 +1839,21 @@ write_dentry(const struct wim_dentry *dentry, u8 *p)
        p = put_u32(p, inode->i_attributes);
        p = put_u32(p, inode->i_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->i_unused_1);
+       p = put_u64(p, inode->i_unused_2);
        p = put_u64(p, inode->i_creation_time);
        p = put_u64(p, inode->i_last_access_time);
        p = put_u64(p, inode->i_last_write_time);
        hash = inode_stream_hash(inode, 0);
        p = put_bytes(p, SHA1_HASH_SIZE, hash);
        if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
-               p = put_zeroes(p, 4);
+               p = put_u32(p, inode->i_rp_unknown_1);
                p = put_u32(p, inode->i_reparse_tag);
-               p = put_zeroes(p, 4);
+               p = put_u16(p, inode->i_rp_unknown_2);
+               p = put_u16(p, inode->i_not_rpfixed);
        } else {
                u64 link_group_id;
-               p = put_u32(p, 0);
+               p = put_u32(p, inode->i_rp_unknown_1);
                if (inode->i_nlink == 1)
                        link_group_id = 0;
                else
@@ -1764,7 +1881,7 @@ write_dentry(const struct wim_dentry *dentry, u8 *p)
         * alternate data stream entries. */
        for (u16 i = 0; i < inode->i_num_ads; i++) {
                p = put_u64(p, ads_entry_total_length(&inode->i_ads_entries[i]));
-               p = put_u64(p, 0); /* Unused */
+               p = put_u64(p, inode->i_ads_entries[i].unused);
                hash = inode_stream_hash(inode, i + 1);
                p = put_bytes(p, SHA1_HASH_SIZE, hash);
                p = put_u16(p, inode->i_ads_entries[i].stream_name_nbytes);