From: Eric Biggers Date: Thu, 15 Aug 2013 23:05:07 +0000 (-0500) Subject: ntfs-3g capture/apply: Capture & apply DOS names correctly X-Git-Tag: v1.5.0~70 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=52a908ca68284a4dfa89a884f547fd78d1543772 ntfs-3g capture/apply: Capture & apply DOS names correctly --- diff --git a/NEWS b/NEWS index e8281cc2..c31cea5c 100644 --- a/NEWS +++ b/NEWS @@ -54,6 +54,8 @@ Version 1.5.0: wimlib_split() progress messages now report the total number of parts being written. + Fixed storing DOS names in NTFS-3g capture mode. + A few changes were made to the error codes returned by library routines. To make wimlib easier to use on Windows, wimlib_global_init() now diff --git a/include/wimlib/dentry.h b/include/wimlib/dentry.h index ad8b0328..09de869a 100644 --- a/include/wimlib/dentry.h +++ b/include/wimlib/dentry.h @@ -159,6 +159,8 @@ struct wim_dentry { * characters). Otherwise this will always be 0. */ u8 extraction_skipped : 1; + u8 skeleton_extracted : 1; + /* When capturing from a NTFS volume using NTFS-3g, this flag is set on * dentries that were created from a filename in the WIN32 or WIN32+DOS * namespaces rather than the POSIX namespace. Otherwise this will @@ -311,6 +313,9 @@ struct wim_inode { * error paths. */ u8 i_visited : 1; + /* Set if the DOS name of an inode has already been extracted. */ + u8 i_dos_name_extracted : 1; + /* Pointer to a malloc()ed array of i_num_ads alternate data stream * entries for this inode. */ struct wim_ads_entry *i_ads_entries; diff --git a/src/extract.c b/src/extract.c index e599ffff..ce72b781 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1067,10 +1067,63 @@ dentry_extract_skeleton(struct wim_dentry *dentry, void *_ctx) { struct apply_ctx *ctx = _ctx; tchar path[ctx->ops->path_max]; + struct wim_dentry *orig_dentry; + struct wim_dentry *other_dentry; + int ret; + /* Here we may re-order the extraction of multiple names (hard links) + * for the same file in the same directory in order to ensure the short + * (DOS) name is set correctly. A short name is always associated with + * exactly one long name, and at least on NTFS, only one long name for a + * file can have a short name associated with it. (More specifically, + * there can be unlimited names in the POSIX namespace, but only one + * name can be in the Win32+DOS namespace, or one name in the Win32 + * namespace with a corresponding name in the DOS namespace.) To ensure + * the short name of a file is associated with the correct long name in + * a directory, we extract the long name with a corresponding short name + * before any additional names. This can affect NTFS-3g extraction + * (which uses ntfs_set_ntfs_dos_name(), which doesn't allow specifying + * the long name to associate with a short name) and may affect Win32 + * extraction as well (which uses SetFileShortName()). */ + + if (dentry->skeleton_extracted) + return 0; + orig_dentry = NULL; + if (ctx->supported_features.short_names + && !dentry_has_short_name(dentry) + && !dentry->d_inode->i_dos_name_extracted) + { + inode_for_each_dentry(other_dentry, dentry->d_inode) { + if (dentry_has_short_name(other_dentry) + && !other_dentry->skeleton_extracted + && other_dentry->parent == dentry->parent) + { + DEBUG("Creating %"TS" before %"TS" " + "to guarantee correct DOS name extraction", + dentry_full_path(other_dentry), + dentry_full_path(dentry)); + orig_dentry = dentry; + dentry = other_dentry; + break; + } + } + } +again: if (!build_extraction_path(path, dentry, ctx)) return 0; - return do_dentry_extract_skeleton(path, dentry, ctx); + ret = do_dentry_extract_skeleton(path, dentry, ctx); + if (ret) + return ret; + + dentry->skeleton_extracted = 1; + + if (orig_dentry) { + dentry = orig_dentry; + orig_dentry = NULL; + goto again; + } + dentry->d_inode->i_dos_name_extracted = 1; + return 0; } /* Create a file or directory, then immediately extract all streams. This @@ -1083,13 +1136,13 @@ dentry_extract(struct wim_dentry *dentry, void *_ctx) tchar path[ctx->ops->path_max]; int ret; - if (!build_extraction_path(path, dentry, ctx)) - return 0; - - ret = do_dentry_extract_skeleton(path, dentry, ctx); + ret = dentry_extract_skeleton(dentry, ctx); if (ret) return ret; + if (!build_extraction_path(path, dentry, ctx)) + return 0; + return extract_streams(path, ctx, dentry, NULL, NULL); } @@ -1578,9 +1631,11 @@ dentry_reset_needs_extraction(struct wim_dentry *dentry, void *_ignore) dentry->extraction_skipped = 0; dentry->was_hardlinked = 0; + dentry->skeleton_extracted = 0; inode->i_visited = 0; FREE(inode->i_extracted_file); inode->i_extracted_file = NULL; + inode->i_dos_name_extracted = 0; if ((void*)dentry->extraction_name != (void*)dentry->file_name) FREE(dentry->extraction_name); dentry->extraction_name = NULL; diff --git a/src/ntfs-3g_capture.c b/src/ntfs-3g_capture.c index f5929292..10d4bcc5 100644 --- a/src/ntfs-3g_capture.c +++ b/src/ntfs-3g_capture.c @@ -24,34 +24,23 @@ * along with wimlib; if not, see http://www.gnu.org/licenses/. */ - #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef WITH_NTFS_3G +#include +#include + #ifdef HAVE_ALLOCA_H # include - #endif -#include -#include -#include /* NTFS-3g headers are missing include */ -#include #include -#include -#include -#include #include -#include /* ntfs-3g/security.h before ntfs-3g/xattrs.h */ -#include +#include #include -#include - -#include /* This should be included last as it requires - definitions from above not included by itself */ #include "wimlib/capture.h" #include "wimlib/dentry.h" @@ -333,13 +322,13 @@ struct dos_name_node { struct rb_node rb_node; char dos_name[24]; int name_nbytes; - u64 ntfs_ino; + le64 ntfs_ino; }; /* Inserts a new DOS name into the map */ static int insert_dos_name(struct dos_name_map *map, const ntfschar *dos_name, - size_t name_nbytes, u64 ntfs_ino) + size_t name_nbytes, le64 ntfs_ino) { struct dos_name_node *new_node; struct rb_node **p; @@ -379,13 +368,13 @@ insert_dos_name(struct dos_name_map *map, const ntfschar *dos_name, * have multiple DOS names, and we only should get each * DOS name entry once from the ntfs_readdir() calls. */ ERROR("NTFS inode %"PRIu64" has multiple DOS names", - ntfs_ino); + le64_to_cpu(ntfs_ino)); return -1; } } rb_link_node(&new_node->rb_node, rb_parent, p); rb_insert_color(&new_node->rb_node, root); - DEBUG("Inserted DOS name for inode %"PRIu64, ntfs_ino); + DEBUG("Inserted DOS name for inode %"PRIu64, le64_to_cpu(ntfs_ino)); return 0; } @@ -452,7 +441,6 @@ destroy_dos_name_map(struct dos_name_map *map) struct readdir_ctx { struct wim_dentry *parent; - ntfs_inode *dir_ni; char *path; size_t path_len; struct dos_name_map *dos_name_map; @@ -462,7 +450,6 @@ struct readdir_ctx { static int build_dentry_tree_ntfs_recursive(struct wim_dentry **root_p, - ntfs_inode *dir_ni, ntfs_inode *ni, char *path, size_t path_len, @@ -513,7 +500,7 @@ wim_ntfs_capture_filldir(void *dirent, const ntfschar *name, /* Open the inode for this directory entry and recursively capture the * directory tree rooted at it */ - ntfs_inode *ni = ntfs_inode_open(ctx->dir_ni->vol, mref); + ntfs_inode *ni = ntfs_inode_open(ctx->vol, mref); if (!ni) { /* XXX This used to be treated as an error, but NTFS-3g seemed * to be unable to read some inodes on a Windows 8 image for @@ -529,8 +516,8 @@ wim_ntfs_capture_filldir(void *dirent, const ntfschar *name, memcpy(ctx->path + path_len, mbs_name, mbs_name_nbytes + 1); path_len += mbs_name_nbytes; child = NULL; - ret = build_dentry_tree_ntfs_recursive(&child, ctx->dir_ni, - ni, ctx->path, path_len, name_type, + ret = build_dentry_tree_ntfs_recursive(&child, ni, ctx->path, + path_len, name_type, ctx->vol, ctx->params); path_len -= mbs_name_nbytes + 1; if (child) @@ -549,7 +536,6 @@ out: */ static int build_dentry_tree_ntfs_recursive(struct wim_dentry **root_ret, - ntfs_inode *dir_ni, ntfs_inode *ni, char *path, size_t path_len, @@ -580,12 +566,7 @@ build_dentry_tree_ntfs_recursive(struct wim_dentry **root_ret, } /* Get file attributes */ - struct SECURITY_CONTEXT ctx; - memset(&ctx, 0, sizeof(ctx)); - ctx.vol = vol; - ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ATTRIB, - ni, dir_ni, (char *)&attributes, - sizeof(attributes)); + ret = ntfs_get_ntfs_attrib(ni, (char*)&attributes, sizeof(attributes)); if (ret != sizeof(attributes)) { ERROR_WITH_ERRNO("Failed to get NTFS attributes from \"%s\"", path); return WIMLIB_ERR_NTFS_3G; @@ -607,13 +588,14 @@ build_dentry_tree_ntfs_recursive(struct wim_dentry **root_ret, if (ret) return ret; + if (name_type & FILE_NAME_WIN32) /* Win32 or Win32+DOS name (rather than POSIX) */ + root->is_win32_name = 1; + inode = root->d_inode; if (inode->i_nlink > 1) /* Shared inode; nothing more to do */ goto out; - if (name_type & FILE_NAME_WIN32) /* Win32 or Win32+DOS name (rather than POSIX) */ - root->is_win32_name = 1; inode->i_creation_time = le64_to_cpu(ni->creation_time); inode->i_last_write_time = le64_to_cpu(ni->last_data_change_time); inode->i_last_access_time = le64_to_cpu(ni->last_access_time); @@ -644,7 +626,6 @@ build_dentry_tree_ntfs_recursive(struct wim_dentry **root_ret, struct dos_name_map dos_name_map = { .rb_root = {.rb_node = NULL} }; struct readdir_ctx ctx = { .parent = root, - .dir_ni = ni, .path = path, .path_len = path_len, .dos_name_map = &dos_name_map, @@ -671,17 +652,19 @@ build_dentry_tree_ntfs_recursive(struct wim_dentry **root_ret, inode->i_not_rpfixed = 0; if (!(params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS)) { - /* Get security descriptor */ + struct SECURITY_CONTEXT sec_ctx; char _sd[1]; - char *sd = _sd; + char *sd; + + /* Get security descriptor */ + memset(&sec_ctx, 0, sizeof(sec_ctx)); + sec_ctx.vol = vol; + errno = 0; - ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ACL, - ni, dir_ni, sd, - sizeof(sd)); - if (ret > sizeof(sd)) { + ret = ntfs_get_ntfs_acl(&sec_ctx, ni, _sd, sizeof(_sd)); + if (ret > sizeof(_sd)) { sd = alloca(ret); - ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ACL, - ni, dir_ni, sd, ret); + ret = ntfs_get_ntfs_acl(&sec_ctx, ni, sd, ret); } if (ret > 0) { inode->i_security_id = sd_set_add_sd(¶ms->sd_set, @@ -782,7 +765,7 @@ build_dentry_tree_ntfs(struct wim_dentry **root_p, path[0] = '/'; path[1] = '\0'; - ret = build_dentry_tree_ntfs_recursive(root_p, NULL, root_ni, path, 1, + ret = build_dentry_tree_ntfs_recursive(root_p, root_ni, path, 1, FILE_NAME_POSIX, vol, params); out_cleanup: FREE(path);