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 <barryjzhao@tencent.com>
This commit is contained in:
Jun Zhao
2026-07-11 11:26:20 +08:00
committed by Jun Zhao
parent 683fae35e1
commit a6ed750664
5 changed files with 79 additions and 0 deletions

View File

@@ -780,6 +780,7 @@ SKIPHEADERS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh.h
SKIPHEADERS-$(CONFIG_NETWORK) += network.h rtsp.h
TESTPROGS = id3v2 \
mkdir \
seek \
url \
seek_utils

View File

@@ -1,6 +1,7 @@
/id3v2
/fifo_muxer
/imf
/mkdir
/movenc
/noproxy
/rtmpdh

66
libavformat/tests/mkdir.c Normal file
View File

@@ -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 <errno.h>
#include <stdio.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#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;
}

View File

@@ -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;
}
}

View File

@@ -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)