Only call ftruncate() once, when the output file descriptor is closed at the end, instead of after every lseek() - this corrects the file growth on XFS but has the side effect that writing to stdout in >> (append) mode truncates the file to the wrong size (#45).

This commit is contained in:
Andrew Wood
2025-10-15 22:32:21 +01:00
parent 71f929e29c
commit 5c15337662
2 changed files with 27 additions and 16 deletions
+27
View File
@@ -257,6 +257,31 @@ void pv_freecontents_calc(pvtransfercalc_t calc)
}
/*
* Truncate the output file descriptor to its current position, if it's a
* valid fd, we're in sparse output mode, and no lseek() failed.
*/
static void pv_truncate_output(pvstate_t state)
{
off_t current_offset;
if (!state->control.sparse_output)
return;
if (state->transfer.output_not_seekable)
return;
if (state->control.output_fd < 0)
return;
current_offset = lseek(state->control.output_fd, (off_t) 0, SEEK_CUR);
if (current_offset == (off_t) - 1)
return;
if (0 != ftruncate(state->control.output_fd, current_offset)) {
debug("%s: %s", "output ftruncate() failed", strerror(errno));
}
}
/*
* Free a state structure, after which it can no longer be used.
*/
@@ -270,6 +295,7 @@ void pv_state_free(pvstate_t state)
* still know the program name and output filename.
*/
if (state->control.output_fd >= 0) {
pv_truncate_output(state);
if (STDOUT_FILENO != state->control.output_fd) {
if (close(state->control.output_fd) < 0) {
pv_error("%s: %s",
@@ -631,6 +657,7 @@ void pv_state_output_set(pvstate_t state, int fd, const char *name)
* Close any previous output file first, so we can report any errors
* before we store the new output filename.
*/
pv_truncate_output(state);
if (state->control.output_fd >= 0 && state->control.output_fd != STDOUT_FILENO) {
if (close(state->control.output_fd) < 0) {
pv_error("%s: %s",
-16
View File
@@ -709,22 +709,6 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long
if (output_offset == (off_t) - 1) {
debug("%s: %s", "output lseek() failed", strerror(errno));
state->transfer.output_not_seekable = true;
} else if (0 != ftruncate(state->control.output_fd, output_offset)) {
/* TODO: don't ftruncate() immediately. */
/*
* On XFS filesystems it looks like
* this can actually make a sparse
* file take up more space than a
* regular file, though it's not
* obvious yet whether the
* ftruncate() is causing it or just
* the lseek().
*
* Maybe just ftruncate() at the end
* of the program.
*/
debug("%s: %s", "output ftruncate() failed", strerror(errno));
state->transfer.output_not_seekable = true;
} else {
/* Seek successful - skip write. */
nwritten = state->transfer.to_write;