Add apply_ops.requires_short_name_reordering
authorEric Biggers <ebiggers3@gmail.com>
Sun, 18 Aug 2013 21:09:17 +0000 (16:09 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Sun, 18 Aug 2013 21:09:17 +0000 (16:09 -0500)
include/wimlib/apply.h
src/extract.c
src/ntfs-3g_apply.c
src/win32_apply.c

index 1019a48..f8023a3 100644 (file)
@@ -176,6 +176,11 @@ struct apply_operations {
        /* OPTIONAL:  Set to 1 if extract_encrypted_stream() must be used to
         * create encrypted files.  */
        unsigned extract_encrypted_stream_creates_file : 1;
+
+       /* OPTIONAL:  Set to 1 if a link to a file with corresponding short name
+        * must be created before all links to the same file without
+        * corresponding short names.  */
+       unsigned requires_short_name_reordering : 1;
 };
 
 struct wim_features {
index 235d504..1784cdf 100644 (file)
@@ -1119,6 +1119,11 @@ hardlink:
        return 0;
 }
 
+/* This is a wrapper around do_dentry_extract_skeleton() that handles building
+ * the path, doing short name reordering.  This is also idempotent; dentries
+ * already processed have skeleton_extracted set and no action is taken.  See
+ * apply_operations.requires_short_name_reordering for more details about short
+ * name reordering.  */
 static int
 dentry_extract_skeleton(struct wim_dentry *dentry, void *_ctx)
 {
@@ -1128,32 +1133,18 @@ dentry_extract_skeleton(struct wim_dentry *dentry, void *_ctx)
        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
+           && ctx->ops->requires_short_name_reordering
            && !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)
+                           && !other_dentry->skeleton_extracted)
                        {
                                DEBUG("Creating %"TS" before %"TS" "
                                      "to guarantee correct DOS name extraction",
@@ -1183,6 +1174,14 @@ again:
        return 0;
 }
 
+static int
+dentry_extract_dir_skeleton(struct wim_dentry *dentry, void *_ctx)
+{
+       if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
+               return dentry_extract_skeleton(dentry, _ctx);
+       return 0;
+}
+
 /* Create a file or directory, then immediately extract all streams.  This
  * assumes that WIMLIB_EXTRACT_FLAG_SEQUENTIAL is not specified, since the WIM
  * may not be read sequentially by this function.  */
@@ -2322,6 +2321,13 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target,
                ctx.realtarget_nchars = tstrlen(ctx.realtarget);
        }
 
+       if (ctx.ops->requires_short_name_reordering) {
+               ret = for_dentry_in_tree(root, dentry_extract_dir_skeleton,
+                                        &ctx);
+               if (ret)
+                       goto out_free_realtarget;
+       }
+
        /* Finally, the important part: extract the tree of files.  */
        if (extract_flags & (WIMLIB_EXTRACT_FLAG_SEQUENTIAL |
                             WIMLIB_EXTRACT_FLAG_FROM_PIPE)) {
index a1c6775..2858969 100644 (file)
@@ -508,9 +508,42 @@ const struct apply_operations ntfs_3g_apply_ops = {
        .path_separator = '/',
        .path_max = 32768,
 
+       /* By default, NTFS-3g creates names in the NTFS POSIX namespace, which
+        * is case-sensitive.  */
        .supports_case_sensitive_filenames = 1,
+
+       /* The root directory of the NTFS volume should not be created
+        * explicitly.  */
        .root_directory_is_special = 1,
+
+       /* NTFS-3g can open files by MFT reference.  */
        .uses_cookies = 1,
+
+       /*
+        * With NTFS-3g, the extraction order of the names of a file that has a
+        * short name needs to be:
+        *
+        * 1. Create file using the long name that has an associated short name.
+        *    This long name is temporarily placed in the POSIX namespace.
+        * 2. Set the short name on the file.  This will either change the POSIX
+        *    name to Win32 and create a new DOS name, or replace the POSIX name
+        *    with a Win32+DOS name.
+        * 3. Create additional long names (links) of the file, which are placed
+        *    in the POSIX namespace.
+        *
+        * The reason for this is that two issues can come up when the
+        * extraction is done otherwise:
+        *
+        * - If a DOS name is set on a file in a directory with several long
+        *   names, it is ambiguous which long name to use (at least with the
+        *   exported ntfs_set_ntfs_dos_name() function).
+        * - NTFS-3g 2013.1.13 will no longer allow even setting the DOS name on
+        *   a file with multiple existing long names, even if those long names
+        *   are in different directories and the ntfs_set_ntfs_dos_name() call
+        *   is therefore unambiguous.  (This was apparently changed with the
+        *   FUSE interface in mind.)
+        */
+       .requires_short_name_reordering    = 1,
 };
 
 #endif /* WITH_NTFS_3G */
index e554b95..48dd66d 100644 (file)
@@ -614,6 +614,7 @@ const struct apply_operations win32_apply_ops = {
        .root_directory_is_special = 1,
        .requires_final_set_attributes_pass = 1,
        .extract_encrypted_stream_creates_file = 1,
+       .requires_short_name_reordering = 1, /* TODO: check if this is really needed  */
 };
 
 #endif /* __WIN32__ */