src/sha1.c \
src/sha1.h \
src/split.c \
+ src/symlink.c \
src/timestamp.h \
src/util.c \
src/util.h \
.TP
\fB--flags\fR \fIEDITIONID\fR
Specify a string to use in the <FLAGS> element of the XML data for the image.
+.TP
+\fB--dereference\fR
+Follow symlinks; archive and dump the files they point to. (The default is to
+archive the symlinks themselves)
.SH EXAMPLES
.IP
.TP
\fB--verbose\fR
Print the names of files and directories as they are captured.
+.TP
+\fB--dereference\fR
+Follow symlinks; archive and dump the files they point to. (The default is to
+archive the symlinks themselves)
.SH EXAMPLES
.IP
static const char *usage_strings[] = {
[APPEND] =
" imagex append DIRECTORY WIMFILE [\"IMAGE_NAME\"] [\"DESCRIPTION\"] [--boot]\n"
-" [--check] [--flags EDITIONID]\n",
+" [--check] [--flags EDITIONID] [--dereference]\n",
[APPLY] =
" imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all] DIRECTORY [--check]\n"
" [--hardlink] [--symlink] [--verbose]\n",
[CAPTURE] =
" imagex capture DIRECTORY WIMFILE [\"IMAGE_NAME\"] [\"DESCRIPTION\"]\n"
" l [--boot] [--check] [--compress[=TYPE]]\n"
-" [--flags \"EditionID\"] [--verbose]\n",
+" [--flags \"EditionID\"] [--verbose] [--dereference]\n",
[DELETE] =
" imagex delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check]\n",
[DIR] =
{"boot", no_argument, NULL, 'b'},
{"check", no_argument, NULL, 'c'},
{"flags", required_argument, NULL, 'f'},
+ {"dereference", no_argument, NULL, 'L'},
{NULL, 0, NULL, 0},
};
static const struct option apply_options[] = {
{"flags", required_argument, NULL, 'f'},
{"verbose", no_argument, NULL,'v'},
{"ntfs", no_argument, NULL, 'N'},
+ {"dereference", no_argument, NULL, 'L'},
{NULL, 0, NULL, 0},
};
static const struct option delete_options[] = {
case 'f':
flags_element = optarg;
break;
+ case 'L':
+ add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
+ break;
default:
usage(APPEND);
return -1;
case 'N':
add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NTFS;
break;
+ case 'L':
+ add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
+ break;
default:
usage(CAPTURE);
return -1;
/* Transfers file attributes from a `stat' buffer to a struct dentry. */
void stbuf_to_dentry(const struct stat *stbuf, struct dentry *dentry)
{
- if (S_ISDIR(stbuf->st_mode))
+ if (S_ISLNK(stbuf->st_mode)) {
+ dentry->attributes = FILE_ATTRIBUTE_REPARSE_POINT;
+ dentry->reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK;
+ } else if (S_ISDIR(stbuf->st_mode)) {
dentry->attributes = FILE_ATTRIBUTE_DIRECTORY;
- else
+ } else {
dentry->attributes = FILE_ATTRIBUTE_NORMAL;
+ }
}
/* Transfers file attributes from a struct dentry to a `stat' buffer. */
printf("Subdir offset = %"PRIu64"\n", dentry->subdir_offset);
/*printf("Unused1 = 0x%"PRIu64"\n", dentry->unused1);*/
/*printf("Unused2 = %"PRIu64"\n", dentry->unused2);*/
- printf("Creation Time = %"PRIu64"\n", dentry->creation_time);
- printf("Last Access Time = %"PRIu64"\n", dentry->last_access_time);
- printf("Last Write Time = %"PRIu64"\n", dentry->last_write_time);
printf("Creation Time = 0x%"PRIx64"\n", dentry->creation_time);
- printf("Hash = ");
+ printf("Last Access Time = 0x%"PRIx64"\n", dentry->last_access_time);
+ printf("Last Write Time = 0x%"PRIx64"\n", dentry->last_write_time);
+ printf("Hash = 0x");
print_hash(dentry->hash);
putchar('\n');
printf("Reparse Tag = 0x%"PRIx32"\n", dentry->reparse_tag);
printf("Name = \"%s\"\n", dentry->ads_entries[i].stream_name_utf8);
printf("Name Length (UTF-16) = %u\n",
dentry->ads_entries[i].stream_name_len);
+ printf("Hash = 0x");
+ print_hash(dentry->ads_entries[i].hash);
+ putchar('\n');
lte = lookup_resource(lookup_table, dentry->ads_entries[i].hash);
if (lte)
print_lookup_table_entry(lte, NULL);
return dentry;
}
+static void dentry_free_ads_entries(struct dentry *dentry)
+{
+ for (u16 i = 0; i < dentry->num_ads; i++) {
+ FREE(dentry->ads_entries[i].stream_name);
+ FREE(dentry->ads_entries[i].stream_name_utf8);
+ }
+ FREE(dentry->ads_entries);
+ dentry->ads_entries = NULL;
+ dentry->num_ads = 0;
+}
+
void free_dentry(struct dentry *dentry)
{
FREE(dentry->file_name_utf8);
FREE(dentry->short_name);
FREE(dentry->full_path_utf8);
+ dentry_free_ads_entries(dentry);
FREE(dentry);
}
static u8 *write_dentry(const struct dentry *dentry, u8 *p)
{
u8 *orig_p = p;
+ unsigned padding;
memset(p, 0, dentry->length);
p = put_u64(p, dentry->length);
p = put_u32(p, dentry->attributes);
p = put_bytes(p, dentry->file_name_len, (u8*)dentry->file_name);
p = put_u16(p, 0); /* filename padding, 2 bytes. */
p = put_bytes(p, dentry->short_name_len, (u8*)dentry->short_name);
+
+ wimlib_assert(p - orig_p <= dentry->length);
+ if (p - orig_p < dentry->length)
+ p = put_zeroes(p, dentry->length - (p - orig_p));
+
+ p = put_zeroes(p, (8 - (p - orig_p) % 8) % 8);
+
for (u16 i = 0; i < dentry->num_ads; i++) {
p = put_u64(p, ads_entry_length(&dentry->ads_entries[i]));
p = put_u64(p, 0); /* Unused */
p = put_u16(p, dentry->ads_entries[i].stream_name_len);
p = put_bytes(p, dentry->ads_entries[i].stream_name_len,
(u8*)dentry->ads_entries[i].stream_name);
+ p = put_zeroes(p, (8 - (p - orig_p) % 8) % 8);
}
- return orig_p + dentry->length;
+ return p;
}
/* Recursive function that writes a dentry tree rooted at @tree, not including
dentry->children = first_child;
return ret;
}
+
+int dentry_set_symlink_buf(struct dentry *dentry, const u8 symlink_buf_hash[])
+{
+ struct ads_entry *ads_entries;
+
+ ads_entries = CALLOC(2, sizeof(struct ads_entry));
+ if (!ads_entries)
+ return WIMLIB_ERR_NOMEM;
+ memcpy(ads_entries[1].hash, symlink_buf_hash, WIM_HASH_SIZE);
+ dentry_free_ads_entries(dentry);
+ dentry->num_ads = 2;
+ dentry->ads_entries = ads_entries;
+ return 0;
+}
#include "config.h"
#include <string.h>
+struct stat;
+struct lookup_table;
+typedef struct WIMStruct WIMStruct;
+
/* Size of the struct dentry up to and including the file_name_len. */
#define WIM_DENTRY_DISK_SIZE 102
#define WIM_ADS_ENTRY_DISK_SIZE 38
+#ifndef WIM_HASH_SIZE
+#define WIM_HASH_SIZE 20
+#endif
+
/*
* Reparse tags documented at
* http://msdn.microsoft.com/en-us/library/dd541667(v=prot.10).aspx
- *
- * IO_REPARSE_TAG_SYMLINK is the only one we really care about.
*/
#define WIM_IO_REPARSE_TAG_RESERVED_ZERO 0x00000000
#define WIM_IO_REPARSE_TAG_RESERVED_ONE 0x00000001
extern u8 *write_dentry_tree(const struct dentry *tree, u8 *p);
+extern int dentry_set_symlink_buf(struct dentry *dentry,
+ const u8 symlink_buf_hash[]);
+
/* Inline utility functions for WIMDentries */
/*
return (dentry->attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}
+/* For our purposes, we consider "real" symlinks and "junction points" to both
+ * be symlinks. */
static inline bool dentry_is_symlink(const struct dentry *dentry)
{
return (dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT)
- && (dentry->reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK);
+ && ((dentry->reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK) ||
+ dentry->reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
}
static inline bool dentry_is_regular_file(const struct dentry *dentry)
{
- return !dentry_is_directory(dentry);
+ return !dentry_is_directory(dentry) && !dentry_is_symlink(dentry);
}
static inline bool dentry_is_empty_directory(const struct dentry *dentry)
return ret;
}
+static int extract_symlink(const struct dentry *dentry, const char *output_path,
+ const WIMStruct *w)
+{
+ char target[4096];
+ ssize_t ret = dentry_readlink(dentry, target, sizeof(target), w);
+ if (ret <= 0) {
+ ERROR("Could not read the symbolic link from dentry `%s'",
+ dentry->full_path_utf8);
+ return WIMLIB_ERR_INVALID_DENTRY;
+ }
+ ret = symlink(target, output_path);
+ if (ret != 0) {
+ ERROR_WITH_ERRNO("Failed to symlink `%s' to `%s'",
+ output_path, target);
+ return WIMLIB_ERR_LINK;
+ }
+ return 0;
+}
+
/*
* Extracts a directory from the WIM archive.
*
* @dentry: The dentry to extract.
* @arg: A pointer to the WIMStruct for the WIM file.
*/
-static int extract_regular_file_or_directory(struct dentry *dentry, void *arg)
+static int extract_dentry(struct dentry *dentry, void *arg)
{
struct extract_args *args = arg;
WIMStruct *w = args->w;
int extract_flags = args->extract_flags;
size_t len = strlen(w->output_dir);
char output_path[len + dentry->full_path_utf8_len + 1];
+ int ret = 0;
if (extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE)
puts(dentry->full_path_utf8);
memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len);
output_path[len + dentry->full_path_utf8_len] = '\0';
- if (dentry_is_regular_file(dentry)) {
- return extract_regular_file(w, dentry, output_path, extract_flags);
+ if (dentry_is_symlink(dentry)) {
+ ret = extract_symlink(dentry, output_path, w);
+ } else if (dentry_is_directory(dentry)) {
+ if (!dentry_is_root(dentry)) /* Root doesn't need to be extracted. */
+ ret = extract_directory(dentry, output_path);
} else {
- if (dentry_is_root(dentry)) /* Root doesn't need to be extracted. */
- return 0;
- else
- return extract_directory(dentry, output_path);
+ ret = extract_regular_file(w, dentry, output_path, extract_flags);
}
}
#endif
};
- return for_dentry_in_tree(wim_root_dentry(w),
- extract_regular_file_or_directory, &args);
+ return for_dentry_in_tree(wim_root_dentry(w), extract_dentry, &args);
}
return p + num_bytes;
}
+static inline u8 *put_zeroes(u8 *p, size_t num_bytes)
+{
+ memset(p, 0, num_bytes);
+ return p + num_bytes;
+}
+
static inline u8 *put_bytes(u8 *p, size_t num_bytes, const u8 *input)
{
memcpy(p, input, num_bytes);
for_lookup_table_entry(w->lookup_table,
print_lookup_table_entry, NULL);
}
+
+/*struct lookup_table_entry *lookup_resource(const struct lookup_table *table,*/
+ /*const char *path, int lookup_flags)*/
+/*{*/
+ /*return lookup_resource(w->lookup_table, dentry->hash);*/
+/*}*/
/* Size of each lookup table entry in the WIM file. */
#define WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE 50
+#define LOOKUP_FLAG_ADS_OK
+
/* A lookup table that is used to translate the hash codes of dentries into the
* offsets and sizes of uncompressed or compressed file resources. It is
/* Number of times this lookup table entry is referenced by dentries. */
u32 refcnt;
+ /* If %true, this lookup table entry corresponds to a symbolic link
+ * reparse buffer. @symlink_reparse_data_buf will give the target of
+ * the symbolic link. */
+ bool is_symlink;
+
union {
/* SHA1 hash of the file resource pointed to by this lookup
* table entry */
union {
char *file_on_disk;
char *staging_file_name;
+ void *symlink_buf;
struct lookup_table_entry *next_lte_in_swm;
};
return lookup_resource(w->lookup_table, dentry->hash);
}
+
extern int zero_out_refcnts(struct lookup_table_entry *entry, void *ignore);
extern int print_lookup_table_entry(struct lookup_table_entry *entry, void *ignore);
#include <dirent.h>
#include <string.h>
#include <errno.h>
+#include <unistd.h>
+
+/** Private flag: Used to mark that we currently adding the root directory of
+ * the WIM. */
+#define WIMLIB_ADD_IMAGE_FLAG_ROOT 0x80000000
static void destroy_image_metadata(struct image_metadata *imd,
struct lookup_table *lt)
* to the WIM may still occur later when trying to actually read
* the regular files in the tree into the WIM as file resources.
*/
-static int build_dentry_tree(struct dentry *root, const char *source_path,
- struct stat *root_stat,
- struct lookup_table* lookup_table)
+static int build_dentry_tree(struct dentry *root, const char *root_disk_path,
+ struct lookup_table* lookup_table,
+ int add_flags)
{
- int ret = 0;
+ DEBUG("`%s'", root_disk_path);
+ struct stat root_stbuf;
+ int ret;
+ int (*stat_fn)(const char *restrict, struct stat *restrict);
+
+ if (add_flags & WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)
+ stat_fn = stat;
+ else
+ stat_fn = lstat;
+
+
+ ret = (*stat_fn)(root_disk_path, &root_stbuf);
+ if (ret != 0) {
+ ERROR_WITH_ERRNO("Failed to stat `%s'", root_disk_path);
+ return WIMLIB_ERR_STAT;
+ }
+
+ if ((add_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) &&
+ !S_ISDIR(root_stbuf.st_mode)) {
+ ERROR("`%s' is not a directory", root_disk_path);
+ return WIMLIB_ERR_NOTDIR;
+ }
+ stbuf_to_dentry(&root_stbuf, root);
+ add_flags &= ~WIMLIB_ADD_IMAGE_FLAG_ROOT;
- stbuf_to_dentry(root_stat, root);
if (dentry_is_directory(root)) {
/* Open the directory on disk */
DIR *dir;
struct dirent *p;
- struct stat child_stat;
struct dentry *child;
- dir = opendir(source_path);
+ dir = opendir(root_disk_path);
if (!dir) {
ERROR_WITH_ERRNO("Failed to open the directory `%s'",
- source_path);
+ root_disk_path);
return WIMLIB_ERR_OPEN;
}
/* Buffer for names of files in directory. */
- size_t len = strlen(source_path);
+ size_t len = strlen(root_disk_path);
char name[len + 1 + FILENAME_MAX + 1];
- memcpy(name, source_path, len);
+ memcpy(name, root_disk_path, len);
name[len] = '/';
/* Create a dentry for each entry in the directory on disk, and recurse
|| (p->d_name[1] == '.' && p->d_name[2] == '\0')))
continue;
strcpy(name + len + 1, p->d_name);
- if (stat(name, &child_stat) != 0) {
- ERROR_WITH_ERRNO("Cannot stat `%s'", name);
- ret = WIMLIB_ERR_STAT;
- break;
- }
child = new_dentry(p->d_name);
if (!child) {
ERROR("No memory to allocate new dentry");
return WIMLIB_ERR_NOMEM;
}
- ret = build_dentry_tree(child, name, &child_stat,
- lookup_table);
+ ret = build_dentry_tree(child, name, lookup_table,
+ add_flags);
link_dentry(child, root);
if (ret != 0)
break;
}
closedir(dir);
+ } else if (dentry_is_symlink(root)) {
+ /* Archiving a symbolic link */
+ size_t symlink_buf_len;
+ char deref_name_buf[4096];
+ ssize_t ret = readlink(root_disk_path, deref_name_buf,
+ sizeof(deref_name_buf) - 1);
+ if (ret == -1) {
+ ERROR_WITH_ERRNO("Failed to read target of "
+ "symbolic link `%s'", root_disk_path);
+ return WIMLIB_ERR_STAT;
+ }
+ deref_name_buf[ret] = '\0';
+ DEBUG("Read symlink `%s'", deref_name_buf);
+ void *symlink_buf = make_symlink_reparse_data_buf(deref_name_buf,
+ &symlink_buf_len);
+ if (!symlink_buf)
+ return WIMLIB_ERR_NOMEM;
+ DEBUG("Made symlink reparse data buf (len = %zu, name len = %zu)",
+ symlink_buf_len, ret);
+
+ u8 symlink_buf_hash[WIM_HASH_SIZE];
+ sha1_buffer(symlink_buf, symlink_buf_len, symlink_buf_hash);
+
+ ret = dentry_set_symlink_buf(root, symlink_buf_hash);
+
+ if (ret != 0) {
+ FREE(symlink_buf);
+ return ret;
+ }
+ DEBUG("Created symlink buf");
+
+ struct lookup_table_entry *lte;
+ struct lookup_table_entry *existing_lte;
+
+ existing_lte = lookup_resource(lookup_table, symlink_buf_hash);
+ if (existing_lte) {
+ existing_lte->refcnt++;
+ } else {
+ DEBUG("Creating new lookup table entry");
+ lte = new_lookup_table_entry();
+ if (!lte) {
+ FREE(symlink_buf);
+ return WIMLIB_ERR_NOMEM;
+ }
+ lte->symlink_buf = symlink_buf;
+ lte->resource_entry.original_size = symlink_buf_len;
+ lte->resource_entry.size = symlink_buf_len;
+ lte->is_symlink = true;
+ memcpy(lte->hash, symlink_buf_hash, WIM_HASH_SIZE);
+ lookup_table_insert(lookup_table, lte);
+ }
} else {
struct lookup_table_entry *lte;
* in the lookup table already; if it is, we increment its
* refcnt; otherwise, we create a new lookup table entry and
* insert it. */
- ret = sha1sum(source_path, root->hash);
+ ret = sha1sum(root_disk_path, root->hash);
if (ret != 0)
return ret;
if (lte) {
lte->refcnt++;
} else {
- char *file_on_disk = STRDUP(source_path);
+ char *file_on_disk = STRDUP(root_disk_path);
if (!file_on_disk) {
ERROR("Failed to allocate memory for file path");
return WIMLIB_ERR_NOMEM;
return WIMLIB_ERR_NOMEM;
}
lte->file_on_disk = file_on_disk;
- lte->resource_entry.flags = 0;
- lte->refcnt = 1;
- lte->part_number = 1;
- lte->resource_entry.original_size = root_stat->st_size;
- lte->resource_entry.size = root_stat->st_size;
+ lte->resource_entry.original_size = root_stbuf.st_size;
+ lte->resource_entry.size = root_stbuf.st_size;
memcpy(lte->hash, root->hash, WIM_HASH_SIZE);
lookup_table_insert(lookup_table, lte);
}
root_dentry->attributes |= FILE_ATTRIBUTE_DIRECTORY;
- /* Construct the dentry tree from the outside filesystem. */
- if (stat(dir, &root_stat) != 0) {
- ERROR_WITH_ERRNO("Failed to stat `%s'", dir);
- ret = WIMLIB_ERR_STAT;
- goto out_free_dentry_tree;
- }
- if (!S_ISDIR(root_stat.st_mode)) {
- ERROR("`%s' is not a directory", dir);
- ret = WIMLIB_ERR_NOTDIR;
- goto out_free_dentry_tree;
- }
DEBUG("Building dentry tree.");
- ret = build_dentry_tree(root_dentry, dir, &root_stat, w->lookup_table);
+ ret = build_dentry_tree(root_dentry, dir, w->lookup_table,
+ flags | WIMLIB_ADD_IMAGE_FLAG_ROOT);
if (ret != 0) {
ERROR("Failed to build dentry tree for `%s'", dir);
dentry = get_dentry(w, path);
if (!dentry)
- return -EEXIST;
+ return -ENOENT;
if (dentry_is_directory(dentry))
return -EISDIR;
lte = wim_lookup_resource(w, dentry);
return 0;
}
-/*
- * Find the symlink target of a symbolic link dentry in the WIM.
- *
- * See http://msdn.microsoft.com/en-us/library/cc232006(v=prot.10).aspx
- * Except the first 8 bytes aren't included in the resource (presumably because
- * we already know the reparse tag from the dentry, and we already know the
- * reparse tag len from the lookup table entry resource length).
- */
-static int get_symlink_name(const u8 *resource, size_t resource_len,
- char *buf, size_t buf_len)
-{
- const u8 *p = resource;
- u16 substitute_name_offset;
- u16 substitute_name_len;
- u16 print_name_offset;
- u16 print_name_len;
- u32 flags;
- char *link_target;
- size_t link_target_len;
- int ret;
- if (resource_len < 12)
- return -EIO;
- p = get_u16(p, &substitute_name_offset);
- p = get_u16(p, &substitute_name_len);
- p = get_u16(p, &print_name_offset);
- p = get_u16(p, &print_name_len);
- p = get_u32(p, &flags);
- if (12 + substitute_name_offset + substitute_name_len > resource_len)
- return -EIO;
- link_target = utf16_to_utf8(p + substitute_name_offset,
- substitute_name_len,
- &link_target_len);
- if (!link_target)
- return -EIO;
- if (link_target_len + 1 > buf_len) {
- ret = -ENAMETOOLONG;
- goto out;
- }
- memcpy(buf, link_target, link_target_len + 1);
- ret = 0;
-out:
- free(link_target);
- return ret;
-}
static int wimfs_readlink(const char *path, char *buf, size_t buf_len)
{
struct dentry *dentry = get_dentry(w, path);
- struct ads_entry *ads;
- struct lookup_table_entry *entry;
- struct resource_entry *res_entry;
+ int ret;
if (!dentry)
return -ENOENT;
if (!dentry_is_symlink(dentry))
return -EINVAL;
- /*
- * This is of course not actually documented, but what I think is going
- * on here is that the symlink dentries have 2 alternate data streams;
- * one is the default data stream, which is not used and is empty, and
- * one is the symlink buffer data stream, which is confusingly also
- * unnamed, but isn't empty as it contains the symlink target within the
- * resource.
- */
- if (dentry->num_ads != 2)
- return -EIO;
- if ((entry = lookup_resource(w->lookup_table, dentry->ads_entries[0].hash)))
- goto do_readlink;
- if ((entry = lookup_resource(w->lookup_table, dentry->ads_entries[1].hash)))
- goto do_readlink;
- return -EIO;
-do_readlink:
- res_entry = &entry->resource_entry;
- char res_buf[res_entry->original_size];
- if (read_full_resource(w->fp, res_entry->size,
- res_entry->original_size,
- res_entry->offset,
- wim_resource_compression_type(w, res_entry),
- res_buf) != 0)
- return -EIO;
- return get_symlink_name(res_buf, res_entry->original_size, buf, buf_len);
+ ret = dentry_readlink(dentry, buf, buf_len, w);
+ if (ret > 0)
+ ret = 0;
+ return ret;
}
/* Close a file. */
return 0;
}
-/*
- * Writes a file resource to the output file.
- *
- * @dentry: The dentry for the file resource.
- * @wim_p: A pointer to the WIMStruct. The fields of interest to this
- * function are the input and output file streams and the lookup
- * table.
- *
- * @return zero on success, nonzero on failure.
- */
-int write_file_resource(struct dentry *dentry, void *wim_p)
+static int write_file_resource(WIMStruct *w, const u8 hash[])
{
- WIMStruct *w;
- FILE *out_fp;
- FILE *in_fp;
- struct lookup_table_entry *lte;
- int in_wim_ctype;
- int out_wim_ctype;
- struct resource_entry *output_res_entry;
- u64 len;
- int ret;
-
- w = wim_p;
- out_fp = w->out_fp;
-
- /* Directories don't need file resources. */
- if (dentry_is_directory(dentry))
- return 0;
-
/* Get the lookup entry for the file resource. */
- lte = wim_lookup_resource(w, dentry);
+ struct lookup_table_entry *lte;
+
+ lte = lookup_resource(w->lookup_table, hash);
if (!lte)
return 0;
if (++lte->out_refcnt != 1)
return 0;
- out_wim_ctype = wimlib_get_compression_type(w);
- output_res_entry = <e->output_resource_entry;
-
/* do not write empty resources */
if (lte->resource_entry.original_size == 0)
return 0;
+ int out_wim_ctype = wimlib_get_compression_type(w);
+ struct resource_entry *output_res_entry = <e->output_resource_entry;
+ u64 len;
+ FILE *in_fp;
+ FILE *out_fp = w->out_fp;
+ int ret = 0;
+
/* Figure out if we can read the resource from the WIM file, or
- * if we have to read it from the filesystem outside. */
- if (lte->file_on_disk) {
+ * if we have to read it from the filesystem outside, or if it's a
+ * symbolic link with the data already in memory pointed to by a field
+ * of the lookup table entry. */
+ if (lte->is_symlink) {
+ off_t offset = ftello(w->out_fp);
+ u64 new_size;
+
+ if (offset == -1) {
+ ERROR_WITH_ERRNO("Could not get position in output "
+ "file");
+ return WIMLIB_ERR_WRITE;
+ }
+
+ wimlib_assert(lte->symlink_buf);
+
+ len = lte->resource_entry.original_size;
+
+ recompress_resource(in_fp, lte->symlink_buf, len, len, 0,
+ 0, out_fp, out_wim_ctype, &new_size);
+ output_res_entry->size = new_size;
+ output_res_entry->original_size = len;
+ output_res_entry->offset = offset;
+ output_res_entry->flags = (out_wim_ctype == WIM_COMPRESSION_TYPE_NONE)
+ ? 0 : WIM_RESHDR_FLAG_COMPRESSED;
+ } else if (lte->file_on_disk) {
/* Read from disk (uncompressed) */
out_wim_ctype, output_res_entry);
fclose(in_fp);
} else {
+ int in_wim_ctype;
/* Read from input WIM (possibly compressed) */
}
return ret;
}
+
+/*
+ * Writes a dentry's resources to the output file.
+ *
+ * @dentry: The dentry for the file resource.
+ * @wim_p: A pointer to the WIMStruct. The fields of interest to this
+ * function are the input and output file streams and the lookup
+ * table, and the alternate data streams.
+ *
+ * @return zero on success, nonzero on failure.
+ */
+int write_dentry_resources(struct dentry *dentry, void *wim_p)
+{
+ WIMStruct *w = wim_p;
+ int ret;
+
+ /* Directories don't need file resources. */
+ if (dentry_is_directory(dentry))
+ return 0;
+
+ ret = write_file_resource(w, dentry->hash);
+ if (ret != 0)
+ return ret;
+ for (u16 i = 0; i < dentry->num_ads; i++) {
+ ret = write_file_resource(w, dentry->ads_entries[i].hash);
+ if (ret != 0)
+ return ret;
+ }
+ return 0;
+}
+
--- /dev/null
+#include "dentry.h"
+#include "io.h"
+#include "lookup_table.h"
+
+/*
+ * Find the symlink target of a symbolic link or junction point in the WIM.
+ *
+ * See http://msdn.microsoft.com/en-us/library/cc232006(v=prot.10).aspx
+ * Except the first 8 bytes aren't included in the resource (presumably because
+ * we already know the reparse tag from the dentry, and we already know the
+ * reparse tag len from the lookup table entry resource length).
+ */
+static ssize_t get_symlink_name(const u8 *resource, size_t resource_len,
+ char *buf, size_t buf_len,
+ bool is_junction_point)
+{
+ const u8 *p = resource;
+ u16 substitute_name_offset;
+ u16 substitute_name_len;
+ u16 print_name_offset;
+ u16 print_name_len;
+ char *link_target;
+ size_t link_target_len;
+ ssize_t ret;
+ unsigned header_size;
+ char *translated_target;
+ bool is_absolute;
+
+ if (resource_len < 12)
+ return -EIO;
+ p = get_u16(p, &substitute_name_offset);
+ p = get_u16(p, &substitute_name_len);
+ p = get_u16(p, &print_name_offset);
+ p = get_u16(p, &print_name_len);
+ if (is_junction_point) {
+ header_size = 8;
+ } else {
+ u32 flags;
+ p = get_u32(p, &flags);
+ is_absolute = (flags & 1) ? false : true;
+ header_size = 12;
+ }
+ if (header_size + substitute_name_offset + substitute_name_len > resource_len)
+ return -EIO;
+ link_target = utf16_to_utf8(p + substitute_name_offset,
+ substitute_name_len,
+ &link_target_len);
+
+ if (!link_target)
+ return -EIO;
+
+ if (link_target_len + 1 > buf_len) {
+ ret = -ENAMETOOLONG;
+ goto out;
+ }
+
+ translated_target = link_target;
+ if (is_junction_point || is_absolute) {
+ if (link_target_len < 7
+ || memcmp(translated_target, "\\??\\", 4) != 0
+ || translated_target[4] == '\0'
+ || translated_target[5] != ':'
+ || translated_target[6] != '\\') {
+ ret = -EIO;
+ goto out;
+ }
+ translated_target += 4;
+ link_target_len -= 4;
+ }
+ for (size_t i = 0; i < link_target_len; i++)
+ if (translated_target[i] == '\\')
+ translated_target[i] = '/';
+
+ memcpy(buf, translated_target, link_target_len + 1);
+ ret = link_target_len;
+out:
+ FREE(link_target);
+ return ret;
+}
+
+void *make_symlink_reparse_data_buf(const char *symlink_target, size_t *len_ret)
+{
+ size_t utf8_len = strlen(symlink_target);
+ size_t utf16_len;
+ char *name_utf16 = utf8_to_utf16(symlink_target, utf8_len, &utf16_len);
+ if (!name_utf16)
+ return NULL;
+ /*DEBUG("utf16_len = %zu", utf16_len);*/
+ for (size_t i = 0; i < utf16_len / 2; i++)
+ if (((u16*)name_utf16)[i] == to_le16('/'))
+ ((u16*)name_utf16)[i] = to_le16('\\');
+ size_t len = 12 + utf16_len * 2;
+ void *buf = MALLOC(len);
+ if (!buf)
+ goto out;
+
+ u8 *p = buf;
+ p = put_u16(p, 0); /* Substitute name offset */
+ p = put_u16(p, utf16_len); /* Substitute name length */
+ p = put_u16(p, utf16_len); /* Print name offset */
+ p = put_u16(p, utf16_len); /* Print name length */
+ p = put_u32(p, (symlink_target[0] == '/') ? 0 : 1);
+ p = put_bytes(p, utf16_len, name_utf16);
+ p = put_bytes(p, utf16_len, name_utf16);
+ /*DEBUG("utf16_len = %zu, len = %zu", utf16_len, len);*/
+ *len_ret = len;
+out:
+ FREE(name_utf16);
+ return buf;
+}
+
+/* Get the symlink target from a dentry that's already checked to be either a
+ * "real" symlink or a junction point. */
+ssize_t dentry_readlink(const struct dentry *dentry, char *buf, size_t buf_len,
+ const WIMStruct *w)
+{
+ struct ads_entry *ads;
+ struct lookup_table_entry *entry;
+ struct resource_entry *res_entry;
+ bool is_junction_point;
+
+ wimlib_assert(dentry_is_symlink(dentry));
+
+ if (dentry->reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK) {
+ is_junction_point = false;
+ /*
+ * This is of course not actually documented, but what I think is going
+ * on here is that the symlink dentries have 2 alternate data streams;
+ * one is the default data stream, which is not used and is empty, and
+ * one is the symlink buffer data stream, which is confusingly also
+ * unnamed, but isn't empty as it contains the symlink target within the
+ * resource.
+ */
+ if (dentry->num_ads != 2)
+ return -EIO;
+ if ((entry = lookup_resource(w->lookup_table, dentry->ads_entries[0].hash)))
+ goto do_readlink;
+ if ((entry = lookup_resource(w->lookup_table, dentry->ads_entries[1].hash)))
+ goto do_readlink;
+ } else {
+ wimlib_assert(dentry->reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
+
+ is_junction_point = true;
+
+ if ((entry = lookup_resource(w->lookup_table, dentry->hash)))
+ goto do_readlink;
+ }
+ return -EIO;
+do_readlink:
+ res_entry = &entry->resource_entry;
+ if (res_entry->original_size > 10000)
+ return -EIO;
+ char res_buf[res_entry->original_size];
+ if (read_full_resource(w->fp, res_entry->size,
+ res_entry->original_size,
+ res_entry->offset,
+ wim_resource_compression_type(w, res_entry),
+ res_buf) != 0)
+ return -EIO;
+ return get_symlink_name(res_buf, res_entry->original_size, buf,
+ buf_len, is_junction_point);
+}
/* Converts a string in the UTF-8 encoding to a newly allocated string in the
* UTF-16 encoding. */
-char *utf8_to_utf16(const char *utf8_str, size_t utf8_len,
- size_t *utf16_len_ret)
+char *utf8_to_utf16(const char *utf8_str, size_t utf8_len,
+ size_t *utf16_len_ret)
{
if (cd_utf8_to_utf16 == (iconv_t)(-1)) {
cd_utf8_to_utf16 = iconv_open("UTF-16LE", "UTF-8");
* mounted with NTFS-3g, and wimlib was compiled with support for NTFS-3g */
#define WIMLIB_ADD_IMAGE_FLAG_NTFS 0x00000004
+/** Follow symlinks; archive and dump the files they point to. */
+#define WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE 0x00000008
+
/** See documentation for wimlib_export_image(). */
#define WIMLIB_EXPORT_FLAG_BOOT 0x00000001
struct stat;
+#ifndef WIM_HASH_SIZE
#define WIM_HASH_SIZE 20
+#endif
+
#define WIM_MAGIC_LEN 8
#define WIM_GID_LEN 16
#define WIM_UNUSED_LEN 60
* of this size. */
#define WIM_CHUNK_SIZE 32768
-/* Version of the WIM file. I don't know if there has ever been a different
- * version or not. */
+/* Version of the WIM file. There is an older version, but we don't support it
+ * yet. The differences between the versions are undocumented. */
#define WIM_VERSION 0x10d00
enum wim_integrity_status {
resource_original_size, 0, contents_ret);
}
-extern int write_file_resource(struct dentry *dentry, void *wim_p);
+extern int write_dentry_resources(struct dentry *dentry, void *wim_p);
extern int copy_resource(struct lookup_table_entry *lte, void *w);
extern int copy_between_files(FILE *in, off_t in_offset, FILE *out, size_t len);
extern int write_resource_from_memory(const u8 resource[], int out_ctype,
u64 *resource_size_ret);
extern int write_metadata_resource(WIMStruct *w);
+
/* security.c */
int read_security_data(const u8 metadata_resource[],
u64 metadata_resource_len, struct wim_security_data **sd_p);
u8 *write_security_data(const struct wim_security_data *sd, u8 *p);
void free_security_data(struct wim_security_data *sd);
+/* symlink.c */
+ssize_t dentry_readlink(const struct dentry *dentry, char *buf, size_t buf_len,
+ const WIMStruct *w);
+extern void *make_symlink_reparse_data_buf(const char *symlink_target,
+ size_t *len_ret);
+
/* wim.c */
extern WIMStruct *new_wim_struct();
extern int wimlib_select_image(WIMStruct *w, int image);
{
DEBUG("Writing file resources for image %u.", w->current_image);
- return for_dentry_in_tree(wim_root_dentry(w), write_file_resource, w);
+ return for_dentry_in_tree(wim_root_dentry(w), write_dentry_resources, w);
}
/* Write the lookup table, xml data, and integrity table, then overwrite the WIM