postgresql/src/bin/pg_dump/compress_none.c
Tom Lane 8b02c22bb4 Avoid leaking duplicated file descriptors in corner cases.
pg_dump's compression modules had variations on the theme of

	fp = fdopen(dup(fd), mode);
	if (fp == NULL)
	    // fail, reporting errno

which is problematic for two reasons.  First, if dup() succeeds but
fdopen() fails, we'd leak the duplicated FD.  That's not important
at present since the program will just exit immediately after failure
anyway; but perhaps someday we'll try to continue, making the resource
leak potentially significant.  Second, if dup() fails then fdopen()
will overwrite the useful errno (perhaps EMFILE) with a misleading
value EBADF, making it difficult to understand what went wrong.
Fix both issues by testing for dup() failure before proceeding to
the next call.

These failures are sufficiently unlikely, and the consequences minor
enough, that this doesn't seem worth the effort to back-patch.
But let's fix it in HEAD.

Author: Jianghua Yang <yjhjstz@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/62bbe34d-2315-4b42-b768-56d901aa83e1@gmail.com
2026-03-19 14:25:26 -04:00

287 lines
5.6 KiB
C

/*-------------------------------------------------------------------------
*
* compress_none.c
* Routines for archivers to read or write an uncompressed stream.
*
* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/bin/pg_dump/compress_none.c
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include <unistd.h>
#include "compress_none.h"
#include "pg_backup_utils.h"
/*----------------------
* Compressor API
*----------------------
*/
/*
* We buffer outgoing data, just to ensure that data blocks written to the
* archive file are of reasonable size. The read side could use this struct,
* but there's no need because it does not retain data across calls.
*/
typedef struct NoneCompressorState
{
char *buffer; /* buffer for unwritten data */
size_t buflen; /* allocated size of buffer */
size_t bufdata; /* amount of valid data currently in buffer */
} NoneCompressorState;
/*
* Private routines
*/
static void
ReadDataFromArchiveNone(ArchiveHandle *AH, CompressorState *cs)
{
size_t cnt;
char *buf;
size_t buflen;
buflen = DEFAULT_IO_BUFFER_SIZE;
buf = pg_malloc(buflen);
while ((cnt = cs->readF(AH, &buf, &buflen)))
{
ahwrite(buf, 1, cnt, AH);
}
free(buf);
}
static void
WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
const void *data, size_t dLen)
{
NoneCompressorState *nonecs = (NoneCompressorState *) cs->private_data;
size_t remaining = dLen;
while (remaining > 0)
{
size_t chunk;
/* Dump buffer if full */
if (nonecs->bufdata >= nonecs->buflen)
{
cs->writeF(AH, nonecs->buffer, nonecs->bufdata);
nonecs->bufdata = 0;
}
/* And fill it */
chunk = nonecs->buflen - nonecs->bufdata;
if (chunk > remaining)
chunk = remaining;
memcpy(nonecs->buffer + nonecs->bufdata, data, chunk);
nonecs->bufdata += chunk;
data = ((const char *) data) + chunk;
remaining -= chunk;
}
}
static void
EndCompressorNone(ArchiveHandle *AH, CompressorState *cs)
{
NoneCompressorState *nonecs = (NoneCompressorState *) cs->private_data;
if (nonecs)
{
/* Dump buffer if nonempty */
if (nonecs->bufdata > 0)
cs->writeF(AH, nonecs->buffer, nonecs->bufdata);
/* Free working state */
pg_free(nonecs->buffer);
pg_free(nonecs);
cs->private_data = NULL;
}
}
/*
* Public interface
*/
void
InitCompressorNone(CompressorState *cs,
const pg_compress_specification compression_spec)
{
cs->readData = ReadDataFromArchiveNone;
cs->writeData = WriteDataToArchiveNone;
cs->end = EndCompressorNone;
cs->compression_spec = compression_spec;
/*
* If the caller has defined a write function, prepare the necessary
* buffer.
*/
if (cs->writeF)
{
NoneCompressorState *nonecs;
nonecs = pg_malloc_object(NoneCompressorState);
nonecs->buflen = DEFAULT_IO_BUFFER_SIZE;
nonecs->buffer = pg_malloc(nonecs->buflen);
nonecs->bufdata = 0;
cs->private_data = nonecs;
}
}
/*----------------------
* Compress File API
*----------------------
*/
/*
* Private routines
*/
static size_t
read_none(void *ptr, size_t size, CompressFileHandle *CFH)
{
FILE *fp = (FILE *) CFH->private_data;
size_t ret;
ret = fread(ptr, 1, size, fp);
if (ferror(fp))
pg_fatal("could not read from input file: %m");
return ret;
}
static void
write_none(const void *ptr, size_t size, CompressFileHandle *CFH)
{
size_t ret;
errno = 0;
ret = fwrite(ptr, 1, size, (FILE *) CFH->private_data);
if (ret != size)
{
errno = (errno) ? errno : ENOSPC;
pg_fatal("could not write to file: %m");
}
}
static const char *
get_error_none(CompressFileHandle *CFH)
{
return strerror(errno);
}
static char *
gets_none(char *ptr, int size, CompressFileHandle *CFH)
{
return fgets(ptr, size, (FILE *) CFH->private_data);
}
static int
getc_none(CompressFileHandle *CFH)
{
FILE *fp = (FILE *) CFH->private_data;
int ret;
ret = fgetc(fp);
if (ret == EOF)
{
if (!feof(fp))
pg_fatal("could not read from input file: %m");
else
pg_fatal("could not read from input file: end of file");
}
return ret;
}
static bool
close_none(CompressFileHandle *CFH)
{
FILE *fp = (FILE *) CFH->private_data;
int ret = 0;
CFH->private_data = NULL;
if (fp)
{
errno = 0;
ret = fclose(fp);
if (ret != 0)
pg_log_error("could not close file: %m");
}
return ret == 0;
}
static bool
eof_none(CompressFileHandle *CFH)
{
return feof((FILE *) CFH->private_data) != 0;
}
static bool
open_none(const char *path, int fd, const char *mode, CompressFileHandle *CFH)
{
Assert(CFH->private_data == NULL);
if (fd >= 0)
{
int dup_fd = dup(fd);
if (dup_fd < 0)
return false;
CFH->private_data = fdopen(dup_fd, mode);
if (CFH->private_data == NULL)
{
close(dup_fd);
return false;
}
}
else
{
CFH->private_data = fopen(path, mode);
if (CFH->private_data == NULL)
return false;
}
return true;
}
static bool
open_write_none(const char *path, const char *mode, CompressFileHandle *CFH)
{
Assert(CFH->private_data == NULL);
CFH->private_data = fopen(path, mode);
if (CFH->private_data == NULL)
return false;
return true;
}
/*
* Public interface
*/
void
InitCompressFileHandleNone(CompressFileHandle *CFH,
const pg_compress_specification compression_spec)
{
CFH->open_func = open_none;
CFH->open_write_func = open_write_none;
CFH->read_func = read_none;
CFH->write_func = write_none;
CFH->gets_func = gets_none;
CFH->getc_func = getc_none;
CFH->close_func = close_none;
CFH->eof_func = eof_none;
CFH->get_error_func = get_error_none;
CFH->private_data = NULL;
}