X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fdentry.c;h=324fbcf8a63b4f72dba20625fa927d6cbcaf48c4;hp=9c32bcb68130bc2459dbc0ad8bbab0d740e80f45;hb=65adcad3e3d5eb01aaaf0dab9d3e509364dac673;hpb=e2697bba43598a3ff2e2d66c8596df7acba1acb6 diff --git a/src/dentry.c b/src/dentry.c index 9c32bcb6..324fbcf8 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -1,50 +1,72 @@ /* - * dentry.c - * - * In the WIM file format, the dentries are stored in the "metadata resource" - * section right after the security data. Each image in the WIM file has its - * own metadata resource with its own security data and dentry tree. Dentries - * in different images may share file resources by referring to the same lookup - * table entries. + * dentry.c - see description below */ /* * Copyright (C) 2012, 2013, 2014 Eric Biggers * - * This file is part of wimlib, a library for working with WIM files. + * This file is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) any + * later version. + * + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this file; if not, see http://www.gnu.org/licenses/. + */ + +/* + * This file contains logic to deal with WIM directory entries, or "dentries": + * + * - Reading a dentry tree from a metadata resource in a WIM file + * - Writing a dentry tree to a metadata resource in a WIM file + * - Iterating through a tree of WIM dentries + * - Path lookup: translating a path into a WIM dentry or inode + * - Creating, modifying, and deleting WIM dentries * - * wimlib is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. + * Notes: * - * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * - A WIM file can contain multiple images, each of which has an independent + * tree of dentries. "On disk", the dentry tree for an image is stored in + * the "metadata resource" for that image. * - * You should have received a copy of the GNU General Public License along with - * wimlib; if not, see http://www.gnu.org/licenses/. + * - Multiple dentries in an image may correspond to the same inode, or "file". + * When this occurs, it means that the file has multiple names, or "hard + * links". A dentry is not a file, but rather the name of a file! + * + * - Inodes are not represented explicitly in the WIM file format. Instead, + * the metadata resource provides a "hard link group ID" for each dentry. + * wimlib handles pulling out actual inodes from this information, but this + * occurs in inode_fixup.c and not in this file. + * + * - wimlib does not allow *directory* hard links, so a WIM image really does + * have a *tree* of dentries (and not an arbitrary graph of dentries). + * + * - wimlib indexes dentries both case-insensitively and case-sensitively, + * allowing either behavior to be used for path lookup. + * + * - Multiple dentries in a directory might have the same case-insensitive + * name. But wimlib enforces that at most one dentry in a directory can have + * a given case-sensitive name. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif -#include "wimlib.h" -#include "wimlib/case.h" +#include + +#include "wimlib/assert.h" #include "wimlib/dentry.h" +#include "wimlib/inode.h" #include "wimlib/encoding.h" #include "wimlib/endianness.h" -#include "wimlib/error.h" -#include "wimlib/lookup_table.h" #include "wimlib/metadata.h" #include "wimlib/paths.h" -#include "wimlib/resource.h" -#include "wimlib/security.h" -#include "wimlib/sha1.h" -#include "wimlib/timestamp.h" - -#include /* On-disk format of a WIM dentry (directory entry), located in the metadata * resource for a WIM image. */ @@ -142,11 +164,8 @@ struct wim_dentry_on_disk { * not matter, as long as they are unique. * - However, due to bugs in Microsoft's software, it is actually NOT * guaranteed that directory entries that share the same hard link - * group ID are actually hard linked to each either. We have to - * handle this by using special code to use distinguishing features - * (which is possible because some information about the underlying - * inode is repeated in each dentry) to split up these fake hard link - * groups into what they actually are supposed to be. + * group ID are actually hard linked to each either. See + * inode_fixup.c for the code that handles this. */ union { struct { @@ -165,41 +184,56 @@ struct wim_dentry_on_disk { * dentry on-disk. */ le16 num_alternate_data_streams; - /* Length of this file's UTF-16LE encoded short name (8.3 DOS-compatible - * name), if present, in bytes, excluding the null terminator. If this - * file has no short name, then this field should be 0. */ + /* If nonzero, this is the length, in bytes, of this dentry's UTF-16LE + * encoded short name (8.3 DOS-compatible name), excluding the null + * terminator. If zero, then the long name of this dentry does not have + * a corresponding short name (but this does not exclude the possibility + * that another dentry for the same file has a short name). */ le16 short_name_nbytes; - /* Length of this file's UTF-16LE encoded "long" name, excluding the - * null terminator. If this file has no short name, then this field - * should be 0. It's expected that only the root dentry has this field - * set to 0. */ + /* If nonzero, this is the length, in bytes, of this dentry's UTF-16LE + * encoded "long" name, excluding the null terminator. If zero, then + * this file has no long name. The root dentry should not have a long + * name, but all other dentries in the image should have long names. */ le16 file_name_nbytes; - /* Followed by variable length file name, in UTF16-LE, if - * file_name_nbytes != 0. Includes null terminator. */ + /* Beginning of optional, variable-length fields */ + + /* If file_name_nbytes != 0, the next field will be the UTF-16LE encoded + * long file name. This will be null-terminated, so the size of this + * field will really be file_name_nbytes + 2. */ /*utf16lechar file_name[];*/ - /* Followed by variable length short name, in UTF16-LE, if - * short_name_nbytes != 0. Includes null terminator. */ + /* If short_name_nbytes != 0, the next field will be the UTF-16LE + * encoded short name. This will be null-terminated, so the size of + * this field will really be short_name_nbytes + 2. */ /*utf16lechar short_name[];*/ - /* And optionally followed by a variable-length series of tagged items; - * see tagged_items.c. */ -} _packed_attribute; + /* If there is still space in the dentry (according to the 'length' + * field) after 8-byte alignment, then the remaining space will be a + * variable-length list of tagged metadata items. See tagged_items.c + * for more information. */ + /* u8 tagged_items[] _aligned_attribute(8); */ -/* Calculates the unaligned length, in bytes, of an on-disk WIM dentry that has - * a file name and short name that take the specified numbers of bytes. This - * excludes tagged items as well as any alternate data stream entries that may - * follow the dentry. */ +} _packed_attribute; + /* If num_alternate_data_streams != 0, then there are that many + * alternate data stream entries following the dentry, on an 8-byte + * aligned boundary. They are not counted in the 'length' field of the + * dentry. */ + +/* Calculate the minimum unaligned length, in bytes, of an on-disk WIM dentry + * that has names of the specified lengths. (Zero length means the + * corresponding name actually does not exist.) The returned value excludes + * tagged metadata items as well as any alternate data stream entries that may + * need to follow the dentry. */ static u64 dentry_min_len_with_names(u16 file_name_nbytes, u16 short_name_nbytes) { u64 length = sizeof(struct wim_dentry_on_disk); if (file_name_nbytes) - length += file_name_nbytes + 2; + length += (u32)file_name_nbytes + 2; if (short_name_nbytes) - length += short_name_nbytes + 2; + length += (u32)short_name_nbytes + 2; return length; } @@ -218,9 +252,24 @@ do_dentry_set_name(struct wim_dentry *dentry, utf16lechar *file_name, } } -/* Sets the name of a WIM dentry from a UTF-16LE string. - * Only use this on dentries not inserted into the tree. Use rename_wim_path() - * to do a real rename. */ +/* + * Set the name of a WIM dentry from a UTF-16LE string. + * + * This sets the long name of the dentry. The short name will automatically be + * removed, since it may not be appropriate for the new long name. + * + * The @name string need not be null-terminated, since its length is specified + * in @name_nbytes. + * + * If @name_nbytes is 0, both the long and short names of the dentry will be + * removed. + * + * Only use this function on unlinked dentries, since it doesn't update the name + * indices. For dentries that are currently linked into the tree, use + * rename_wim_path(). + * + * Returns 0 or WIMLIB_ERR_NOMEM. + */ int dentry_set_name_utf16le(struct wim_dentry *dentry, const utf16lechar *name, size_t name_nbytes) @@ -237,9 +286,21 @@ dentry_set_name_utf16le(struct wim_dentry *dentry, const utf16lechar *name, } -/* Sets the name of a WIM dentry from a multibyte string. - * Only use this on dentries not inserted into the tree. Use rename_wim_path() - * to do a real rename. */ +/* + * Set the name of a WIM dentry from a 'tchar' string. + * + * This sets the long name of the dentry. The short name will automatically be + * removed, since it may not be appropriate for the new long name. + * + * If @name is NULL or empty, both the long and short names of the dentry will + * be removed. + * + * Only use this function on unlinked dentries, since it doesn't update the name + * indices. For dentries that are currently linked into the tree, use + * rename_wim_path(). + * + * Returns 0 or an error code resulting from a failed string conversion. + */ int dentry_set_name(struct wim_dentry *dentry, const tchar *name) { @@ -258,20 +319,22 @@ dentry_set_name(struct wim_dentry *dentry, const tchar *name) return 0; } -/* Returns the total length of a WIM alternate data stream entry on-disk, - * including the stream name, the null terminator, AND the padding after the - * entry to align the next ADS entry or dentry on an 8-byte boundary. */ +/* Return the length, in bytes, required for the specified alternate data stream + * (ADS) entry on-disk. This accounts for the fixed-length portion of the ADS + * entry, the {stream name and its null terminator} if present, and the padding + * after the entry to align the next ADS entry or dentry on an 8-byte boundary + * in the uncompressed metadata resource buffer. */ static u64 -ads_entry_total_length(const struct wim_ads_entry *entry) +ads_entry_out_total_length(const struct wim_ads_entry *entry) { u64 len = sizeof(struct wim_ads_entry_on_disk); if (entry->stream_name_nbytes) - len += entry->stream_name_nbytes + 2; + len += (u32)entry->stream_name_nbytes + 2; return (len + 7) & ~7; } /* - * Determine whether to include a "dummy" stream when writing a WIM dentry: + * Determine whether to include a "dummy" stream when writing a WIM dentry. * * Some versions of Microsoft's WIM software (the boot driver(s) in WinPE 3.0, * for example) contain a bug where they assume the first alternate data stream @@ -285,20 +348,34 @@ ads_entry_total_length(const struct wim_ads_entry *entry) * though there is already a field in the dentry itself for the unnamed stream * reference, which then goes to waste. */ -static inline bool +static bool inode_needs_dummy_stream(const struct wim_inode *inode) { - return (inode->i_num_ads > 0 && - inode->i_num_ads < 0xffff && /* overflow check */ - inode->i_canonical_streams); /* assume the dentry is okay if it - already had an unnamed ADS entry - when it was read in */ + /* Normal case */ + if (likely(inode->i_num_ads <= 0)) + return false; + + /* Overflow check */ + if (inode->i_num_ads >= 0xFFFF) + return false; + + /* Assume the dentry is okay if it already had an unnamed ADS entry when + * it was read in. */ + if (!inode->i_canonical_streams) + return false; + + /* We can't use use this workaround on encrypted files because WIMGAPI + * reports that the WIM is in an incorrect format. */ + if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) + return false; + + return true; } -/* Calculate the total number of bytes that will be consumed when a WIM dentry - * is written. This includes the base dentry the name fields, any tagged items, - * any alternate data stream entries. Also includes all alignment bytes between - * these parts. */ +/* Calculate the total number of bytes that will be consumed when a dentry is + * written. This includes the fixed-length portion of the dentry, the name + * fields, any tagged metadata items, and any alternate data stream entries. + * Also includes all alignment bytes. */ u64 dentry_out_total_length(const struct wim_dentry *dentry) { @@ -316,15 +393,16 @@ dentry_out_total_length(const struct wim_dentry *dentry) if (unlikely(inode->i_num_ads)) { if (inode_needs_dummy_stream(inode)) - len += ads_entry_total_length(&(struct wim_ads_entry){}); + len += ads_entry_out_total_length(&(struct wim_ads_entry){}); for (u16 i = 0; i < inode->i_num_ads; i++) - len += ads_entry_total_length(&inode->i_ads_entries[i]); + len += ads_entry_out_total_length(&inode->i_ads_entries[i]); } return len; } +/* Internal version of for_dentry_in_tree() that omits the NULL check */ static int do_for_dentry_in_tree(struct wim_dentry *dentry, int (*visitor)(struct wim_dentry *, void *), void *arg) @@ -344,7 +422,7 @@ do_for_dentry_in_tree(struct wim_dentry *dentry, return 0; } - +/* Internal version of for_dentry_in_tree_depth() that omits the NULL check */ static int do_for_dentry_in_tree_depth(struct wim_dentry *dentry, int (*visitor)(struct wim_dentry *, void *), void *arg) @@ -360,10 +438,25 @@ do_for_dentry_in_tree_depth(struct wim_dentry *dentry, return unlikely((*visitor)(dentry, arg)); } -/* Calls a function on all directory entries in a WIM dentry tree. Logically, - * this is a pre-order traversal (the function is called on a parent dentry - * before its children), but sibling dentries will be visited in order as well. - * */ +/* + * Call a function on all dentries in a tree. + * + * @arg will be passed as the second argument to each invocation of @visitor. + * + * This function does a pre-order traversal --- that is, a parent will be + * visited before its children. It also will visit siblings in order of + * case-sensitive filename. Equivalently, this function visits the entire tree + * in the case-sensitive lexicographic order of the full paths. + * + * It is safe to pass NULL for @root, which means that the dentry tree is empty. + * In this case, this function does nothing. + * + * @visitor must not modify the structure of the dentry tree during the + * traversal. + * + * The return value will be 0 if all calls to @visitor returned 0. Otherwise, + * the return value will be the first nonzero value returned by @visitor. + */ int for_dentry_in_tree(struct wim_dentry *root, int (*visitor)(struct wim_dentry *, void *), void *arg) @@ -373,9 +466,10 @@ for_dentry_in_tree(struct wim_dentry *root, return do_for_dentry_in_tree(root, visitor, arg); } -/* Like for_dentry_in_tree(), but the visitor function is always called on a - * dentry's children before on itself. */ -int +/* Like for_dentry_in_tree(), but do a depth-first traversal of the dentry tree. + * That is, the visitor function will be called on a dentry's children before + * itself. It will be safe to free a dentry when visiting it. */ +static int for_dentry_in_tree_depth(struct wim_dentry *root, int (*visitor)(struct wim_dentry *, void *), void *arg) { @@ -384,7 +478,16 @@ for_dentry_in_tree_depth(struct wim_dentry *root, return do_for_dentry_in_tree_depth(root, visitor, arg); } -/* Calculate the full path of @dentry. */ +/* + * Calculate the full path to @dentry within the WIM image, if not already done. + * + * The full name will be saved in the cached value 'dentry->_full_path'. + * + * Whenever possible, use dentry_full_path() instead of calling this and + * accessing _full_path directly. + * + * Returns 0 or an error code resulting from a failed string conversion. + */ int calculate_dentry_full_path(struct wim_dentry *dentry) { @@ -420,6 +523,13 @@ calculate_dentry_full_path(struct wim_dentry *dentry) &dentry->_full_path, &dummy); } +/* + * Return the full path to the @dentry within the WIM image, or NULL if the full + * path could not be determined due to a string conversion error. + * + * The returned memory will be cached in the dentry, so the caller is not + * responsible for freeing it. + */ tchar * dentry_full_path(struct wim_dentry *dentry) { @@ -430,7 +540,6 @@ dentry_full_path(struct wim_dentry *dentry) static int dentry_calculate_subdir_offset(struct wim_dentry *dentry, void *_subdir_offset_p) { - if (dentry_is_directory(dentry)) { u64 *subdir_offset_p = _subdir_offset_p; struct wim_dentry *child; @@ -452,7 +561,18 @@ dentry_calculate_subdir_offset(struct wim_dentry *dentry, void *_subdir_offset_p } /* - * Calculates the subdir offsets for a directory tree. + * Calculate the subdir offsets for a dentry tree, in preparation of writing + * that dentry tree to a metadata resource. + * + * The subdir offset of each dentry is the offset in the uncompressed metadata + * resource at which its child dentries begin, or 0 if that dentry has no + * children. + * + * The caller must initialize *subdir_offset_p to the first subdir offset that + * is available to use after the root dentry is written. + * + * When this function returns, *subdir_offset_p will have been advanced past the + * size needed for the dentry tree within the uncompressed metadata resource. */ void calculate_subdir_offsets(struct wim_dentry *root, u64 *subdir_offset_p) @@ -507,8 +627,9 @@ _avl_dentry_compare_names(const struct avl_tree_node *n1, } /* Default case sensitivity behavior for searches with - * WIMLIB_CASE_PLATFORM_DEFAULT specified. This can be modified by - * wimlib_global_init(). */ + * WIMLIB_CASE_PLATFORM_DEFAULT specified. This can be modified by passing + * WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE or + * WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE to wimlib_global_init(). */ bool default_ignore_case = #ifdef __WIN32__ true @@ -604,8 +725,10 @@ get_dentry_child_with_utf16le_name(const struct wim_dentry *dentry, return child; } -/* Returns the child of @dentry that has the file name @name. Returns NULL if - * no child has the name. */ +/* Given a 'tchar' filename and a directory, look up the dentry for the file. + * If the filename was successfully converted to UTF-16LE and the dentry was + * found, return it; otherwise return NULL. This has configurable case + * sensitivity. */ struct wim_dentry * get_dentry_child_with_name(const struct wim_dentry *dentry, const tchar *name, CASE_SENSITIVITY_TYPE case_type) @@ -628,6 +751,8 @@ get_dentry_child_with_name(const struct wim_dentry *dentry, const tchar *name, return child; } +/* This is the UTF-16LE version of get_dentry(), currently private to this file + * because no one needs it besides get_dentry(). */ static struct wim_dentry * get_dentry_utf16le(WIMStruct *wim, const utf16lechar *path, CASE_SENSITIVITY_TYPE case_type) @@ -744,19 +869,19 @@ get_dentry(WIMStruct *wim, const tchar *path, CASE_SENSITIVITY_TYPE case_type) return dentry; } -/* Takes in a path of length @len in @buf, and transforms it into a string for - * the path of its parent directory. */ +/* Modify @path, which is a null-terminated string @len 'tchars' in length, + * in-place to produce the path to its parent directory. */ static void -to_parent_name(tchar *buf, size_t len) +to_parent_name(tchar *path, size_t len) { ssize_t i = (ssize_t)len - 1; - while (i >= 0 && buf[i] == WIM_PATH_SEPARATOR) + while (i >= 0 && path[i] == WIM_PATH_SEPARATOR) i--; - while (i >= 0 && buf[i] != WIM_PATH_SEPARATOR) + while (i >= 0 && path[i] != WIM_PATH_SEPARATOR) i--; - while (i >= 0 && buf[i] == WIM_PATH_SEPARATOR) + while (i >= 0 && path[i] == WIM_PATH_SEPARATOR) i--; - buf[i + 1] = T('\0'); + path[i + 1] = T('\0'); } /* Similar to get_dentry(), but returns the dentry named by @path with the last @@ -775,73 +900,18 @@ get_parent_dentry(WIMStruct *wim, const tchar *path, return get_dentry(wim, buf, case_type); } -#ifdef WITH_FUSE -/* Finds the dentry, lookup table entry, and stream index for a WIM file stream, - * given a path name. +/* + * Create an unlinked dentry. * - * Currently, lookups of this type are only needed if FUSE is enabled. */ -int -wim_pathname_to_stream(WIMStruct *wim, - const tchar *path, - int lookup_flags, - struct wim_dentry **dentry_ret, - struct wim_lookup_table_entry **lte_ret, - u16 *stream_idx_ret) -{ - struct wim_dentry *dentry; - struct wim_lookup_table_entry *lte; - u16 stream_idx; - const tchar *stream_name = NULL; - struct wim_inode *inode; - tchar *p = NULL; - - if (lookup_flags & LOOKUP_FLAG_ADS_OK) { - stream_name = path_stream_name(path); - if (stream_name) { - p = (tchar*)stream_name - 1; - *p = T('\0'); - } - } - - dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE); - if (p) - *p = T(':'); - if (!dentry) - return -errno; - - inode = dentry->d_inode; - - if (!inode->i_resolved) - if (inode_resolve_streams(inode, wim->lookup_table, false)) - return -EIO; - - if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK) - && inode_is_directory(inode)) - return -EISDIR; - - if (stream_name) { - struct wim_ads_entry *ads_entry; - - ads_entry = inode_get_ads_entry(inode, stream_name); - if (!ads_entry) - return -errno; - - stream_idx = ads_entry - inode->i_ads_entries + 1; - lte = ads_entry->lte; - } else { - lte = inode_unnamed_stream_resolved(inode, &stream_idx); - } - if (dentry_ret) - *dentry_ret = dentry; - if (lte_ret) - *lte_ret = lte; - if (stream_idx_ret) - *stream_idx_ret = stream_idx; - return 0; -} -#endif /* WITH_FUSE */ - -/* Creates an unlinked directory entry. */ + * @name specifies the long name to give the new dentry. If NULL or empty, the + * new dentry will be given no long name. + * + * The new dentry will have no short name and no associated inode. + * + * On success, returns 0 and a pointer to the new, allocated dentry is stored in + * *dentry_ret. On failure, returns WIMLIB_ERR_NOMEM or an error code resulting + * from a failed string conversion. + */ int new_dentry(const tchar *name, struct wim_dentry **dentry_ret) { @@ -852,12 +922,10 @@ new_dentry(const tchar *name, struct wim_dentry **dentry_ret) if (!dentry) return WIMLIB_ERR_NOMEM; - if (*name) { + if (name && *name) { ret = dentry_set_name(dentry, name); if (ret) { FREE(dentry); - ERROR("Failed to set name on new dentry with name \"%"TS"\"", - name); return ret; } } @@ -868,9 +936,10 @@ new_dentry(const tchar *name, struct wim_dentry **dentry_ret) static int _new_dentry_with_inode(const tchar *name, struct wim_dentry **dentry_ret, - bool timeless) + bool timeless) { struct wim_dentry *dentry; + struct wim_inode *inode; int ret; ret = new_dentry(name, &dentry); @@ -878,38 +947,46 @@ _new_dentry_with_inode(const tchar *name, struct wim_dentry **dentry_ret, return ret; if (timeless) - dentry->d_inode = new_timeless_inode(); + inode = new_timeless_inode(); else - dentry->d_inode = new_inode(); - if (dentry->d_inode == NULL) { + inode = new_inode(); + if (!inode) { free_dentry(dentry); return WIMLIB_ERR_NOMEM; } - inode_add_dentry(dentry, dentry->d_inode); + d_associate(dentry, inode); + *dentry_ret = dentry; return 0; } +/* Like new_dentry(), but also allocate an inode and associate it with the + * dentry. The timestamps for the inode will be set to the current time. */ int -new_dentry_with_timeless_inode(const tchar *name, struct wim_dentry **dentry_ret) +new_dentry_with_inode(const tchar *name, struct wim_dentry **dentry_ret) { - return _new_dentry_with_inode(name, dentry_ret, true); + return _new_dentry_with_inode(name, dentry_ret, false); } +/* Like new_dentry_with_inode(), but don't bother setting the timestamps for the + * new inode; instead, just leave them as 0, under the presumption that the + * caller will set them itself. */ int -new_dentry_with_inode(const tchar *name, struct wim_dentry **dentry_ret) +new_dentry_with_timeless_inode(const tchar *name, struct wim_dentry **dentry_ret) { - return _new_dentry_with_inode(name, dentry_ret, false); + return _new_dentry_with_inode(name, dentry_ret, true); } +/* Create an unnamed dentry with a new inode for a directory with the default + * metadata. */ int new_filler_directory(struct wim_dentry **dentry_ret) { int ret; struct wim_dentry *dentry; - ret = new_dentry_with_inode(T(""), &dentry); + ret = new_dentry_with_inode(NULL, &dentry); if (ret) return ret; /* Leave the inode number as 0; this is allowed for non @@ -936,19 +1013,17 @@ dentry_tree_clear_inode_visited(struct wim_dentry *root) /* * Free a WIM dentry. * - * In addition to freeing the dentry itself, this decrements the link count of - * the corresponding inode (if any). If the inode's link count reaches 0, the - * inode is freed as well. + * In addition to freeing the dentry itself, this disassociates the dentry from + * its inode. If the inode is no longer in use, it will be freed as well. */ void free_dentry(struct wim_dentry *dentry) { if (dentry) { + d_disassociate(dentry); FREE(dentry->file_name); FREE(dentry->short_name); FREE(dentry->_full_path); - if (dentry->d_inode) - put_inode(dentry->d_inode); FREE(dentry); } } @@ -969,19 +1044,22 @@ do_free_dentry_and_unref_streams(struct wim_dentry *dentry, void *lookup_table) } /* - * Recursively frees all directory entries in the specified tree. + * Free all dentries in a tree. * * @root: - * The root of the tree. + * The root of the dentry tree to free. If NULL, this function has no + * effect. * * @lookup_table: - * The lookup table for dentries. If non-NULL, the reference counts in the - * lookup table for the lookup table entries corresponding to the dentries - * will be decremented. + * A pointer to the lookup table for the WIM, or NULL if not specified. If + * specified, this function will decrement the reference counts of the + * single-instance streams referenced by the dentries. * - * This also puts references to the corresponding inodes. + * This function also releases references to the corresponding inodes. * - * This does *not* unlink @root from its parent directory (if it has one). + * This function does *not* unlink @root from its parent directory, if it has + * one. If @root has a parent, the caller must unlink @root before calling this + * function. */ void free_dentry_tree(struct wim_dentry *root, struct wim_lookup_table *lookup_table) @@ -1028,21 +1106,21 @@ dir_index_child_ci(struct wim_inode *dir, struct wim_dentry *child) return avl_tree_entry(duplicate, struct wim_dentry, d_index_node_ci); } -/* Removes the specified dentry from its directory's case-sensitive index. */ +/* Remove the specified dentry from its directory's case-sensitive index. */ static void dir_unindex_child(struct wim_inode *dir, struct wim_dentry *child) { avl_tree_remove(&dir->i_children, &child->d_index_node); } -/* Removes the specified dentry from its directory's case-insensitive index. */ +/* Remove the specified dentry from its directory's case-insensitive index. */ static void dir_unindex_child_ci(struct wim_inode *dir, struct wim_dentry *child) { avl_tree_remove(&dir->i_children_ci, &child->d_index_node_ci); } -/* Returns true iff the specified dentry is in its parent directory's +/* Return true iff the specified dentry is in its parent directory's * case-insensitive index. */ static bool dentry_in_ci_index(const struct wim_dentry *dentry) @@ -1051,10 +1129,13 @@ dentry_in_ci_index(const struct wim_dentry *dentry) } /* - * Links a dentry into the directory tree. + * Link a dentry into the tree. * - * @parent: The dentry that will be the parent of @child. - * @child: The dentry to link. + * @parent: + * The dentry that will be the parent of @child. It must name a directory. + * + * @child: + * The dentry to link. It must be currently unlinked. * * Returns NULL if successful. If @parent already contains a dentry with the * same case-sensitive name as @child, returns a pointer to this duplicate @@ -1087,13 +1168,16 @@ dentry_add_child(struct wim_dentry *parent, struct wim_dentry *child) return NULL; } -/* Unlink a WIM dentry from the directory entry tree. */ +/* Unlink a dentry from the tree. */ void unlink_dentry(struct wim_dentry *dentry) { struct wim_inode *dir; - if (dentry_is_root(dentry)) + /* Do nothing if the dentry is root or it's already unlinked. Not + * actually necessary based on the current callers, but we do the check + * here to be safe. */ + if (unlikely(dentry->d_parent == dentry)) return; dir = dentry->d_parent->d_inode; @@ -1106,7 +1190,7 @@ unlink_dentry(struct wim_dentry *dentry) if (!list_empty(&dentry->d_ci_conflict_list)) { /* Make a different case-insensitively-the-same dentry - * be the "representative" in the search index. */ + * be the "representative" in the search index. */ struct list_head *next; struct wim_dentry *other; struct wim_dentry *existing; @@ -1118,6 +1202,10 @@ unlink_dentry(struct wim_dentry *dentry) } } list_del(&dentry->d_ci_conflict_list); + + /* Not actually necessary, but to be safe don't retain the now-obsolete + * parent pointer. */ + dentry->d_parent = dentry; } static int @@ -1135,8 +1223,8 @@ read_extra_data(const u8 *p, const u8 *end, struct wim_inode *inode) return 0; } -/* Reads a WIM directory entry, including all alternate data stream entries that - * follow it, from the WIM image's metadata resource. */ +/* Read a dentry, including all alternate data stream entries that follow it, + * from an uncompressed metadata resource buffer. */ static int read_dentry(const u8 * restrict buf, size_t buf_len, u64 *offset_p, struct wim_dentry **dentry_ret) @@ -1199,7 +1287,7 @@ read_dentry(const u8 * restrict buf, size_t buf_len, } /* Allocate new dentry structure, along with a preliminary inode. */ - ret = new_dentry_with_timeless_inode(T(""), &dentry); + ret = new_dentry_with_timeless_inode(NULL, &dentry); if (ret) return ret; @@ -1268,27 +1356,25 @@ read_dentry(const u8 * restrict buf, size_t buf_len, /* Read the filename if present. Note: if the filename is empty, there * is no null terminator following it. */ if (file_name_nbytes) { - dentry->file_name = utf16le_dupz((const utf16lechar *)p, - file_name_nbytes); + dentry->file_name = utf16le_dupz(p, file_name_nbytes); if (dentry->file_name == NULL) { ret = WIMLIB_ERR_NOMEM; goto err_free_dentry; } dentry->file_name_nbytes = file_name_nbytes; - p += file_name_nbytes + 2; + p += (u32)file_name_nbytes + 2; } /* Read the short filename if present. Note: if there is no short * filename, there is no null terminator following it. */ if (short_name_nbytes) { - dentry->short_name = utf16le_dupz((const utf16lechar *)p, - short_name_nbytes); + dentry->short_name = utf16le_dupz(p, short_name_nbytes); if (dentry->short_name == NULL) { ret = WIMLIB_ERR_NOMEM; goto err_free_dentry; } dentry->short_name_nbytes = short_name_nbytes; - p += short_name_nbytes + 2; + p += (u32)short_name_nbytes + 2; } /* Read extra data at end of dentry (but before alternate data stream @@ -1334,6 +1420,7 @@ err_free_dentry: return ret; } +/* Is the dentry named "." or ".." ? */ static bool dentry_is_dot_or_dotdot(const struct wim_dentry *dentry) { @@ -1432,7 +1519,7 @@ read_dentry_tree_recursive(const u8 * restrict buf, size_t buf_len, } /* - * Read a tree of dentries (directory entries) from a WIM metadata resource. + * Read a tree of dentries from a WIM metadata resource. * * @buf: * Buffer containing an uncompressed WIM metadata resource. @@ -1472,12 +1559,7 @@ read_dentry_tree(const u8 *buf, size_t buf_len, { WARNING("The root directory has a nonempty name; " "removing it."); - FREE(root->file_name); - FREE(root->short_name); - root->file_name = NULL; - root->short_name = NULL; - root->file_name_nbytes = 0; - root->short_name_nbytes = 0; + dentry_set_name(root, NULL); } if (unlikely(!dentry_is_directory(root))) { @@ -1504,11 +1586,17 @@ err_free_dentry_tree: } /* - * Writes a WIM alternate data stream (ADS) entry to an output buffer. + * Write a WIM alternate data stream (ADS) entry to an output buffer. + * + * @ads_entry: + * The ADS entry to write. + * + * @hash: + * The hash field to use (instead of the one stored directly in the ADS + * entry, which isn't valid if the inode has been "resolved"). * - * @ads_entry: The ADS entry structure. - * @hash: The hash field to use (instead of the one in the ADS entry). - * @p: The memory location to write the data to. + * @p: + * The memory location to which to write the data. * * Returns a pointer to the byte after the last byte written. */ @@ -1526,7 +1614,7 @@ write_ads_entry(const struct wim_ads_entry *ads_entry, p += sizeof(struct wim_ads_entry_on_disk); if (ads_entry->stream_name_nbytes) { p = mempcpy(p, ads_entry->stream_name, - ads_entry->stream_name_nbytes + 2); + (u32)ads_entry->stream_name_nbytes + 2); } /* Align to 8-byte boundary */ while ((uintptr_t)p & 7) @@ -1536,13 +1624,18 @@ write_ads_entry(const struct wim_ads_entry *ads_entry, } /* - * Writes a WIM dentry to an output buffer. + * Write a WIM dentry to an output buffer. * - * @dentry: The dentry structure. - * @p: The memory location to write the data to. + * This includes any alternate data stream entries that may follow the dentry + * itself. * - * Returns the pointer to the byte after the last byte we wrote as part of the - * dentry, including any alternate data stream entries. + * @dentry: + * The dentry to write. + * + * @p: + * The memory location to which to write the data. + * + * Returns a pointer to the byte following the last written. */ static u8 * write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p) @@ -1565,8 +1658,6 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p) disk_dentry->security_id = cpu_to_le32(inode->i_security_id); disk_dentry->subdir_offset = cpu_to_le64(dentry->subdir_offset); - /* UNIX data uses the two 8-byte reserved fields. So if no UNIX data - * exists, they get set to 0, just as we would do anyway. */ disk_dentry->unused_1 = cpu_to_le64(0); disk_dentry->unused_2 = cpu_to_le64(0); @@ -1599,10 +1690,10 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p) wimlib_assert(dentry_is_root(dentry) != dentry_has_long_name(dentry)); if (dentry_has_long_name(dentry)) - p = mempcpy(p, dentry->file_name, dentry->file_name_nbytes + 2); + p = mempcpy(p, dentry->file_name, (u32)dentry->file_name_nbytes + 2); if (dentry_has_short_name(dentry)) - p = mempcpy(p, dentry->short_name, dentry->short_name_nbytes + 2); + p = mempcpy(p, dentry->short_name, (u32)dentry->short_name_nbytes + 2); /* Align to 8-byte boundary */ while ((uintptr_t)p & 7) @@ -1651,18 +1742,26 @@ write_dir_dentries(struct wim_dentry *dir, void *_pp) return 0; } -/* Writes a directory tree to the metadata resource. +/* + * Write 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. + * @root: + * The root of a dentry tree on which calculate_subdir_offsets() has been + * called. This cannot be NULL; if the dentry tree is empty, the caller is + * expected to first generate a dummy root directory. + * + * @p: + * Pointer to a buffer with enough space for the dentry tree. This size + * must have been obtained by calculate_subdir_offsets(). * - * Returns pointer to the byte after the last byte we wrote. + * Returns a pointer to the byte following the last written. */ u8 * write_dentry_tree(struct wim_dentry *root, u8 *p) { DEBUG("Writing dentry tree."); - wimlib_assert(dentry_is_root(root)); + + wimlib_assert(root != NULL); /* write root dentry and end-of-directory entry following it */ p = write_dentry(root, p);