Implement wimlib_iterate_dir_tree()
authorEric Biggers <ebiggers3@gmail.com>
Thu, 23 May 2013 06:39:01 +0000 (01:39 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Thu, 23 May 2013 06:39:01 +0000 (01:39 -0500)
include/wimlib.h
programs/imagex.c
src/dentry.c

index 6585d0b..b2ac86c 100644 (file)
 #include <stddef.h>
 #include <stdbool.h>
 #include <inttypes.h>
+#include <time.h>
 
 /** Major version of the library (for example, the 1 in 1.2.5). */
 #define WIMLIB_MAJOR_VERSION 1
@@ -717,6 +718,123 @@ struct wimlib_wim_info {
        uint32_t reserved[9];
 };
 
+/** Iterate recursively on children rather than just on the specified path. */
+#define WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE 0x00000001
+
+/** Don't iterate on the file or directory itself; only its children (in the
+ * case of a non-empty directory) */
+#define WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN  0x00000002
+
+/** A stream of a file in the WIM.  */
+struct wimlib_stream_entry {
+       /** Name of the stream, or NULL if the stream is unnamed. */
+       const wimlib_tchar *stream_name;
+
+       /** Size of the stream (uncompressed) in bytes. */
+       uint64_t stream_size;
+
+       uint64_t reserved[10];
+};
+
+/** Roughly, the information about "file" in the WIM--- really a directory entry
+ * ("dentry") because hard links are allowed.  The hard_link_group_id field
+ * can be used to distinguish actual file inodes.  */
+struct wimlib_wim_dentry {
+       /** Name of the file, or NULL if this file is unnamed (only possible for
+        * the root directory) */
+       const wimlib_tchar *filename;
+
+       /** 8.3 DOS name of this file, or NULL if this file has no such name.
+        * */
+       const wimlib_tchar *dos_name;
+
+       /** Full path to this file within the WIM image.  */
+       const wimlib_tchar *full_path;
+
+       /** Depth of this directory entry, where 0 is the root, 1 is the root's
+        * children, ..., etc. */
+       size_t depth;
+
+       /** Pointer to the security descriptor for this file, in Windows
+        * SECURITY_DESCRIPTOR_RELATIVE format, or NULL if this file has no
+        * security descriptor.  */
+       const char *security_descriptor;
+
+       /** Length of the above security descriptor.  */
+       size_t security_descriptor_size;
+
+#define WIMLIB_FILE_ATTRIBUTE_READONLY            0x00000001
+#define WIMLIB_FILE_ATTRIBUTE_HIDDEN              0x00000002
+#define WIMLIB_FILE_ATTRIBUTE_SYSTEM              0x00000004
+#define WIMLIB_FILE_ATTRIBUTE_DIRECTORY           0x00000010
+#define WIMLIB_FILE_ATTRIBUTE_ARCHIVE             0x00000020
+#define WIMLIB_FILE_ATTRIBUTE_DEVICE              0x00000040
+#define WIMLIB_FILE_ATTRIBUTE_NORMAL              0x00000080
+#define WIMLIB_FILE_ATTRIBUTE_TEMPORARY           0x00000100
+#define WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE         0x00000200
+#define WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT       0x00000400
+#define WIMLIB_FILE_ATTRIBUTE_COMPRESSED          0x00000800
+#define WIMLIB_FILE_ATTRIBUTE_OFFLINE             0x00001000
+#define WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000
+#define WIMLIB_FILE_ATTRIBUTE_ENCRYPTED           0x00004000
+#define WIMLIB_FILE_ATTRIBUTE_VIRTUAL             0x00010000
+       /** File attributes, such as whether the file is a directory or not.
+        * These are the "standard" Windows FILE_ATTRIBUTE_* values, although in
+        * wimlib.h they are defined as WIMLIB_FILE_ATTRIBUTE_* for convenience
+        * on other platforms.  */
+       uint32_t attributes;
+
+#define WIMLIB_REPARSE_TAG_RESERVED_ZERO       0x00000000
+#define WIMLIB_REPARSE_TAG_RESERVED_ONE                0x00000001
+#define WIMLIB_REPARSE_TAG_MOUNT_POINT         0xA0000003
+#define WIMLIB_REPARSE_TAG_HSM                 0xC0000004
+#define WIMLIB_REPARSE_TAG_HSM2                        0x80000006
+#define WIMLIB_REPARSE_TAG_DRIVER_EXTENDER     0x80000005
+#define WIMLIB_REPARSE_TAG_SIS                 0x80000007
+#define WIMLIB_REPARSE_TAG_DFS                 0x8000000A
+#define WIMLIB_REPARSE_TAG_DFSR                        0x80000012
+#define WIMLIB_REPARSE_TAG_FILTER_MANAGER      0x8000000B
+#define WIMLIB_REPARSE_TAG_SYMLINK             0xA000000C
+       /** If the file is a reparse point (FILE_ATTRIBUTE_DIRECTORY set in the
+        * attributes), this will give the reparse tag.  This tells you whether
+        * the reparse point is a symbolic link, junction point, or some other,
+        * more unusual kind of reparse point.  */
+       uint32_t reparse_tag;
+
+       /*  Number of (hard) links to this file.  */
+       uint32_t num_links;
+
+       /** Number of named data streams that this file has.  Normally 0.  */
+       uint32_t num_named_streams;
+
+       /** Roughly, the inode number of this file.  However, it may be 0 if
+        * num_links == 1.  */
+       uint64_t hard_link_group_id;
+
+       /** Time this file was created.  */
+       struct timespec creation_time;
+
+       /** Time this file was last written to.  */
+       struct timespec last_write_time;
+
+       /** Time this file was last accessed.  */
+       struct timespec last_access_time;
+       uint64_t reserved[16];
+
+       /** Array of streams that make up this file.  The first entry will
+        * always exist and will correspond to the unnamed data stream (default
+        * file contents), so it will have stream_name == NULL.  There will then
+        * be num_named_streams additional entries that specify the named data
+        * streams, if any, each of which will have stream_name != NULL.  */
+       struct wimlib_stream_entry streams[];
+};
+
+/**
+ * Type of a callback function to wimlib_iterate_dir_tree().
+ */
+typedef int (*wimlib_iterate_dir_tree_callback_t)(const struct wimlib_wim_dentry *dentry,
+                                                 void *user_ctx);
+
 
 /*****************************
  * WIMLIB_ADD_FLAG_*
@@ -1841,6 +1959,41 @@ extern bool
 wimlib_image_name_in_use(const WIMStruct *wim, const wimlib_tchar *name);
 
 /**
+ * Iterate through a file or directory tree in the WIM image.  By specifying
+ * appropriate flags and a callback function, you can get the attributes of a
+ * file in the WIM, get a directory listing, or even get a listing of the entire
+ * WIM image.
+ *
+ * @param wim
+ *     Pointer to the ::WIMStruct for a standalone WIM file, or part 1 of a
+ *     split WIM.
+ *
+ * @param image
+ *     The 1-based number of the image in @a wim that contains the files or
+ *     directories to iterate over.
+ *
+ * @param path
+ *     Path in the WIM image at which to do the iteration.
+ *
+ * @param flags
+ *     Bitwise OR of ::WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE and/or
+ *     ::WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN.
+ *
+ * @param cb
+ *     A callback function that will receive each directory entry.
+ *
+ * @param user_ctx
+ *     An extra parameter that will always be passed to the callback function
+ *     @a cb.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+extern int
+wimlib_iterate_dir_tree(WIMStruct *wim, int image, const wimlib_tchar *path,
+                       int flags,
+                       wimlib_iterate_dir_tree_callback_t cb, void *user_ctx);
+
+/**
  * Joins a split WIM into a stand-alone one-part WIM.
  *
  * @param swms
@@ -2228,22 +2381,22 @@ wimlib_print_available_images(const WIMStruct *wim, int image);
 /** TODO: wimlib-imagex uses this for the 'dir' command, but a better API is
  * needed for this.  */
 extern int
-wimlib_print_files(WIMStruct *wim, int image);
+wimlib_print_files(WIMStruct *wim, int image) _wimlib_deprecated;
 
 /** TODO: wimlib-imagex uses this for the 'info --header' command, but a better
  * API is needed for this.  */
 extern void
-wimlib_print_header(const WIMStruct *wim);
+wimlib_print_header(const WIMStruct *wim) _wimlib_deprecated;
 
 /** TODO: wimlib-imagex uses this for the 'info --lookup-table' command, but a
  * better API is needed for this.  */
 extern void
-wimlib_print_lookup_table(WIMStruct *wim);
+wimlib_print_lookup_table(WIMStruct *wim) _wimlib_deprecated;
 
 /** TODO: wimlib-imagex uses this for the 'info --metadata' command, but a
  * better API is needed for this.  */
 extern int
-wimlib_print_metadata(WIMStruct *wim, int image);
+wimlib_print_metadata(WIMStruct *wim, int image) _wimlib_deprecated;
 
 /**
  * Deprecated in favor of wimlib_get_wim_info(), which provides the information
index 15f68d1..114424e 100644 (file)
@@ -1968,6 +1968,13 @@ out:
        return ret;
 }
 
+static int
+print_full_path(const struct wimlib_wim_dentry *wdentry, void *_ignore)
+{
+       tprintf(T("%"TS"\n"), wdentry->full_path);
+       return 0;
+}
+
 /* Print the files contained in an image(s) in a WIM file. */
 static int
 imagex_dir(int argc, tchar **argv)
@@ -2014,7 +2021,9 @@ imagex_dir(int argc, tchar **argv)
                image = 1;
        }
 
-       ret = wimlib_print_files(w, image);
+       ret = wimlib_iterate_dir_tree(w, image, T(""),
+                                     WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE,
+                                     print_full_path, NULL);
 out:
        wimlib_free(w);
        return ret;
index 47ea134..bc1a524 100644 (file)
@@ -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"
 
@@ -2255,3 +2256,201 @@ write_dentry_tree(const struct wim_dentry * restrict root, u8 * restrict p)
        /* Recursively write the rest of the dentry tree. */
        return write_dentry_tree_recursive(root, p);
 }
+
+
+static int
+init_wimlib_dentry(struct wimlib_wim_dentry *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)
+               wdentry->streams[0].stream_size = wim_resource_size(lte);
+
+       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) {
+                       wdentry->streams[wdentry->num_named_streams].stream_size =
+                                       wim_resource_size(lte);
+               }
+       #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_wim_dentry *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)
+{
+       u32 level;
+       struct wimlib_wim_dentry *wdentry;
+       int ret = WIMLIB_ERR_NOMEM;
+
+
+       wdentry = CALLOC(1, sizeof(struct wimlib_wim_dentry) +
+                                 (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);
+}
+
+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)
+{
+       int ret;
+       struct wim_dentry *dentry;
+       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);
+}