]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Update archive_entry_link_resolver to copy the "wide" pathname for hardlinks on Windo...
authorDuncan Horn <40036384+dunhor@users.noreply.github.com>
Tue, 11 Jun 2024 04:23:13 +0000 (21:23 -0700)
committerGitHub <noreply@github.com>
Tue, 11 Jun 2024 04:23:13 +0000 (21:23 -0700)
On Windows, if you are using `archive_entry_link_resolver` and give it
an entry that links to past entry whose pathname was set using a "wide"
string that cannot be represented by the current locale (i.e. WCS -> MBS
conversion fails), this code will crash due to a null pointer read. This
updates to use the `_w` function instead on Windows.

Note: this is a partial cherry-pick from
https://github.com/libarchive/libarchive/pull/2095, which I'm going to
go through and break into smaller pieces in hopes of getting some things
in while discussion of other things can continue.

libarchive/archive_entry_link_resolver.c
libarchive/test/test_link_resolver.c

index 6c6173430250b57571424333446041246fe71180..c2fd6895f21e97b7b70ea6ed9c3384f0ccf4a4dc 100644 (file)
@@ -201,16 +201,26 @@ archive_entry_linkify(struct archive_entry_linkresolver *res,
                le = find_entry(res, *e);
                if (le != NULL) {
                        archive_entry_unset_size(*e);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+                       archive_entry_copy_hardlink_w(*e,
+                           archive_entry_pathname_w(le->canonical));
+#else
                        archive_entry_copy_hardlink(*e,
                            archive_entry_pathname(le->canonical));
+#endif
                } else
                        insert_entry(res, *e);
                return;
        case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE:
                le = find_entry(res, *e);
                if (le != NULL) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+                       archive_entry_copy_hardlink_w(*e,
+                           archive_entry_pathname_w(le->canonical));
+#else
                        archive_entry_copy_hardlink(*e,
                            archive_entry_pathname(le->canonical));
+#endif
                } else
                        insert_entry(res, *e);
                return;
@@ -229,8 +239,13 @@ archive_entry_linkify(struct archive_entry_linkresolver *res,
                        le->entry = t;
                        /* Make the old entry into a hardlink. */
                        archive_entry_unset_size(*e);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+                       archive_entry_copy_hardlink_w(*e,
+                           archive_entry_pathname_w(le->canonical));
+#else
                        archive_entry_copy_hardlink(*e,
                            archive_entry_pathname(le->canonical));
+#endif
                        /* If we ran out of links, return the
                         * final entry as well. */
                        if (le->links == 0) {
index 5bea9a463b300281a8573228250c258569d8bcce..6c6230c4d07c47fd573ff175c445dcebb771f8db 100644 (file)
@@ -202,3 +202,48 @@ DEFINE_TEST(test_link_resolver)
        test_linkify_old_cpio();
        test_linkify_new_cpio();
 }
+
+DEFINE_TEST(test_link_resolver_unicode_win)
+{
+#if !defined(_WIN32) || defined(__CYGWIN__)
+       skipping("This test is meant to verify unicode string handling"
+           " on Windows with UTF-16 names");
+       return;
+#else
+       struct archive_entry *entry, *e2;
+       struct archive_entry_linkresolver *resolver;
+
+       /* Initialize the resolver. */
+       assert(NULL != (resolver = archive_entry_linkresolver_new()));
+       archive_entry_linkresolver_set_strategy(resolver,
+           ARCHIVE_FORMAT_TAR_USTAR);
+
+       /* Create an entry with a unicode filename and 2 links. */
+       assert(NULL != (entry = archive_entry_new()));
+       archive_entry_copy_pathname_w(entry, L"\u4f60\u597d.txt");
+       archive_entry_set_ino(entry, 1);
+       archive_entry_set_dev(entry, 2);
+       archive_entry_set_nlink(entry, 2);
+       archive_entry_set_size(entry, 10);
+       archive_entry_linkify(resolver, &entry, &e2);
+
+       /* Shouldn't be altered, since it wasn't seen before. */
+       assert(e2 == NULL);
+       assertEqualWString(L"\u4f60\u597d.txt", archive_entry_pathname_w(entry));
+       assertEqualWString(NULL, archive_entry_hardlink_w(entry));
+       assertEqualInt(10, archive_entry_size(entry));
+
+       /* Link to the same file contents, but a new unicode name. */
+       archive_entry_copy_pathname_w(entry, L"\u518d\u89c1.txt");
+       archive_entry_linkify(resolver, &entry, &e2);
+
+       /* Size & link path should have changed. */
+       assert(e2 == NULL);
+       assertEqualWString(L"\u518d\u89c1.txt", archive_entry_pathname_w(entry));
+       assertEqualWString(L"\u4f60\u597d.txt", archive_entry_hardlink_w(entry));
+       assertEqualInt(0, archive_entry_size(entry));
+
+       archive_entry_free(entry);
+       archive_entry_linkresolver_free(resolver);
+#endif
+}