[PR] avformat/os_support: fix return value of win32_rename (PR #23890)

cdecker08 via ffmpeg-devel <[email protected]> Thu, 23 Jul 2026 15:33:05 -0000
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <178482078651.59.10962633584579552102@29965ddac10e>
PR #23890 opened by cdecker08
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23890
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23890.patch

The return value of MoveFileExW was not being correctly interpreted,
see https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-movefileexw

Also, added unit tests to exercise the rename function.

NOTE: This is a small bug we discovered at plex and meant to test the PR waters, many more bugfixes/features to come


>From 498a146dac633f4905bc0937d30d94d8a590969e Mon Sep 17 00:00:00 2001
From: Christopher Decker <[email protected]>
Date: Thu, 23 Jul 2026 11:01:30 -0400
Subject: [PATCH] avformat/os_support: fix return value of win32_rename

The return value of MoveFileExW was not being correctly interpreted,
see https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-movefileexw

Also, added unit tests to exercise the rename function.

Signed-off-by: Christopher Decker <[email protected]>
---
 libavformat/Makefile       |  1 +
 libavformat/os_support.h   |  4 +-
 libavformat/tests/rename.c | 97 ++++++++++++++++++++++++++++++++++++++
 tests/fate/libavformat.mak |  5 ++
 4 files changed, 105 insertions(+), 2 deletions(-)
 create mode 100644 libavformat/tests/rename.c

diff --git a/libavformat/Makefile b/libavformat/Makefile
index 2f85e8a2ad..2f53c78569 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -780,6 +780,7 @@ SKIPHEADERS-$(CONFIG_NETWORK)            += network.h rtsp.h
 
 TESTPROGS = id3v2                                                       \
             mkdir                                                       \
+            rename                                                      \
             seek                                                        \
             url                                                         \
             seek_utils
diff --git a/libavformat/os_support.h b/libavformat/os_support.h
index 8479163f70..6536ceb907 100644
--- a/libavformat/os_support.h
+++ b/libavformat/os_support.h
@@ -279,7 +279,7 @@ static inline int win32_rename(const char *src_utf8, const char *dest_utf8)
         goto fallback;
     }
 
-    ret = MoveFileExW(src_w, dest_w, MOVEFILE_REPLACE_EXISTING);
+    ret = !MoveFileExW(src_w, dest_w, MOVEFILE_REPLACE_EXISTING);
     av_free(src_w);
     av_free(dest_w);
     // Lacking proper mapping from GetLastError() error codes to errno codes
@@ -290,7 +290,7 @@ static inline int win32_rename(const char *src_utf8, const char *dest_utf8)
 fallback:
     /* filename may be be in CP_ACP */
 #if !HAVE_UWP
-    ret = MoveFileExA(src_utf8, dest_utf8, MOVEFILE_REPLACE_EXISTING);
+    ret = !MoveFileExA(src_utf8, dest_utf8, MOVEFILE_REPLACE_EXISTING);
     if (ret)
         errno = EPERM;
 #else
diff --git a/libavformat/tests/rename.c b/libavformat/tests/rename.c
new file mode 100644
index 0000000000..e9eec9b422
--- /dev/null
+++ b/libavformat/tests/rename.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2026 Christopher Decker
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "libavutil/random_seed.h"
+
+#include "libavformat/os_support.h"
+
+static int create_file(const char *path)
+{
+    FILE *f = fopen(path, "wb");
+    if (!f)
+        return -1;
+    fputs("ffmpeg rename test\n", f);
+    fclose(f);
+    return 0;
+}
+
+static int file_exists(const char *path)
+{
+    FILE *f = fopen(path, "rb");
+    if (!f)
+        return 0;
+    fclose(f);
+    return 1;
+}
+
+int main(void)
+{
+    char src[64];
+    char dst[64];
+    unsigned seed = av_get_random_seed();
+    int ret = 0;
+
+    snprintf(src, sizeof(src), "ff-rename-test-%08x.src", seed);
+    snprintf(dst, sizeof(dst), "ff-rename-test-%08x.dst", seed);
+
+    if (create_file(src) < 0) {
+        perror("create src");
+        return 1;
+    }
+
+    /* rename() must follow POSIX semantics and return 0 on success. */
+    if (rename(src, dst) != 0) {
+        perror("rename");
+        ret = 1;
+        goto cleanup;
+    }
+
+    if (file_exists(src)) {
+        fprintf(stderr, "source still exists after rename\n");
+        ret = 1;
+        goto cleanup;
+    }
+
+    if (!file_exists(dst)) {
+        fprintf(stderr, "destination missing after rename\n");
+        ret = 1;
+        goto cleanup;
+    }
+
+    /* Renaming a nonexistent source must fail with a nonzero return. */
+    if (rename(src, dst) == 0) {
+        fprintf(stderr, "rename of nonexistent source unexpectedly succeeded\n");
+        ret = 1;
+        goto cleanup;
+    }
+
+cleanup:
+    unlink(src);
+    unlink(dst);
+    return ret;
+}
diff --git a/tests/fate/libavformat.mak b/tests/fate/libavformat.mak
index 989515a646..7bd20cf9c8 100644
--- a/tests/fate/libavformat.mak
+++ b/tests/fate/libavformat.mak
@@ -7,6 +7,11 @@ fate-mkdir: libavformat/tests/mkdir$(EXESUF)
 fate-mkdir: CMD = run libavformat/tests/mkdir$(EXESUF)
 fate-mkdir: CMP = null
 
+FATE_LIBAVFORMAT += fate-rename
+fate-rename: libavformat/tests/rename$(EXESUF)
+fate-rename: CMD = run libavformat/tests/rename$(EXESUF)
+fate-rename: CMP = null
+
 FATE_LIBAVFORMAT-$(CONFIG_NETWORK) += fate-noproxy
 fate-noproxy: libavformat/tests/noproxy$(EXESUF)
 fate-noproxy: CMD = run libavformat/tests/noproxy$(EXESUF)
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]