]> wimlib.net Git - wimlib/blobdiff - src/ntfs-apply.c
Cleanup
[wimlib] / src / ntfs-apply.c
index 998702f2d1959cba9d8b82085bc95c4b776704f9..31b84239d00db6c8621678f2be18ca45a78d9708 100644 (file)
@@ -197,21 +197,21 @@ static ntfs_inode *dentry_open_parent_ni(const struct wim_dentry *dentry,
  * Or, in other words, this adds a new name @from_dentry->full_path_utf8 to an
  * existing NTFS inode which already has a name @inode->i_extracted_file.
  *
- * Return 0 on success, nonzero on failure.
+ * The new name is made in the POSIX namespace (this is the behavior of
+ * ntfs_link()).
+ *
+ * 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)
+                              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;
@@ -233,12 +233,12 @@ static int apply_ntfs_hardlink(const struct wim_dentry *from_dentry,
                return WIMLIB_ERR_NTFS_3G;
        }
 
-       *dir_ni_p = dir_ni;
-
        ret = ntfs_link(to_ni, dir_ni,
                        (ntfschar*)from_dentry->file_name,
                        from_dentry->file_name_len / 2);
-       if (ntfs_inode_close_in_dir(to_ni, dir_ni) || ret != 0) {
+       ret |= ntfs_inode_close(dir_ni);
+       ret |= ntfs_inode_close(to_ni);
+       if (ret) {
                ERROR_WITH_ERRNO("Could not create hard link `%s' => `%s'",
                                 from_dentry->full_path_utf8,
                                 inode->i_extracted_file);
@@ -356,57 +356,6 @@ static int apply_reparse_data(ntfs_inode *ni, const struct wim_dentry *dentry,
        return 0;
 }
 
-static int do_apply_dentry_ntfs(struct wim_dentry *dentry, ntfs_inode *dir_ni,
-                               struct apply_args *args);
-
-/*
- * If @dentry is part of a hard link group, search for hard-linked dentries in
- * the same directory that have a nonempty DOS (short) filename.  There should
- * be exactly 0 or 1 such dentries.  If there is 1, extract that dentry first,
- * so that the DOS name is correctly associated with the corresponding long name
- * in the Win32 namespace, and not any of the additional names in the POSIX
- * namespace created from hard links.
- */
-static int preapply_dentry_with_dos_name(struct wim_dentry *dentry,
-                                        ntfs_inode **dir_ni_p,
-                                        struct apply_args *args)
-{
-       struct wim_dentry *other;
-       struct wim_dentry *dentry_with_dos_name;
-
-       dentry_with_dos_name = NULL;
-       inode_for_each_dentry(other, dentry->d_inode) {
-               if (other != dentry && (dentry->parent == other->parent)
-                   && other->short_name_len)
-               {
-                       if (dentry_with_dos_name) {
-                               ERROR("Found multiple DOS names for file `%s' "
-                                     "in the same directory",
-                                     dentry_with_dos_name->full_path_utf8);
-                               return WIMLIB_ERR_INVALID_DENTRY;
-                       }
-                       dentry_with_dos_name = other;
-               }
-       }
-       /* If there's a dentry with a DOS name, extract it first */
-       if (dentry_with_dos_name && !dentry_with_dos_name->is_extracted) {
-               int ret;
-               ntfs_volume *vol = (*dir_ni_p)->vol;
-
-               DEBUG("pre-applying DOS name `%s'",
-                     dentry_with_dos_name->full_path_utf8);
-               ret = do_apply_dentry_ntfs(dentry_with_dos_name,
-                                          *dir_ni_p, args);
-               if (ret != 0)
-                       return ret;
-
-               *dir_ni_p = dentry_open_parent_ni(dentry, vol);
-               if (!*dir_ni_p)
-                       return WIMLIB_ERR_NTFS_3G;
-       }
-       return 0;
-}
-
 /*
  * Applies a WIM dentry to a NTFS filesystem.
  *
@@ -421,37 +370,22 @@ static int do_apply_dentry_ntfs(struct wim_dentry *dentry, ntfs_inode *dir_ni,
        int ret = 0;
        mode_t type;
        ntfs_inode *ni = NULL;
-       ntfs_volume *vol = dir_ni->vol;
        struct wim_inode *inode = dentry->d_inode;
        dentry->is_extracted = 1;
 
        if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
                type = S_IFDIR;
        } else {
-               /* 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 (ret != 0)
-                               return ret;
-               }
                type = S_IFREG;
                if (inode->i_nlink > 1) {
                        /* Inode has multiple dentries referencing it. */
-
                        if (inode->i_extracted_file) {
                                /* 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);
-                               if (ret == 0)
-                                       goto out_set_dos_name;
-                               else
-                                       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
@@ -502,10 +436,8 @@ static int do_apply_dentry_ntfs(struct wim_dentry *dentry, ntfs_inode *dir_ni,
                        goto out_close_dir_ni;
        }
 
-out_set_dos_name:
        /* 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,
@@ -515,20 +447,6 @@ out_set_dos_name:
                if (ret != 0)
                        goto out_close_dir_ni;
 
-               if (!ni) {
-                       /* Hardlink was made; linked inode needs to be looked up
-                        * again.  */
-                       ni = ntfs_pathname_to_inode(vol, dir_ni,
-                                                   dentry->file_name_utf8);
-                       if (!ni) {
-                               ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
-                                                dentry->full_path_utf8);
-                               FREE(short_name_utf8);
-                               ret = WIMLIB_ERR_NTFS_3G;
-                               goto out_close_dir_ni;
-                       }
-               }
-
                DEBUG("Setting short (DOS) name of `%s' to %s",
                      dentry->full_path_utf8, short_name_utf8);
 
@@ -541,9 +459,8 @@ out_set_dos_name:
                        ret = WIMLIB_ERR_NTFS_3G;
                }
                /* inodes have been closed by ntfs_set_ntfs_dos_name(). */
