/*
* ntfs-apply.c
*
- * Apply a WIM image to a NTFS volume. We restore everything we can, including
- * security data and alternate data streams.
+ * Apply a WIM image to a NTFS volume. Restore as much information as possible,
+ * including security data, file attributes, DOS names, and alternate data
+ * streams.
*/
/*
#include <stdlib.h>
#include <unistd.h>
+static int extract_wim_chunk_to_ntfs_attr(const u8 *buf, size_t len,
+ u64 offset, void *arg)
+{
+ ntfs_attr *na = arg;
+ if (ntfs_attr_pwrite(na, offset, len, buf) == len) {
+ return 0;
+ } else {
+ ERROR_WITH_ERRNO("Error extracting WIM resource to NTFS attribute");
+ return WIMLIB_ERR_WRITE;
+ }
+}
+
/*
* Extracts a WIM resource to a NTFS attribute.
*/
extract_wim_resource_to_ntfs_attr(const struct lookup_table_entry *lte,
ntfs_attr *na)
{
- u64 bytes_remaining = wim_resource_size(lte);
- u8 buf[min(WIM_CHUNK_SIZE, bytes_remaining)];
- u64 offset = 0;
- int ret = 0;
- u8 hash[SHA1_HASH_SIZE];
-
- SHA_CTX ctx;
- sha1_init(&ctx);
-
- while (bytes_remaining) {
- u64 to_read = min(bytes_remaining, WIM_CHUNK_SIZE);
- ret = read_wim_resource(lte, buf, to_read, offset, 0);
- if (ret != 0)
- break;
- sha1_update(&ctx, buf, to_read);
- if (ntfs_attr_pwrite(na, offset, to_read, buf) != to_read) {
- ERROR_WITH_ERRNO("Error extracting WIM resource");
- return WIMLIB_ERR_WRITE;
- }
- bytes_remaining -= to_read;
- offset += to_read;
- }
- sha1_final(hash, &ctx);
- if (!hashes_equal(hash, lte->hash)) {
- ERROR("Invalid checksum on a WIM resource "
- "(detected when extracting to NTFS stream)");
- ERROR("The following WIM resource is invalid:");
- print_lookup_table_entry(lte);
- return WIMLIB_ERR_INVALID_RESOURCE_HASH;
- }
- return 0;
+ return extract_wim_resource(lte, wim_resource_size(lte),
+ extract_wim_chunk_to_ntfs_attr, na);
}
/* Writes the data streams to a NTFS file
* Returns 0 on success, nonzero on failure.
*/
static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry,
- struct apply_args *args)
+ union wimlib_progress_info *progress_info)
{
int ret = 0;
unsigned stream_idx = 0;
while (1) {
struct lookup_table_entry *lte;
- ntfs_attr *na;
lte = inode_stream_lte_resolved(inode, stream_idx);
* Otherwise, we must open the attribute and extract the data.
* */
if (lte) {
+ ntfs_attr *na;
+
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
if (!na) {
ERROR_WITH_ERRNO("Failed to open a data stream of "
ret = WIMLIB_ERR_NTFS_3G;
break;
}
+ ret = ntfs_attr_truncate_solid(na, wim_resource_size(lte));
+ if (ret != 0) {
+ ntfs_attr_close(na);
+ break;
+ }
+
ret = extract_wim_resource_to_ntfs_attr(lte, na);
+ ntfs_attr_close(na);
if (ret != 0)
break;
- args->progress.extract.completed_bytes += wim_resource_size(lte);
- ntfs_attr_close(na);
+ progress_info->extract.completed_bytes += wim_resource_size(lte);
}
if (stream_idx == inode->num_ads)
break;
return ret;
}
+/* Open the NTFS inode that corresponds to the parent of a WIM dentry. */
+static ntfs_inode *dentry_open_parent_ni(const struct 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;
+ do {
+ p--;
+ } while (*p != '/');
+
+ orig = *p;
+ *p = '\0';
+ dir_name = dentry->full_path_utf8;
+ dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
+ if (!dir_ni) {
+ ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
+ dir_name);
+ }
+ *p = orig;
+ return dir_ni;
+}
+
/*
* Makes a NTFS hard link
*
*
* Return 0 on success, nonzero on failure.
*/
-static int apply_hardlink_ntfs(const struct dentry *from_dentry,
+static int apply_ntfs_hardlink(const struct dentry *from_dentry,
const struct inode *inode,
- ntfs_inode *dir_ni,
- ntfs_inode **to_ni_ret)
+ ntfs_inode **dir_ni_p)
{
int ret;
- char *p;
- char orig;
- const char *dir_name;
-
ntfs_inode *to_ni;
+ ntfs_inode *dir_ni;
ntfs_volume *vol;
- wimlib_assert(dentry_is_regular_file(from_dentry)
- && inode_is_regular_file(inode));
-
- if (ntfs_inode_close(dir_ni) != 0) {
+ 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;
}
- vol = dir_ni->vol;
-
DEBUG("Extracting NTFS hard link `%s' => `%s'",
from_dentry->full_path_utf8, inode->extracted_file);
inode->extracted_file);
return WIMLIB_ERR_NTFS_3G;
}
- p = from_dentry->full_path_utf8 + from_dentry->full_path_utf8_len;
- do {
- p--;
- } while (*p != '/');
-
- orig = *p;
- *p = '\0';
- dir_name = from_dentry->full_path_utf8;
- dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
+ dir_ni = dentry_open_parent_ni(from_dentry, vol);
if (!dir_ni) {
- ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
- from_dentry->full_path_utf8);
- *p = orig;
+ ntfs_inode_close(to_ni);
return WIMLIB_ERR_NTFS_3G;
}
- *p = orig;
+
+ *dir_ni_p = dir_ni;
ret = ntfs_link(to_ni, dir_ni,
(ntfschar*)from_dentry->file_name,
from_dentry->file_name_len / 2);
- if (ret != 0) {
+ if (ntfs_inode_close_in_dir(to_ni, dir_ni) || ret != 0) {
ERROR_WITH_ERRNO("Could not create hard link `%s' => `%s'",
from_dentry->full_path_utf8,
inode->extracted_file);
ret = WIMLIB_ERR_NTFS_3G;
}
- *to_ni_ret = to_ni;
return ret;
}
}
static int apply_reparse_data(ntfs_inode *ni, const struct dentry *dentry,
- struct apply_args *args)
+ union wimlib_progress_info *progress_info)
{
struct lookup_table_entry *lte;
int ret = 0;
dentry->full_path_utf8);
return WIMLIB_ERR_NTFS_3G;
}
- args->progress.extract.completed_bytes += wim_resource_size(lte);
+ progress_info->extract.completed_bytes += wim_resource_size(lte);
return 0;
}
*dir_ni_p, args);
if (ret != 0)
return ret;
- p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
- do {
- p--;
- } while (*p != '/');
-
- orig = *p;
- *p = '\0';
- dir_name = dentry->full_path_utf8;
-
- *dir_ni_p = ntfs_pathname_to_inode(vol, NULL, dir_name);
- *p = orig;
- if (!*dir_ni_p) {
- ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
- dir_name);
+
+ *dir_ni_p = dentry_open_parent_ni(dentry, vol);
+ if (!*dir_ni_p)
return WIMLIB_ERR_NTFS_3G;
- }
}
return 0;
}
bool is_hardlink = false;
ntfs_volume *vol = dir_ni->vol;
struct inode *inode = dentry->d_inode;
- dentry->is_extracted = true;
+ dentry->is_extracted = 1;
if (inode->attributes & FILE_ATTRIBUTE_DIRECTORY) {
type = S_IFDIR;
} else {
- /* Apply hard-linked directory in same directory with DOS name
- * (if there is one) before this dentry */
+ /* If this dentry is hard-linked to any other dentries in the
+ * same directory, make sure to apply the one (if any) with a
+ * DOS name first. Otherwise, NTFS-3g might not assign the file
+ * names correctly. */
if (dentry->short_name_len == 0) {
ret = preapply_dentry_with_dos_name(dentry,
&dir_ni, args);
if (inode->link_count > 1) {
/* Already extracted another dentry in the hard link
- * group. We can make a hard link instead of extracting
- * the file data. */
+ * group. Make a hard link instead of extracting the
+ * file data. */
if (inode->extracted_file) {
- ret = apply_hardlink_ntfs(dentry, inode,
- dir_ni, &ni);
+ ret = apply_ntfs_hardlink(dentry, inode,
+ &dir_ni);
is_hardlink = true;
if (ret)
goto out_close_dir_ni;
* */
if (!(inode->attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
FILE_ATTRIBUTE_DIRECTORY))) {
- ret = write_ntfs_data_streams(ni, dentry, args);
+ ret = write_ntfs_data_streams(ni, dentry, &args->progress);
if (ret != 0)
goto out_close_dir_ni;
}
goto out_close_dir_ni;
if (inode->attributes & FILE_ATTR_REPARSE_POINT) {
- ret = apply_reparse_data(ni, dentry, args);
+ ret = apply_reparse_data(ni, dentry, &args->progress);
if (ret != 0)
goto out_close_dir_ni;
}
char *short_name_utf8;
size_t short_name_utf8_len;
- short_name_utf8 = utf16_to_utf8(dentry->short_name,
- dentry->short_name_len,
- &short_name_utf8_len);
- if (!short_name_utf8) {
- ERROR("Out of memory");
- ret = WIMLIB_ERR_NOMEM;
+ ret = utf16_to_utf8(dentry->short_name,
+ dentry->short_name_len,
+ &short_name_utf8,
+ &short_name_utf8_len);
+ if (ret != 0)
goto out_close_dir_ni;
- }
if (is_hardlink) {
- char *p;
- char orig;
- const char *dir_name;
-
- /* ntfs_set_ntfs_dos_name() closes the inodes in the
- * wrong order if we have applied a hard link. Close
- * them ourselves, then re-open then. */
- if (ntfs_inode_close(dir_ni) != 0) {
- if (ret == 0)
- ret = WIMLIB_ERR_NTFS_3G;
- ERROR_WITH_ERRNO("Failed to close directory inode");
- }
- if (ntfs_inode_close(ni) != 0) {
- if (ret == 0)
- ret = WIMLIB_ERR_NTFS_3G;
- ERROR_WITH_ERRNO("Failed to close hard link target inode");
- }
- p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
- do {
- p--;
- } while (*p != '/');
-
- orig = *p;
- *p = '\0';
- dir_name = dentry->full_path_utf8;
-
- dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
- *p = orig;
- if (!dir_ni) {
- ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
- dir_name);
- return WIMLIB_ERR_NTFS_3G;
- }
+ wimlib_assert(ni == NULL);
ni = ntfs_pathname_to_inode(vol, dir_ni,
dentry->file_name_utf8);
if (!ni) {
ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
- dir_name);
- return WIMLIB_ERR_NTFS_3G;
+ dentry->full_path_utf8);
+ ret = WIMLIB_ERR_NTFS_3G;
+ goto out_close_dir_ni;
}
}
+ wimlib_assert(ni != NULL);
+
DEBUG("Setting short (DOS) name of `%s' to %s",
dentry->full_path_utf8, short_name_utf8);
}
out_close_dir_ni:
- if (ntfs_inode_close(dir_ni) != 0) {
- if (ret == 0)
- ret = WIMLIB_ERR_NTFS_3G;
- ERROR_WITH_ERRNO("Failed to close directory inode");
- }
- if (ni && ntfs_inode_close(ni) != 0) {
- if (ret == 0)
- ret = WIMLIB_ERR_NTFS_3G;
- ERROR_WITH_ERRNO("Failed to close inode");
+ if (dir_ni) {
+ if (ni) {
+ if (ntfs_inode_close_in_dir(ni, dir_ni)) {
+ if (ret == 0)
+ ret = WIMLIB_ERR_NTFS_3G;
+ ERROR_WITH_ERRNO("Failed to close inode for `%s'",
+ dentry->full_path_utf8);
+ }
+ }
+ if (ntfs_inode_close(dir_ni)) {
+ if (ret == 0)
+ ret = WIMLIB_ERR_NTFS_3G;
+ ERROR_WITH_ERRNO("Failed to close directory inode");
+ }
+ } else {
+ wimlib_assert(ni == NULL);
}
return ret;
}
int apply_dentry_ntfs(struct dentry *dentry, void *arg)
{
struct apply_args *args = arg;
- ntfs_volume *vol = args->vol;
- int extract_flags = args->extract_flags;
- WIMStruct *w = args->w;
+ ntfs_volume *vol = args->vol;
+ WIMStruct *w = args->w;
ntfs_inode *dir_ni;
- char *p;
- char orig;
- const char *dir_name;
-
- if (dentry->is_extracted)
- return 0;
-
- if (extract_flags & WIMLIB_EXTRACT_FLAG_NO_STREAMS)
- if (inode_unnamed_lte_resolved(dentry->d_inode))
- return 0;
-
- DEBUG("Applying dentry `%s' to NTFS", dentry->full_path_utf8);
-
- if ((extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE) &&
- args->progress_func)
- {
- args->progress.extract.cur_path = dentry->full_path_utf8;
- args->progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY,
- &args->progress);
- }
if (dentry_is_root(dentry))
return apply_root_dentry_ntfs(dentry, vol, w);
- p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
- do {
- p--;
- } while (*p != '/');
-
- orig = *p;
- *p = '\0';
- dir_name = dentry->full_path_utf8;
-
- dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
- *p = orig;
- if (!dir_ni) {
- ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
- dir_name);
+ dir_ni = dentry_open_parent_ni(dentry, vol);
+ if (dir_ni)
+ return do_apply_dentry_ntfs(dentry, dir_ni, arg);
+ else
return WIMLIB_ERR_NTFS_3G;
- }
- return do_apply_dentry_ntfs(dentry, dir_ni, arg);
}
int apply_dentry_timestamps_ntfs(struct dentry *dentry, void *arg)