*/
/*
- * Copyright (C) 2012 Eric Biggers
+ * Copyright (C) 2012, 2013 Eric Biggers
*
* This file is part of wimlib, a library for working with WIM files.
*
#include <ntfs-3g/reparse.h>
#include <ntfs-3g/xattrs.h>
#include <string.h>
+#include <locale.h>
-static int extract_wim_chunk_to_ntfs_attr(const u8 *buf, size_t len,
- u64 offset, void *arg)
+struct ntfs_attr_extract_ctx {
+ u64 offset;
+ ntfs_attr *na;
+};
+
+static int
+extract_wim_chunk_to_ntfs_attr(const void *buf, size_t len, void *_ctx)
{
- ntfs_attr *na = arg;
- if (ntfs_attr_pwrite(na, offset, len, buf) == len) {
+ struct ntfs_attr_extract_ctx *ctx = _ctx;
+ if (ntfs_attr_pwrite(ctx->na, ctx->offset, len, buf) == len) {
+ ctx->offset += len;
return 0;
} else {
ERROR_WITH_ERRNO("Error extracting WIM resource to NTFS attribute");
extract_wim_resource_to_ntfs_attr(const struct wim_lookup_table_entry *lte,
ntfs_attr *na)
{
+ struct ntfs_attr_extract_ctx ctx;
+ ctx.na = na;
+ ctx.offset = 0;
return extract_wim_resource(lte, wim_resource_size(lte),
- extract_wim_chunk_to_ntfs_attr, na);
+ extract_wim_chunk_to_ntfs_attr, &ctx);
}
/* Writes the data streams of a WIM inode to the data attributes of a NTFS
*
* Returns 0 on success, nonzero on failure.
*/
-static int write_ntfs_data_streams(ntfs_inode *ni, const struct wim_dentry *dentry,
- union wimlib_progress_info *progress_info)
+static int
+write_ntfs_data_streams(ntfs_inode *ni, struct wim_dentry *dentry,
+ union wimlib_progress_info *progress_info)
{
int ret = 0;
unsigned stream_idx = 0;
ntfschar *stream_name = AT_UNNAMED;
- u32 stream_name_len = 0;
+ u32 stream_name_nbytes = 0;
const struct wim_inode *inode = dentry->d_inode;
struct wim_lookup_table_entry *lte;
+ lte = inode->i_lte;
+
+ /* For directories, skip unnamed streams; just extract alternate data
+ * streams. */
+ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
+ goto cont;
+
DEBUG("Writing %u NTFS data stream%s for `%s'",
inode->i_num_ads + 1,
(inode->i_num_ads == 0 ? "" : "s"),
- dentry->full_path_utf8);
+ dentry->_full_path);
+
+ for (;;) {
+ if (stream_name_nbytes) {
+ /* Skip special UNIX data entries (see documentation for
+ * WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) */
+ if (stream_name_nbytes == WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES
+ && !memcmp(stream_name,
+ WIMLIB_UNIX_DATA_TAG_UTF16LE,
+ WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES))
+ goto cont;
- lte = inode->i_lte;
- while (1) {
- if (stream_name_len) {
/* Create an empty named stream. */
ret = ntfs_attr_add(ni, AT_DATA, stream_name,
- stream_name_len, NULL, 0);
- if (ret != 0) {
- ERROR_WITH_ERRNO("Failed to create name data "
+ stream_name_nbytes / 2, NULL, 0);
+ if (ret) {
+ ERROR_WITH_ERRNO("Failed to create named data "
"stream for extracted file "
"`%s'",
- dentry->full_path_utf8);
+ dentry->_full_path);
ret = WIMLIB_ERR_NTFS_3G;
break;
if (lte) {
ntfs_attr *na;
- na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
+ na = ntfs_attr_open(ni, AT_DATA, stream_name,
+ stream_name_nbytes / 2);
if (!na) {
ERROR_WITH_ERRNO("Failed to open a data stream of "
"extracted file `%s'",
- dentry->full_path_utf8);
+ dentry->_full_path);
ret = WIMLIB_ERR_NTFS_3G;
break;
}
* length, so the NTFS attribute should be resized to
* this length before starting to extract the data. */
ret = ntfs_attr_truncate_solid(na, wim_resource_size(lte));
- if (ret != 0) {
+ if (ret) {
ntfs_attr_close(na);
break;
}
/* Close the attribute */
ntfs_attr_close(na);
- if (ret != 0)
+ if (ret)
break;
/* Record the number of bytes of uncompressed data that
* have been extracted. */
progress_info->extract.completed_bytes += wim_resource_size(lte);
}
+ cont:
if (stream_idx == inode->i_num_ads) /* Has the last stream been extracted? */
break;
/* Get the name and lookup table entry for the next stream. */
- stream_name = (ntfschar*)inode->i_ads_entries[stream_idx].stream_name;
- stream_name_len = inode->i_ads_entries[stream_idx].stream_name_len / 2;
+ stream_name = inode->i_ads_entries[stream_idx].stream_name;
+ stream_name_nbytes = inode->i_ads_entries[stream_idx].stream_name_nbytes;
lte = inode->i_ads_entries[stream_idx].lte;
stream_idx++;
}
/* Open the NTFS inode that corresponds to the parent of a WIM dentry. Returns
* the opened inode, or NULL on failure. */
-static ntfs_inode *dentry_open_parent_ni(const struct wim_dentry *dentry,
- ntfs_volume *vol)
+static ntfs_inode *
+dentry_open_parent_ni(struct wim_dentry *dentry, ntfs_volume *vol)
{
char *p;
const char *dir_name;
ntfs_inode *dir_ni;
char orig;
- p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
+ p = dentry->_full_path + dentry->full_path_nbytes;
do {
p--;
} while (*p != '/');
orig = *p;
*p = '\0';
- dir_name = dentry->full_path_utf8;
+ dir_name = dentry->_full_path;
dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
if (!dir_ni) {
ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
* directory specified by @dir_ni, and it is made to point to the previously
* extracted file located at @inode->i_extracted_file.
*
- * Or, in other words, this adds a new name @from_dentry->full_path_utf8 to an
+ * Or, in other words, this adds a new name @from_dentry->full_path to an
* existing NTFS inode which already has a name @inode->i_extracted_file.
*
* The new name is made in the POSIX namespace (this is the behavior of
- * ntfs_link()). I am assuming this is an acceptable behavior; however, it's
- * possible that the original name was actually in the Win32 namespace. Note
- * that the WIM format does not provide enough information to distinguish Win32
- * names from POSIX names in all cases.
+ * ntfs_link()).
*
- * Return 0 on success, nonzero on failure.
+ * Return 0 on success, nonzero on failure. dir_ni is closed either way.
*/
-static int apply_ntfs_hardlink(const struct wim_dentry *from_dentry,
- const struct wim_inode *inode,
- ntfs_inode **dir_ni_p)
+static int
+apply_ntfs_hardlink(struct wim_dentry *from_dentry,
+ const struct wim_inode *inode,
+ ntfs_inode *dir_ni)
{
int ret;
ntfs_inode *to_ni;
- ntfs_inode *dir_ni;
ntfs_volume *vol;
- dir_ni = *dir_ni_p;
vol = dir_ni->vol;
ret = ntfs_inode_close(dir_ni);
- *dir_ni_p = NULL;
if (ret != 0) {
ERROR_WITH_ERRNO("Error closing directory");
return WIMLIB_ERR_NTFS_3G;
}
DEBUG("Extracting NTFS hard link `%s' => `%s'",
- from_dentry->full_path_utf8, inode->i_extracted_file);
+ from_dentry->_full_path, inode->i_extracted_file);
to_ni = ntfs_pathname_to_inode(vol, NULL, inode->i_extracted_file);
if (!to_ni) {
}
ret = ntfs_link(to_ni, dir_ni,
- (ntfschar*)from_dentry->file_name,
- from_dentry->file_name_len / 2);
+ from_dentry->file_name,
+ from_dentry->file_name_nbytes / 2);
ret |= ntfs_inode_close(dir_ni);
ret |= ntfs_inode_close(to_ni);
if (ret) {
- ERROR_WITH_ERRNO("Could not create hard link `%s' => `%s' (ret=%d)",
- from_dentry->full_path_utf8,
- inode->i_extracted_file,
- ret);
+ ERROR_WITH_ERRNO("Could not create hard link `%s' => `%s'",
+ from_dentry->_full_path,
+ inode->i_extracted_file);
ret = WIMLIB_ERR_NTFS_3G;
}
return ret;
static int
apply_file_attributes_and_security_data(ntfs_inode *ni,
ntfs_inode *dir_ni,
- const struct wim_dentry *dentry,
- const WIMStruct *w)
+ struct wim_dentry *dentry,
+ const WIMStruct *w,
+ int extract_flags)
{
int ret;
struct SECURITY_CONTEXT ctx;
inode = dentry->d_inode;
DEBUG("Setting NTFS file attributes on `%s' to %#"PRIx32,
- dentry->full_path_utf8, inode->i_attributes);
+ dentry->_full_path, inode->i_attributes);
attributes_le32 = cpu_to_le32(inode->i_attributes);
memset(&ctx, 0, sizeof(ctx));
ni, dir_ni,
(const char*)&attributes_le32,
sizeof(u32), 0);
- if (ret != 0) {
+ if (ret) {
ERROR("Failed to set NTFS file attributes on `%s'",
- dentry->full_path_utf8);
- return WIMLIB_ERR_NTFS_3G;
- }
- if (inode->i_security_id != -1) {
+ dentry->_full_path);
+ ret = WIMLIB_ERR_NTFS_3G;
+ } else if (inode->i_security_id != -1 &&
+ !(extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS))
+ {
const char *desc;
const struct wim_security_data *sd;
wimlib_assert(inode->i_security_id < sd->num_entries);
desc = (const char *)sd->descriptors[inode->i_security_id];
DEBUG("Applying security descriptor %d to `%s'",
- inode->i_security_id, dentry->full_path_utf8);
+ inode->i_security_id, dentry->_full_path);
ret = ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ACL,
ni, dir_ni, desc,
sd->sizes[inode->i_security_id], 0);
- if (ret != 0) {
+ if (ret) {
ERROR_WITH_ERRNO("Failed to set security data on `%s'",
- dentry->full_path_utf8);
- return WIMLIB_ERR_NTFS_3G;
+ dentry->_full_path);
+ ret = WIMLIB_ERR_NTFS_3G;
}
}
- return 0;
+ return ret;
}
/*
* Transfers the reparse data from a WIM inode (which must represent a reparse
* point) to a NTFS inode.
*/
-static int apply_reparse_data(ntfs_inode *ni, const struct wim_dentry *dentry,
- union wimlib_progress_info *progress_info)
+static int
+apply_reparse_data(ntfs_inode *ni, struct wim_dentry *dentry,
+ union wimlib_progress_info *progress_info)
{
struct wim_lookup_table_entry *lte;
- int ret = 0;
+ int ret;
lte = inode_unnamed_lte_resolved(dentry->d_inode);
- DEBUG("Applying reparse data to `%s'", dentry->full_path_utf8);
+ DEBUG("Applying reparse data to `%s'", dentry->_full_path);
if (!lte) {
ERROR("Could not find reparse data for `%s'",
- dentry->full_path_utf8);
+ dentry->_full_path);
return WIMLIB_ERR_INVALID_DENTRY;
}
- if (wim_resource_size(lte) >= 0xffff) {
+ /* "Reparse point data, including the tag and optional GUID, cannot
+ * exceed 16 kilobytes." - MSDN */
+ if (wim_resource_size(lte) > REPARSE_POINT_MAX_SIZE - 8) {
ERROR("Reparse data of `%s' is too long (%"PRIu64" bytes)",
- dentry->full_path_utf8, wim_resource_size(lte));
+ dentry->_full_path, wim_resource_size(lte));
return WIMLIB_ERR_INVALID_DENTRY;
}
u8 reparse_data_buf[8 + wim_resource_size(lte)];
u8 *p = reparse_data_buf;
p = put_u32(p, dentry->d_inode->i_reparse_tag); /* ReparseTag */
+ DEBUG("ReparseTag = %#x", dentry->d_inode->i_reparse_tag);
p = put_u16(p, wim_resource_size(lte)); /* ReparseDataLength */
p = put_u16(p, 0); /* Reserved */
- ret = read_full_wim_resource(lte, p, 0);
- if (ret != 0)
+ ret = read_full_resource_into_buf(lte, p);
+ if (ret)
return ret;
ret = ntfs_set_ntfs_reparse_data(ni, (char*)reparse_data_buf,
wim_resource_size(lte) + 8, 0);
- if (ret != 0) {
+ if (ret) {
ERROR_WITH_ERRNO("Failed to set NTFS reparse data on `%s'",
- dentry->full_path_utf8);
+ dentry->_full_path);
return WIMLIB_ERR_NTFS_3G;
+ } else {
+ progress_info->extract.completed_bytes += wim_resource_size(lte);
}
- progress_info->extract.completed_bytes += wim_resource_size(lte);
- return 0;
+ return ret;
}
/*
*
* @return: 0 on success; nonzero on failure.
*/
-static int do_apply_dentry_ntfs(struct wim_dentry *dentry, ntfs_inode *dir_ni,
- struct apply_args *args)
+static int
+do_apply_dentry_ntfs(struct wim_dentry *dentry, ntfs_inode *dir_ni,
+ struct apply_args *args)
{
- int ret = 0;
+ int ret;
mode_t type;
ntfs_inode *ni = NULL;
struct wim_inode *inode = dentry->d_inode;
/* Already extracted another dentry in the hard
* link group. Make a hard link instead of
* extracting the file data. */
- ret = apply_ntfs_hardlink(dentry, inode,
- &dir_ni);
- goto out_close_dir_ni;
+ ret = apply_ntfs_hardlink(dentry, inode, dir_ni);
+ /* dir_ni was closed */
+ goto out;
} else {
/* None of the dentries of this inode have been
* extracted yet, so go ahead and extract the
* first one. */
FREE(inode->i_extracted_file);
- inode->i_extracted_file = STRDUP(dentry->full_path_utf8);
- if (!inode->i_extracted_file) {
+ if (!(inode->i_extracted_file = STRDUP(dentry->_full_path)))
+ {
ret = WIMLIB_ERR_NOMEM;
goto out_close_dir_ni;
}
* Note: For symbolic links that are not directory junctions, S_IFREG is
* passed here, since the reparse data and file attributes are set
* later. */
- ni = ntfs_create(dir_ni, 0, (ntfschar*)dentry->file_name,
- dentry->file_name_len / 2, type);
+ ni = ntfs_create(dir_ni, 0, dentry->file_name,
+ dentry->file_name_nbytes / 2, type);
if (!ni) {
ERROR_WITH_ERRNO("Could not create NTFS inode for `%s'",
- dentry->full_path_utf8);
+ dentry->_full_path);
ret = WIMLIB_ERR_NTFS_3G;
goto out_close_dir_ni;
}
- /* Write the data streams, unless this is a directory or reparse point
- * */
- if (!(inode->i_attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
- FILE_ATTRIBUTE_DIRECTORY))) {
+ /* Write the data streams, unless this is reparse point. */
+ if (!(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
ret = write_ntfs_data_streams(ni, dentry, &args->progress);
- if (ret != 0)
+ if (ret)
goto out_close_dir_ni;
}
-
ret = apply_file_attributes_and_security_data(ni, dir_ni, dentry,
- args->w);
- if (ret != 0)
+ args->w,
+ args->extract_flags);
+ if (ret)
goto out_close_dir_ni;
if (inode->i_attributes & FILE_ATTR_REPARSE_POINT) {
ret = apply_reparse_data(ni, dentry, &args->progress);
- if (ret != 0)
+ if (ret)
goto out_close_dir_ni;
}
/* Set DOS (short) name if given */
- if (dentry->short_name_len != 0) {
- char *short_name_utf8;
- size_t short_name_utf8_len;
- ret = utf16_to_utf8(dentry->short_name,
- dentry->short_name_len,
- &short_name_utf8,
- &short_name_utf8_len);
- if (ret != 0)
+ if (dentry_has_short_name(dentry)) {
+ char *short_name_mbs;
+ size_t short_name_mbs_nbytes;
+ ret = utf16le_to_tstr(dentry->short_name,
+ dentry->short_name_nbytes,
+ &short_name_mbs,
+ &short_name_mbs_nbytes);
+ if (ret)
goto out_close_dir_ni;
DEBUG("Setting short (DOS) name of `%s' to %s",
- dentry->full_path_utf8, short_name_utf8);
+ dentry->_full_path, short_name_mbs);
- ret = ntfs_set_ntfs_dos_name(ni, dir_ni, short_name_utf8,
- short_name_utf8_len, 0);
- FREE(short_name_utf8);
- if (ret != 0) {
+ ret = ntfs_set_ntfs_dos_name(ni, dir_ni, short_name_mbs,
+ short_name_mbs_nbytes, 0);
+ FREE(short_name_mbs);
+ if (ret) {
ERROR_WITH_ERRNO("Could not set DOS (short) name for `%s'",
- dentry->full_path_utf8);
+ dentry->_full_path);
ret = WIMLIB_ERR_NTFS_3G;
}
/* inodes have been closed by ntfs_set_ntfs_dos_name(). */
if (ret == 0)
ret = WIMLIB_ERR_NTFS_3G;
ERROR_WITH_ERRNO("Failed to close inode for `%s'",
- dentry->full_path_utf8);
+ dentry->_full_path);
}
}
if (ntfs_inode_close(dir_ni)) {
if (ret == 0)
ret = WIMLIB_ERR_NTFS_3G;
ERROR_WITH_ERRNO("Failed to close inode of directory "
- "containing `%s'", dentry->full_path_utf8);
+ "containing `%s'",
+ dentry->_full_path);
}
}
out:
return ret;
}
-static int apply_root_dentry_ntfs(const struct wim_dentry *dentry,
- ntfs_volume *vol, const WIMStruct *w)
+static int
+apply_root_dentry_ntfs(struct wim_dentry *dentry,
+ ntfs_volume *vol, const WIMStruct *w,
+ int extract_flags)
{
ntfs_inode *ni;
int ret = 0;
ERROR_WITH_ERRNO("Could not find root NTFS inode");
return WIMLIB_ERR_NTFS_3G;
}
- ret = apply_file_attributes_and_security_data(ni, ni, dentry, w);
+ ret = apply_file_attributes_and_security_data(ni, ni, dentry, w,
+ extract_flags);
if (ntfs_inode_close(ni) != 0) {
ERROR_WITH_ERRNO("Failed to close NTFS inode for root "
"directory");
}
/* Applies a WIM dentry to the NTFS volume */
-int apply_dentry_ntfs(struct wim_dentry *dentry, void *arg)
+int
+apply_dentry_ntfs(struct wim_dentry *dentry, void *arg)
{
struct apply_args *args = arg;
ntfs_volume *vol = args->vol;
/* Treat the root dentry specially. */
if (dentry_is_root(dentry))
- return apply_root_dentry_ntfs(dentry, vol, w);
+ return apply_root_dentry_ntfs(dentry, vol, w,
+ args->extract_flags);
/* NTFS filename namespaces need careful consideration. A name for a
* NTFS file may be in either the POSIX, Win32, DOS, or Win32+DOS
* file. So, this implies that the correct ordering of function calls
* to extract a NTFS file are:
*
- * if (file has a DOS name) {
+ * if (file has a DOS name) {
* - Call ntfs_create() to create long name associated with
* the DOS name (this initially creates a POSIX name)
* - Call ntfs_set_ntfs_dos_name() to associate a DOS name
again:
orig_dentry = NULL;
if (!dentry->d_inode->i_dos_name_extracted &&
- dentry->short_name_len == 0)
+ !dentry_has_short_name(dentry))
{
inode_for_each_dentry(other, dentry->d_inode) {
- if (other->short_name_len != 0) {
+ if (dentry_has_short_name(other)) {
orig_dentry = dentry;
dentry = other;
break;
/* Transfers the 100-nanosecond precision timestamps from a WIM dentry to a NTFS
* inode */
-int apply_dentry_timestamps_ntfs(struct wim_dentry *dentry, void *arg)
+int
+apply_dentry_timestamps_ntfs(struct wim_dentry *dentry, void *arg)
{
struct apply_args *args = arg;
ntfs_volume *vol = args->vol;
ntfs_inode *ni;
int ret;
- DEBUG("Setting timestamps on `%s'", dentry->full_path_utf8);
+ DEBUG("Setting timestamps on `%s'", dentry->_full_path);
- ni = ntfs_pathname_to_inode(vol, NULL, dentry->full_path_utf8);
+ ni = ntfs_pathname_to_inode(vol, NULL, dentry->_full_path);
if (!ni) {
ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
- dentry->full_path_utf8);
+ dentry->_full_path);
return WIMLIB_ERR_NTFS_3G;
}
ret = ntfs_inode_set_times(ni, (const char*)buf, 3 * sizeof(u64), 0);
if (ret != 0) {
ERROR_WITH_ERRNO("Failed to set NTFS timestamps on `%s'",
- dentry->full_path_utf8);
+ dentry->_full_path);
ret = WIMLIB_ERR_NTFS_3G;
}
if (ret == 0)
ret = WIMLIB_ERR_NTFS_3G;
ERROR_WITH_ERRNO("Failed to close NTFS inode for `%s'",
- dentry->full_path_utf8);
+ dentry->_full_path);
}
return ret;
}
+
+void
+libntfs3g_global_init()
+{
+ ntfs_set_char_encoding(setlocale(LC_ALL, ""));
+}