From a6ed750664d6d8760bcee88f8aacec2e7d1b3ab7 Mon Sep 17 00:00:00 2001 From: Jun Zhao Date: Sat, 11 Jul 2026 11:26:20 +0800 Subject: [PATCH] avformat/utils: fix ff_mkdir_p() swallowing intermediate mkdir errors When creating nested directories (e.g. /a/b/c), a genuine mkdir() failure for an intermediate component was overwritten by later attempts, making the original failure harder to diagnose. Stop immediately on intermediate errors other than EEXIST, preserving errno for the caller. Existing path components remain non-fatal, as required by mkdir -p semantics. Add a regression test for creating a child below an existing parent directory. Signed-off-by: Jun Zhao --- libavformat/Makefile | 1 + libavformat/tests/.gitignore | 1 + libavformat/tests/mkdir.c | 66 ++++++++++++++++++++++++++++++++++++ libavformat/utils.c | 6 ++++ tests/fate/libavformat.mak | 5 +++ 5 files changed, 79 insertions(+) create mode 100644 libavformat/tests/mkdir.c diff --git a/libavformat/Makefile b/libavformat/Makefile index 9c761ba341..3595567464 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -780,6 +780,7 @@ SKIPHEADERS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh.h SKIPHEADERS-$(CONFIG_NETWORK) += network.h rtsp.h TESTPROGS = id3v2 \ + mkdir \ seek \ url \ seek_utils diff --git a/libavformat/tests/.gitignore b/libavformat/tests/.gitignore index f9aac1f9e0..951ccf0f37 100644 --- a/libavformat/tests/.gitignore +++ b/libavformat/tests/.gitignore @@ -1,6 +1,7 @@ /id3v2 /fifo_muxer /imf +/mkdir /movenc /noproxy /rtmpdh diff --git a/libavformat/tests/mkdir.c b/libavformat/tests/mkdir.c new file mode 100644 index 0000000000..42e5c0562d --- /dev/null +++ b/libavformat/tests/mkdir.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2026 Jun Zhao + * + * 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 +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include "libavutil/random_seed.h" + +#include "libavformat/internal.h" +#include "libavformat/os_support.h" + +int main(void) +{ + char parent[64]; + char child[80]; + + snprintf(parent, sizeof(parent), "ff-mkdir-test-%08x", av_get_random_seed()); + snprintf(child, sizeof(child), "%s/child", parent); + + if (mkdir(parent, 0755) < 0) { + perror("mkdir parent"); + return 1; + } + + if (ff_mkdir_p(child) < 0) { + perror("ff_mkdir_p"); + rmdir(parent); + return 1; + } + + if (rmdir(child) < 0) { + perror("rmdir child"); + rmdir(parent); + return 1; + } + + if (rmdir(parent) < 0) { + perror("rmdir parent"); + return 1; + } + + return 0; +} diff --git a/libavformat/utils.c b/libavformat/utils.c index c64ff27418..cb0ae7444e 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -443,6 +443,12 @@ int ff_mkdir_p(const char *path) tmp_ch = *pos; *pos = '\0'; ret = mkdir(temp, 0755); + if (ret < 0 && errno != EEXIST) { + int err = errno; + av_free(temp); + errno = err; + return ret; + } *pos = tmp_ch; } } diff --git a/tests/fate/libavformat.mak b/tests/fate/libavformat.mak index 3b3a8a1177..989515a646 100644 --- a/tests/fate/libavformat.mak +++ b/tests/fate/libavformat.mak @@ -2,6 +2,11 @@ #fate-async: libavformat/tests/async$(EXESUF) #fate-async: CMD = run libavformat/tests/async +FATE_LIBAVFORMAT += fate-mkdir +fate-mkdir: libavformat/tests/mkdir$(EXESUF) +fate-mkdir: CMD = run libavformat/tests/mkdir$(EXESUF) +fate-mkdir: CMP = null + FATE_LIBAVFORMAT-$(CONFIG_NETWORK) += fate-noproxy fate-noproxy: libavformat/tests/noproxy$(EXESUF) fate-noproxy: CMD = run libavformat/tests/noproxy$(EXESUF)