PR #23830 opened by John_Smith_LYI
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23830
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23830.patch

## Summary

When an output file already exists and overwrite is refused, ffmpeg currently
returns `AVERROR_EXIT`. The top-level ffmpeg exit path maps `AVERROR_EXIT` to
status 0, which makes `-n` and an interactive "no" look successful to scripts.

This patch changes only the overwrite-refusal branches in
`assert_file_overwrite()` to return `AVERROR(EEXIST)`. Normal `AVERROR_EXIT`
paths such as help/version output remain unchanged.

The change is limited to the `fftools` command-line program and does not affect
public API or ABI.

## Reproduction

Before this patch:

```sh
touch existing.wav
ffmpeg -nostdin -n -f lavfi -i anullsrc -f wav existing.wav
echo $?
```

The command reports that the file already exists, but exits with status 0.

After this patch, the same refusal exits non-zero and reports `File exists`.

## Test Coverage

Added `fate-ffmpeg-no-overwrite`, which creates an existing output file and
checks that `ffmpeg -n` exits non-zero.

Validated locally on commit `1dae8f89b24ff23565be29b689c061a2d499bbeb` with
the default configure path after installing `nasm`:

```sh
./configure

make -j$(nproc) fate-ffmpeg-no-overwrite V=1
make -j$(nproc) \
  fate-ffmpeg-filter-in-eof \
  fate-ffmpeg-filter_complex \
  fate-ffmpeg-filter_complex_audio \
  fate-ffmpeg-lavfi \
  fate-ffmpeg-loopback-decoding \
  fate-ffmpeg-no-overwrite \
  fate-ffmpeg-streamcopy-t

rsync -vrltLW --timeout=60 --contimeout=60 \
  rsync://fate-suite.ffmpeg.org/fate-suite/cover_art \
  /tmp/ffmpeg-fate-samples/
rsync -vrltLW --timeout=60 --contimeout=60 \
  rsync://fate-suite.ffmpeg.org/fate-suite/qt-surge-suite \
  /tmp/ffmpeg-fate-samples/
make -j$(nproc) TARGET_SAMPLES=/tmp/ffmpeg-fate-samples fate

git diff --check
git diff | ./tools/patcheck -
printf '%s\n\n%s\n' \
  'fftools/ffmpeg_opt: return an error when refusing overwrite' \
  'Return EEXIST when output overwrite is refused so ffmpeg exits with a 
non-zero status for -n and interactive no responses.' \
  | sh ./tools/check_commit_msg.sh
```

Interactive stdin check:

```sh
touch tests/data/fate/interactive-refusal.wav
printf 'n\n' | ./ffmpeg -f lavfi -i anullsrc -f wav 
tests/data/fate/interactive-refusal.wav
echo $?
```

The interactive refusal exits non-zero.

The non-interactive `-n` refusal path is covered by FATE. The interactive
refusal path was verified with stdin prompt input because it depends on prompt
handling.

## Notes

- `./configure` detected `standalone assembly yes` and `x86 assembler nasm`.
- Full local `make fate` passed with `TARGET_SAMPLES=/tmp/ffmpeg-fate-samples`.
  Only the sample directories needed by locally enabled MOV metadata tests were
  synced; full external FATE was not enabled with `SAMPLES=...`.
- `tools/patcheck` only reported "Missing changelog entry", treated as
  ignorable for this small CLI error-code fix.
- User-visible behavior changes only when overwrite is refused; scripts now get
  a failure status instead of status 0.


From 529320deba513aa3a4ccee020e85e5855805d017 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BC=A0=E6=B0=B8=E9=B9=8F?= <[email protected]>
Date: Thu, 16 Jul 2026 18:49:06 +0800
Subject: [PATCH] fftools/ffmpeg_opt: return an error when refusing overwrite
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Return EEXIST when output overwrite is refused so ffmpeg exits with a
non-zero status for -n and interactive no responses.

Signed-off-by: 张永鹏 <[email protected]>
---
 fftools/ffmpeg_opt.c  | 5 +++--
 tests/fate/ffmpeg.mak | 4 ++++
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index 4e05e2c56b..823486d3fb 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -20,6 +20,7 @@
 
 #include "config.h"
 
+#include <errno.h>
 #include <stdint.h>
 
 #if HAVE_SYS_RESOURCE_H
@@ -831,13 +832,13 @@ int assert_file_overwrite(const char *filename)
                 signal(SIGINT, SIG_DFL);
                 if (!read_yesno()) {
                     av_log(NULL, AV_LOG_FATAL, "Not overwriting - exiting\n");
-                    return AVERROR_EXIT;
+                    return AVERROR(EEXIST);
                 }
                 term_init();
             }
             else {
                 av_log(NULL, AV_LOG_FATAL, "File '%s' already exists. 
Exiting.\n", filename);
-                return AVERROR_EXIT;
+                return AVERROR(EEXIST);
             }
         }
     }
diff --git a/tests/fate/ffmpeg.mak b/tests/fate/ffmpeg.mak
index db6a171898..ca13073208 100644
--- a/tests/fate/ffmpeg.mak
+++ b/tests/fate/ffmpeg.mak
@@ -244,6 +244,10 @@ fate-ffmpeg-error-rate-fail: CMD = ffmpeg -i 
$(TARGET_SAMPLES)/mkv/h264_tta_unde
 fate-ffmpeg-error-rate-pass: CMD = ffmpeg -i 
$(TARGET_SAMPLES)/mkv/h264_tta_undecodable.mkv -c:v copy -f null - 
-max_error_rate 1
 FATE_SAMPLES_FFMPEG-$(call ENCDEC, PCM_S16LE TTA, NULL MATROSKA) += 
fate-ffmpeg-error-rate-fail fate-ffmpeg-error-rate-pass
 
+fate-ffmpeg-no-overwrite: CMP = null
+fate-ffmpeg-no-overwrite: CMD = out=tests/data/fate/no-overwrite.wav; touch 
$$out; ffmpeg -f lavfi -i anullsrc -t 0.01 -c:a pcm_u8 -f wav -n $$out; 
ret=$$?; rm -f $$out; test $$ret -ne 0
+FATE_FFMPEG-$(call ALLYES, LAVFI_INDEV ANULLSRC_FILTER PCM_U8_DECODER 
PCM_U8_ENCODER WAV_MUXER FILE_PROTOCOL) += fate-ffmpeg-no-overwrite
+
 # test input -bsf
 # use -stream_loop, because it tests flushing bsfs
 fate-ffmpeg-bsf-input: CMD = framecrc -stream_loop 2 -bsf setts=PTS*2 -i 
$(TARGET_SAMPLES)/hevc/extradata-reload-multi-stsd.mov -c copy
-- 
2.52.0

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

Reply via email to