X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fdentry.c;h=c9a84d718eee2952197886e634cf1fcb12594335;hb=52a908ca68284a4dfa89a884f547fd78d1543772;hp=435a7c919665f89ee3736069e7f4f432b233d87b;hpb=b0a6bbcba9dc23f4722827cb13fb0efff5e2799d;p=wimlib diff --git a/src/dentry.c b/src/dentry.c index 435a7c91..c9a84d71 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -38,6 +38,7 @@ #include "wimlib/lookup_table.h" #include "wimlib/metadata.h" #include "wimlib/resource.h" +#include "wimlib/security.h" #include "wimlib/sha1.h" #include "wimlib/timestamp.h" @@ -79,7 +80,9 @@ struct wim_dentry_on_disk { /* Length of this directory entry in bytes, not including any alternate * data stream entries. Should be a multiple of 8 so that the following * dentry or alternate data stream entry is aligned on an 8-byte - * boundary. (If not, wimlib will round it up.) + * boundary. (If not, wimlib will round it up.) It must be at least as + * long as the fixed-length fields of the dentry (WIM_DENTRY_DISK_SIZE), + * plus the lengths of the file name and/or short name if present. * * It is also possible for this field to be 0. This situation, which is * undocumented, indicates the end of a list of sibling nodes in a @@ -98,26 +101,23 @@ struct wim_dentry_on_disk { * security descriptors (see: `struct wim_security_data') */ sle32 security_id; - /* Offset from the start of the uncompressed metadata resource of this - * directory's child directory entries, or 0 if this directory entry - * does not correspond to a directory or otherwise does not have any - * children. */ + /* Offset, in bytes, from the start of the uncompressed metadata + * resource of this directory's child directory entries, or 0 if this + * directory entry does not correspond to a directory or otherwise does + * not have any children. */ le64 subdir_offset; /* Reserved fields */ le64 unused_1; le64 unused_2; - /* The following three time fields should correspond to those gotten by - * calling GetFileTime() on Windows. */ - /* Creation time, in 100-nanosecond intervals since January 1, 1601. */ + /* Creation time, last access time, and last write time, in + * 100-nanosecond intervals since 12:00 a.m UTC January 1, 1601. They + * should correspond to the times gotten by calling GetFileTime() on + * Windows. */ le64 creation_time; - - /* Last access time, in 100-nanosecond intervals since January 1, 1601. */ le64 last_access_time; - - /* Last write time, in 100-nanosecond intervals since January 1, 1601. */ le64 last_write_time; /* Vaguely, the SHA-1 message digest ("hash") of the file's contents. @@ -153,7 +153,7 @@ struct wim_dentry_on_disk { * version of the following fields containing the reparse tag is valid. * Furthermore, the field notated as not_rpfixed, as far as I can tell, * is supposed to be set to 1 if reparse point fixups (a.k.a. fixing the - * targets of absolute symbolic links) were done, and otherwise 0. + * targets of absolute symbolic links) were *not* done, and otherwise 0. * * If this directory entry is not for a reparse point, then the version * of the following fields containing the hard_link_group_id is valid. @@ -170,9 +170,9 @@ struct wim_dentry_on_disk { * 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 - * (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. + * (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. */ union { struct { @@ -202,9 +202,9 @@ struct wim_dentry_on_disk { * set to 0. */ le16 file_name_nbytes; - /* Follewed by variable length file name, in UTF16-LE, if + /* Followed by variable length file name, in UTF16-LE, if * file_name_nbytes != 0. Includes null terminator. */ - utf16lechar file_name[]; + /*utf16lechar file_name[];*/ /* Followed by variable length short name, in UTF16-LE, if * short_name_nbytes != 0. Includes null terminator. */ @@ -791,8 +791,12 @@ get_dentry_utf16le(WIMStruct *wim, const utf16lechar *path) return cur_dentry; } -/* Returns the dentry corresponding to the @path, or NULL if there is no such - * dentry. */ +/* + * Returns the dentry in the currently selected WIM image named by @path + * starting from the root of the WIM image, or NULL if there is no such dentry. + * + * On Windows, the search is done case-insensitively. + */ struct wim_dentry * get_dentry(WIMStruct *wim, const tchar *path) { @@ -984,13 +988,6 @@ new_timeless_inode(void) 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"); - FREE(inode); - return NULL; - } - #endif INIT_LIST_HEAD(&inode->i_dentry); } return inode; @@ -1129,16 +1126,10 @@ free_inode(struct wim_inode *inode) destroy_ads_entry(&inode->i_ads_entries[i]); FREE(inode->i_ads_entries); } - #ifdef WITH_FUSE - wimlib_assert(inode->i_num_opened_fds == 0); - FREE(inode->i_fds); - pthread_mutex_destroy(&inode->i_mutex); - #endif /* HACK: This may instead delete the inode from i_list, but the * hlist_del() behaves the same as list_del(). */ if (!hlist_unhashed(&inode->i_hlist)) hlist_del(&inode->i_hlist); - FREE(inode->i_extracted_file); FREE(inode); } } @@ -1201,11 +1192,13 @@ do_free_dentry(struct wim_dentry *dentry, void *_lookup_table) /* * Unlinks and frees a dentry tree. * - * @root: The root of the tree. - * @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. + * @root: + * The root of the tree. + * + * @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. */ void free_dentry_tree(struct wim_dentry *root, struct wim_lookup_table *lookup_table) @@ -1500,6 +1493,15 @@ inode_add_ads_with_data(struct wim_inode *inode, const tchar *name, return 0; } +bool +inode_has_named_stream(const struct wim_inode *inode) +{ + for (u16 i = 0; i < inode->i_num_ads; i++) + if (ads_entry_is_named_stream(&inode->i_ads_entries[i])) + return true; + return false; +} + /* Set the unnamed stream of a WIM inode, given a data buffer containing the * stream contents. */ int @@ -1540,6 +1542,15 @@ inode_remove_ads(struct wim_inode *inode, u16 idx, inode->i_num_ads--; } +bool +inode_has_unix_data(const struct wim_inode *inode) +{ + for (u16 i = 0; i < inode->i_num_ads; i++) + if (ads_entry_is_unix_data(&inode->i_ads_entries[i])) + return true; + return false; +} + #ifndef __WIN32__ int inode_get_unix_data(const struct wim_inode *inode, @@ -1615,19 +1626,24 @@ inode_set_unix_data(struct wim_inode *inode, uid_t uid, gid_t gid, mode_t mode, /* * Reads the alternate data stream entries of a WIM dentry. * - * @p: Pointer to buffer that starts with the first alternate stream entry. + * @p: + * Pointer to buffer that starts with the first alternate stream entry. * - * @inode: Inode to load the alternate data streams into. - * @inode->i_num_ads must have been set to the number of - * alternate data streams that are expected. + * @inode: + * Inode to load the alternate data streams into. @inode->i_num_ads must + * have been set to the number of alternate data streams that are expected. * - * @remaining_size: Number of bytes of data remaining in the buffer pointed - * to by @p. + * @remaining_size: + * Number of bytes of data remaining in the buffer pointed to by @p. * + * On success, inode->i_ads_entries is set to an array of `struct + * wim_ads_entry's of length inode->i_num_ads. On failure, @inode is not + * modified. * - * Return 0 on success or nonzero on failure. On success, inode->i_ads_entries - * is set to an array of `struct wim_ads_entry's of length inode->i_num_ads. On - * failure, @inode is not modified. + * Return values: + * WIMLIB_ERR_SUCCESS (0) + * WIMLIB_ERR_INVALID_METADATA_RESOURCE + * WIMLIB_ERR_NOMEM */ static int read_ads_entries(const u8 * restrict p, struct wim_inode * restrict inode, @@ -1731,7 +1747,7 @@ out_of_memory: goto out_free_ads_entries; out_invalid: ERROR("An alternate data stream entry is invalid"); - ret = WIMLIB_ERR_INVALID_DENTRY; + ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; out_free_ads_entries: if (ads_entries) { for (u16 i = 0; i < num_ads; i++) @@ -1752,7 +1768,7 @@ out: * @metadata_resource_len: * Length of the metadata resource buffer, in bytes. * - * @offset: Offset of the dentry within the metadata resource. + * @offset: Offset of the dentry within the metadata resource. * * @dentry: A `struct wim_dentry' that will be filled in by this function. * @@ -1762,9 +1778,10 @@ out: * this was a special "end of directory" dentry and not a real dentry. If * nonzero, this was a real dentry. * - * Possible errors include: - * WIMLIB_ERR_NOMEM - * WIMLIB_ERR_INVALID_DENTRY + * Return values: + * WIMLIB_ERR_SUCCESS (0) + * WIMLIB_ERR_INVALID_METADATA_RESOURCE + * WIMLIB_ERR_NOMEM */ int read_dentry(const u8 * restrict metadata_resource, u64 metadata_resource_len, @@ -1798,7 +1815,7 @@ read_dentry(const u8 * restrict metadata_resource, u64 metadata_resource_len, ERROR("Directory entry starting at %"PRIu64" ends past the " "end of the metadata resource (size %"PRIu64")", offset, metadata_resource_len); - return WIMLIB_ERR_INVALID_DENTRY; + return WIMLIB_ERR_INVALID_METADATA_RESOURCE; } dentry->length = le64_to_cpu(disk_dentry->length); @@ -1820,15 +1837,15 @@ read_dentry(const u8 * restrict metadata_resource, u64 metadata_resource_len, "%"PRIu64" ends past the end of the metadata resource " "(size %"PRIu64")", offset, dentry->length, metadata_resource_len); - return WIMLIB_ERR_INVALID_DENTRY; + return WIMLIB_ERR_INVALID_METADATA_RESOURCE; } /* Make sure the dentry length is at least as large as the number of * fixed-length fields */ if (dentry->length < sizeof(struct wim_dentry_on_disk)) { ERROR("Directory entry has invalid length of %"PRIu64" bytes", - entry->length); - return WIMLIB_ERR_INVALID_DENTRY; + dentry->length); + return WIMLIB_ERR_INVALID_METADATA_RESOURCE; } /* Allocate a `struct wim_inode' for this `struct wim_dentry'. */ @@ -1874,7 +1891,7 @@ read_dentry(const u8 * restrict metadata_resource, u64 metadata_resource_len, if ((short_name_nbytes & 1) | (file_name_nbytes & 1)) { ERROR("Dentry name is not valid UTF-16LE (odd number of bytes)!"); - ret = WIMLIB_ERR_INVALID_DENTRY; + ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; goto out_free_inode; } @@ -1891,7 +1908,7 @@ read_dentry(const u8 * restrict metadata_resource, u64 metadata_resource_len, ERROR("Unexpected end of directory entry! (Expected " "at least %"PRIu64" bytes, got %"PRIu64" bytes.)", calculated_size, dentry->length); - ret = WIMLIB_ERR_INVALID_DENTRY; + ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; goto out_free_inode; } @@ -1945,7 +1962,7 @@ read_dentry(const u8 * restrict metadata_resource, u64 metadata_resource_len, * be included in the dentry->length field for some reason. */ if (inode->i_num_ads != 0) { - ret = WIMLIB_ERR_INVALID_DENTRY; + ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; if (offset + dentry->length > metadata_resource_len || (ret = read_ads_entries(&metadata_resource[offset + dentry->length], inode, @@ -1990,22 +2007,27 @@ dentry_get_file_type_string(const struct wim_dentry *dentry) /* Reads the children of a dentry, and all their children, ..., etc. from the * metadata resource and into the dentry tree. * - * @metadata_resource: An array that contains the uncompressed metadata - * resource for the WIM file. + * @metadata_resource: + * An array that contains the uncompressed metadata resource for the WIM + * file. * - * @metadata_resource_len: The length of the uncompressed metadata resource, in - * bytes. + * @metadata_resource_len: + * The length of the uncompressed metadata resource, in bytes. * - * @dentry: A pointer to a `struct wim_dentry' that is the root of the directory - * tree and has already been read from the metadata resource. It - * does not need to be the real root because this procedure is - * called recursively. + * @dentry: + * A pointer to a `struct wim_dentry' that is the root of the directory + * tree and has already been read from the metadata resource. It does not + * need to be the real root because this procedure is called recursively. * - * Returns zero on success; nonzero on failure. + * Return values: + * WIMLIB_ERR_SUCCESS (0) + * WIMLIB_ERR_INVALID_METADATA_RESOURCE + * WIMLIB_ERR_NOMEM */ int -read_dentry_tree(const u8 metadata_resource[], u64 metadata_resource_len, - struct wim_dentry *dentry) +read_dentry_tree(const u8 * restrict metadata_resource, + u64 metadata_resource_len, + struct wim_dentry * restrict dentry) { u64 cur_offset = dentry->subdir_offset; struct wim_dentry *child; @@ -2031,7 +2053,7 @@ read_dentry_tree(const u8 metadata_resource[], u64 metadata_resource_len, "of \"%"TS"\" coincide with children of \"%"TS"\"", dentry_full_path(dentry), dentry_full_path(parent)); - return WIMLIB_ERR_INVALID_DENTRY; + return WIMLIB_ERR_INVALID_METADATA_RESOURCE; } } @@ -2125,7 +2147,7 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p) wimlib_assert(((uintptr_t)p & 7) == 0); /* 8 byte aligned */ orig_p = p; - inode = dentry->d_inode; + inode = dentry->d_inode; disk_dentry = (struct wim_dentry_on_disk*)p; disk_dentry->attributes = cpu_to_le32(inode->i_attributes); @@ -2251,7 +2273,7 @@ write_dentry_tree_recursive(const struct wim_dentry *parent, u8 *p) * Returns pointer to the byte after the last byte we wrote. */ u8 * -write_dentry_tree(const struct wim_dentry *root, u8 *p) +write_dentry_tree(const struct wim_dentry * restrict root, u8 * restrict p) { DEBUG("Writing dentry tree."); wimlib_assert(dentry_is_root(root)); @@ -2268,3 +2290,199 @@ write_dentry_tree(const struct wim_dentry *root, u8 *p) /* Recursively write the rest of the dentry tree. */ return write_dentry_tree_recursive(root, p); } + + +static int +init_wimlib_dentry(struct wimlib_dir_entry *wdentry, + struct wim_dentry *dentry, + const WIMStruct *wim) +{ + int ret; + size_t dummy; + const struct wim_inode *inode = dentry->d_inode; + struct wim_lookup_table_entry *lte; + +#if TCHAR_IS_UTF16LE + wdentry->filename = dentry->file_name; + wdentry->dos_name = dentry->short_name; +#else + if (dentry_has_long_name(dentry)) { + ret = utf16le_to_tstr(dentry->file_name, + dentry->file_name_nbytes, + (tchar**)&wdentry->filename, + &dummy); + if (ret) + return ret; + } + if (dentry_has_short_name(dentry)) { + ret = utf16le_to_tstr(dentry->short_name, + dentry->short_name_nbytes, + (tchar**)&wdentry->dos_name, + &dummy); + if (ret) + return ret; + } +#endif + ret = calculate_dentry_full_path(dentry); + if (ret) + return ret; + wdentry->full_path = dentry->_full_path; + + for (struct wim_dentry *d = dentry; !dentry_is_root(d); d = d->parent) + wdentry->depth++; + + if (inode->i_security_id >= 0) { + const struct wim_security_data *sd = wim_const_security_data(wim); + wdentry->security_descriptor = sd->descriptors[inode->i_security_id]; + wdentry->security_descriptor_size = sd->sizes[inode->i_security_id]; + } + wdentry->reparse_tag = inode->i_reparse_tag; + wdentry->num_links = inode->i_nlink; + wdentry->attributes = inode->i_attributes; + wdentry->hard_link_group_id = inode->i_ino; + wdentry->creation_time = wim_timestamp_to_timespec(inode->i_creation_time); + wdentry->last_write_time = wim_timestamp_to_timespec(inode->i_last_write_time); + wdentry->last_access_time = wim_timestamp_to_timespec(inode->i_last_access_time); + + lte = inode_unnamed_lte(inode, wim->lookup_table); + if (lte) + lte_to_wimlib_resource_entry(lte, &wdentry->streams[0].resource); + + for (unsigned i = 0; i < inode->i_num_ads; i++) { + if (inode->i_ads_entries[i].stream_name == NULL) + continue; + lte = inode_stream_lte(inode, i + 1, wim->lookup_table); + wdentry->num_named_streams++; + if (lte) { + lte_to_wimlib_resource_entry(lte, &wdentry->streams[ + wdentry->num_named_streams].resource); + } + #if TCHAR_IS_UTF16LE + wdentry->streams[wdentry->num_named_streams].stream_name = + inode->i_ads_entries[i].stream_name; + #else + size_t dummy; + + ret = utf16le_to_tstr(inode->i_ads_entries[i].stream_name, + inode->i_ads_entries[i].stream_name_nbytes, + (tchar**)&wdentry->streams[ + wdentry->num_named_streams].stream_name, + &dummy); + if (ret) + return ret; + #endif + } + return 0; +} + +static void +free_wimlib_dentry(struct wimlib_dir_entry *wdentry) +{ +#if !TCHAR_IS_UTF16LE + FREE((tchar*)wdentry->filename); + FREE((tchar*)wdentry->dos_name); + for (unsigned i = 1; i <= wdentry->num_named_streams; i++) + FREE((tchar*)wdentry->streams[i].stream_name); +#endif + FREE(wdentry); +} + +struct iterate_dir_tree_ctx { + WIMStruct *wim; + int flags; + wimlib_iterate_dir_tree_callback_t cb; + void *user_ctx; +}; + +static int +do_iterate_dir_tree(WIMStruct *wim, + struct wim_dentry *dentry, int flags, + wimlib_iterate_dir_tree_callback_t cb, + void *user_ctx); + +static int +call_do_iterate_dir_tree(struct wim_dentry *dentry, void *_ctx) +{ + struct iterate_dir_tree_ctx *ctx = _ctx; + return do_iterate_dir_tree(ctx->wim, dentry, ctx->flags, + ctx->cb, ctx->user_ctx); +} + +static int +do_iterate_dir_tree(WIMStruct *wim, + struct wim_dentry *dentry, int flags, + wimlib_iterate_dir_tree_callback_t cb, + void *user_ctx) +{ + struct wimlib_dir_entry *wdentry; + int ret = WIMLIB_ERR_NOMEM; + + + wdentry = CALLOC(1, sizeof(struct wimlib_dir_entry) + + (1 + dentry->d_inode->i_num_ads) * + sizeof(struct wimlib_stream_entry)); + if (!wdentry) + goto out; + + ret = init_wimlib_dentry(wdentry, dentry, wim); + if (ret) + goto out_free_wimlib_dentry; + + if (!(flags & WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN)) { + ret = (*cb)(wdentry, user_ctx); + if (ret) + goto out_free_wimlib_dentry; + } + + if (flags & (WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE | + WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN)) + { + struct iterate_dir_tree_ctx ctx = { + .wim = wim, + .flags = flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN, + .cb = cb, + .user_ctx = user_ctx, + }; + ret = for_dentry_child(dentry, call_do_iterate_dir_tree, &ctx); + } +out_free_wimlib_dentry: + free_wimlib_dentry(wdentry); +out: + return ret; +} + +struct image_iterate_dir_tree_ctx { + const tchar *path; + int flags; + wimlib_iterate_dir_tree_callback_t cb; + void *user_ctx; +}; + + +static int +image_do_iterate_dir_tree(WIMStruct *wim) +{ + struct image_iterate_dir_tree_ctx *ctx = wim->private; + struct wim_dentry *dentry; + + dentry = get_dentry(wim, ctx->path); + if (!dentry) + return WIMLIB_ERR_PATH_DOES_NOT_EXIST; + return do_iterate_dir_tree(wim, dentry, ctx->flags, ctx->cb, ctx->user_ctx); +} + +/* API function documented in wimlib.h */ +WIMLIBAPI int +wimlib_iterate_dir_tree(WIMStruct *wim, int image, const tchar *path, + int flags, + wimlib_iterate_dir_tree_callback_t cb, void *user_ctx) +{ + struct image_iterate_dir_tree_ctx ctx = { + .path = path, + .flags = flags, + .cb = cb, + .user_ctx = user_ctx, + }; + wim->private = &ctx; + return for_image(wim, image, image_do_iterate_dir_tree); +}