-               return ret;
+               goto out;
        }
-
 out_close_dir_ni:
        if (dir_ni) {
                if (ni) {
@@ -560,9 +477,8 @@ out_close_dir_ni:
                        ERROR_WITH_ERRNO("Failed to close inode of directory "
                                         "containing `%s'", dentry->full_path_utf8);
                }
-       } else {
-               wimlib_assert(ni == NULL);
        }
+out:
        return ret;
 }
 
@@ -592,16 +508,86 @@ int apply_dentry_ntfs(struct wim_dentry *dentry, void *arg)
        struct apply_args *args = arg;
        ntfs_volume *vol = args->vol;
        WIMStruct *w = args->w;
-       ntfs_inode *dir_ni;
+       struct wim_dentry *orig_dentry;
+       struct wim_dentry *other;
+       int ret;
 
+       /* Treat the root dentry specially. */
        if (dentry_is_root(dentry))
                return apply_root_dentry_ntfs(dentry, vol, w);
 
-       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;
+       /* NTFS filename namespaces need careful consideration.  A name for a
+        * NTFS file may be in either the POSIX, Win32, DOS, or Win32+DOS
+        * namespaces.  A NTFS file (a.k.a. inode) may have multiple names in
+        * multiple directories (i.e. hard links); however, a NTFS file can have
+        * at most 1 DOS name total.  Furthermore, a Win32 name is always
+        * associated with a DOS name (either as a Win32+DOS name, or a Win32
+        * name and a DOS name separately), which implies that a NTFS file can
+        * have at most 1 Win32 name.
+        *
+        * A WIM dentry just contains a "long name", which wimlib makes sure is
+        * non-empty, and a "short name", which may be empty.  So, wimlib must
+        * map these to the correct NTFS names.  wimlib collects all WIM
+        * dentries that map to the same NTFS inode and factors out the common
+        * information into a 'struct wim_inode', so this should make the
+        * mapping a little more obvious.  As a NTFS file can have at most 1 DOS
+        * name, a WIM inode cannot have more than 1 dentry with a non-empty
+        * short name, and this is checked in the verify_inode() function in
+        * verify.c.  Furthermore, a WIM dentry, if any, that has a DOS name
+        * must have a long name that corresponds to a Win32 name or Win32+DOS
+        * name.
+        *
+        * WIM dentries that have a long name but no associated short name are
+        * assumed to be in the POSIX namespace.
+        *
+        * So, given a WIM inode that is to map to a NTFS inode, we must apply
+        * the Win32 and DOS or Win32+DOS names, if they exist, then any
+        * additional (POSIX) names.  A caveat when actually doing this:  as
+        * confirmed by the libntfs-3g authors, ntfs_set_ntfs_dos_name() is only
+        * guaranteed to associate a DOS name with the appropriate long name if
+        * it's called when that long name is the only one in existence for that
+        * file.  So, this implies that the correct ordering of function calls
+        * to extract a NTFS file are:
+        *
+        *      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
+        *              with the long name just created.  This either changes
+        *              the POSIX name to Win32+DOS, or changes the POSIX name
+        *              to Win32 and creates a separate DOS name.
+        *      } else {
+        *              - Call ntfs_create() to create the first link to the
+        *              file in the POSIX namespace
+        *      }
+        *      - Call ntfs_link() to create the other names of the file, in the
+        *      POSIX namespace.
+        */
+again:
+       orig_dentry = NULL;
+       if (!dentry->d_inode->i_dos_name_extracted &&
+           dentry->short_name_len == 0)
+       {
+               inode_for_each_dentry(other, dentry->d_inode) {
+                       if (other->short_name_len != 0) {
+                               orig_dentry = dentry;
+                               dentry = other;
+                               break;
+                       }
+               }
+       }
+       dentry->d_inode->i_dos_name_extracted = 1;
+       ntfs_inode *dir_ni = dentry_open_parent_ni(dentry, vol);
+       if (dir_ni) {
+               ret = do_apply_dentry_ntfs(dentry, dir_ni, arg);
+               if (ret == 0 && orig_dentry != NULL) {
+                       dentry = orig_dentry;
+                       goto again;
+               }
+       } else {
+               ret = WIMLIB_ERR_NTFS_3G;
+       }
+       return ret;
 }
 
 /* Transfers the 100-nanosecond precision timestamps from a WIM dentry to a NTFS