2003-11-12 00:52:45 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* copydir.c
|
|
|
|
* copies a directory
|
|
|
|
*
|
2017-01-03 19:48:53 +01:00
|
|
|
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
|
2003-11-12 00:52:45 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
2003-05-15 19:59:17 +02:00
|
|
|
* While "xcopy /e /i /q" works fine for copying directories, on Windows XP
|
2003-09-10 22:12:01 +02:00
|
|
|
* it requires a Window handle which prevents it from working when invoked
|
2003-05-15 19:59:17 +02:00
|
|
|
* as a service.
|
2003-09-10 22:12:01 +02:00
|
|
|
*
|
2003-11-12 00:52:45 +01:00
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/storage/file/copydir.c
|
2003-11-12 00:52:45 +01:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
2003-05-15 19:59:17 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2005-08-02 21:02:32 +02:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2010-11-12 22:39:53 +01:00
|
|
|
#include "storage/copydir.h"
|
2004-02-24 00:03:10 +01:00
|
|
|
#include "storage/fd.h"
|
2010-07-01 22:12:40 +02:00
|
|
|
#include "miscadmin.h"
|
2004-02-24 00:03:10 +01:00
|
|
|
|
2005-08-02 21:02:32 +02:00
|
|
|
|
2003-09-10 22:12:01 +02:00
|
|
|
/*
|
2005-08-02 21:02:32 +02:00
|
|
|
* copydir: copy a directory
|
2003-09-10 22:12:01 +02:00
|
|
|
*
|
2005-08-02 21:02:32 +02:00
|
|
|
* If recurse is false, subdirectories are ignored. Anything that's not
|
|
|
|
* a directory or a regular file is ignored.
|
2003-09-10 22:12:01 +02:00
|
|
|
*/
|
2005-08-02 21:02:32 +02:00
|
|
|
void
|
|
|
|
copydir(char *fromdir, char *todir, bool recurse)
|
2003-05-15 19:59:17 +02:00
|
|
|
{
|
|
|
|
DIR *xldir;
|
|
|
|
struct dirent *xlde;
|
2005-08-02 21:02:32 +02:00
|
|
|
char fromfile[MAXPGPATH];
|
|
|
|
char tofile[MAXPGPATH];
|
2003-05-15 19:59:17 +02:00
|
|
|
|
2010-12-10 23:35:33 +01:00
|
|
|
if (mkdir(todir, S_IRWXU) != 0)
|
2005-08-02 21:02:32 +02:00
|
|
|
ereport(ERROR,
|
2003-07-27 19:10:07 +02:00
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not create directory \"%s\": %m", todir)));
|
2005-08-02 21:02:32 +02:00
|
|
|
|
2004-02-24 00:03:10 +01:00
|
|
|
xldir = AllocateDir(fromdir);
|
2003-05-15 19:59:17 +02:00
|
|
|
if (xldir == NULL)
|
2005-08-02 21:02:32 +02:00
|
|
|
ereport(ERROR,
|
2003-07-27 19:10:07 +02:00
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not open directory \"%s\": %m", fromdir)));
|
2005-08-02 21:02:32 +02:00
|
|
|
|
|
|
|
while ((xlde = ReadDir(xldir, fromdir)) != NULL)
|
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
struct stat fst;
|
2005-08-02 21:02:32 +02:00
|
|
|
|
2010-07-06 21:19:02 +02:00
|
|
|
/* If we got a cancel signal during the copy of the directory, quit */
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
2010-07-01 22:12:40 +02:00
|
|
|
|
2005-10-15 04:49:52 +02:00
|
|
|
if (strcmp(xlde->d_name, ".") == 0 ||
|
2005-08-02 21:02:32 +02:00
|
|
|
strcmp(xlde->d_name, "..") == 0)
|
2005-10-15 04:49:52 +02:00
|
|
|
continue;
|
2005-08-02 21:02:32 +02:00
|
|
|
|
|
|
|
snprintf(fromfile, MAXPGPATH, "%s/%s", fromdir, xlde->d_name);
|
|
|
|
snprintf(tofile, MAXPGPATH, "%s/%s", todir, xlde->d_name);
|
|
|
|
|
2006-07-19 00:36:46 +02:00
|
|
|
if (lstat(fromfile, &fst) < 0)
|
2005-08-02 21:02:32 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
2005-10-29 02:31:52 +02:00
|
|
|
errmsg("could not stat file \"%s\": %m", fromfile)));
|
2005-08-02 21:02:32 +02:00
|
|
|
|
Fix a number of places that were making file-type tests infelicitously.
The places that did, eg,
(statbuf.st_mode & S_IFMT) == S_IFDIR
were correct, but there is no good reason not to use S_ISDIR() instead,
especially when that's what the other 90% of our code does. The places
that did, eg,
(statbuf.st_mode & S_IFDIR)
were flat out *wrong* and would fail in various platform-specific ways,
eg a symlink could be mistaken for a regular file on most Unixen.
The actual impact of this is probably small, since the problem cases
seem to always involve symlinks or sockets, which are unlikely to be
found in the directories that PG code might be scanning. But it's
clearly trouble waiting to happen, so patch all the way back anyway.
(There seem to be no occurrences of the mistake in 7.4.)
2008-03-31 03:31:43 +02:00
|
|
|
if (S_ISDIR(fst.st_mode))
|
2005-08-02 21:02:32 +02:00
|
|
|
{
|
|
|
|
/* recurse to handle subdirectories */
|
|
|
|
if (recurse)
|
|
|
|
copydir(fromfile, tofile, true);
|
|
|
|
}
|
Fix a number of places that were making file-type tests infelicitously.
The places that did, eg,
(statbuf.st_mode & S_IFMT) == S_IFDIR
were correct, but there is no good reason not to use S_ISDIR() instead,
especially when that's what the other 90% of our code does. The places
that did, eg,
(statbuf.st_mode & S_IFDIR)
were flat out *wrong* and would fail in various platform-specific ways,
eg a symlink could be mistaken for a regular file on most Unixen.
The actual impact of this is probably small, since the problem cases
seem to always involve symlinks or sockets, which are unlikely to be
found in the directories that PG code might be scanning. But it's
clearly trouble waiting to happen, so patch all the way back anyway.
(There seem to be no occurrences of the mistake in 7.4.)
2008-03-31 03:31:43 +02:00
|
|
|
else if (S_ISREG(fst.st_mode))
|
2005-08-02 21:02:32 +02:00
|
|
|
copy_file(fromfile, tofile);
|
2003-05-15 19:59:17 +02:00
|
|
|
}
|
2010-02-22 03:50:10 +01:00
|
|
|
FreeDir(xldir);
|
2003-05-15 19:59:17 +02:00
|
|
|
|
2010-02-14 18:50:52 +01:00
|
|
|
/*
|
2010-02-22 03:50:10 +01:00
|
|
|
* Be paranoid here and fsync all files to ensure the copy is really done.
|
2012-07-22 02:10:29 +02:00
|
|
|
* But if fsync is disabled, we're done.
|
2010-02-14 18:50:52 +01:00
|
|
|
*/
|
2012-07-22 02:10:29 +02:00
|
|
|
if (!enableFsync)
|
|
|
|
return;
|
|
|
|
|
2010-02-22 03:50:10 +01:00
|
|
|
xldir = AllocateDir(todir);
|
2010-02-15 01:50:57 +01:00
|
|
|
if (xldir == NULL)
|
2010-02-14 18:50:52 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
2010-02-22 03:50:10 +01:00
|
|
|
errmsg("could not open directory \"%s\": %m", todir)));
|
2010-02-15 01:50:57 +01:00
|
|
|
|
2010-02-22 03:50:10 +01:00
|
|
|
while ((xlde = ReadDir(xldir, todir)) != NULL)
|
2010-02-15 01:50:57 +01:00
|
|
|
{
|
2010-02-15 12:40:49 +01:00
|
|
|
struct stat fst;
|
|
|
|
|
2010-02-15 01:50:57 +01:00
|
|
|
if (strcmp(xlde->d_name, ".") == 0 ||
|
|
|
|
strcmp(xlde->d_name, "..") == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
snprintf(tofile, MAXPGPATH, "%s/%s", todir, xlde->d_name);
|
2010-02-15 12:40:49 +01:00
|
|
|
|
2010-02-22 03:50:10 +01:00
|
|
|
/*
|
|
|
|
* We don't need to sync subdirectories here since the recursive
|
|
|
|
* copydir will do it before it returns
|
|
|
|
*/
|
|
|
|
if (lstat(tofile, &fst) < 0)
|
2010-02-15 12:40:49 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
2010-02-22 03:50:10 +01:00
|
|
|
errmsg("could not stat file \"%s\": %m", tofile)));
|
|
|
|
|
2010-02-15 12:40:49 +01:00
|
|
|
if (S_ISREG(fst.st_mode))
|
2010-02-28 22:05:30 +01:00
|
|
|
fsync_fname(tofile, false);
|
2010-02-15 01:50:57 +01:00
|
|
|
}
|
|
|
|
FreeDir(xldir);
|
|
|
|
|
2010-02-22 03:50:10 +01:00
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* It's important to fsync the destination directory itself as individual
|
|
|
|
* file fsyncs don't guarantee that the directory entry for the file is
|
|
|
|
* synced. Recent versions of ext4 have made the window much wider but
|
|
|
|
* it's been true for ext3 and other filesystems in the past.
|
2010-02-15 01:50:57 +01:00
|
|
|
*/
|
2010-02-28 22:05:30 +01:00
|
|
|
fsync_fname(todir, true);
|
2005-08-02 21:02:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* copy one file
|
|
|
|
*/
|
2010-12-29 12:48:53 +01:00
|
|
|
void
|
2005-08-02 21:02:32 +02:00
|
|
|
copy_file(char *fromfile, char *tofile)
|
|
|
|
{
|
2005-09-02 20:55:32 +02:00
|
|
|
char *buffer;
|
2005-08-02 21:02:32 +02:00
|
|
|
int srcfd;
|
|
|
|
int dstfd;
|
|
|
|
int nbytes;
|
2010-02-15 01:50:57 +01:00
|
|
|
off_t offset;
|
2005-08-02 21:02:32 +02:00
|
|
|
|
2005-09-02 20:55:32 +02:00
|
|
|
/* Use palloc to ensure we get a maxaligned buffer */
|
|
|
|
#define COPY_BUF_SIZE (8 * BLCKSZ)
|
|
|
|
|
|
|
|
buffer = palloc(COPY_BUF_SIZE);
|
|
|
|
|
2005-08-02 21:02:32 +02:00
|
|
|
/*
|
|
|
|
* Open the files
|
|
|
|
*/
|
Add OpenTransientFile, with automatic cleanup at end-of-xact.
Files opened with BasicOpenFile or PathNameOpenFile are not automatically
cleaned up on error. That puts unnecessary burden on callers that only want
to keep the file open for a short time. There is AllocateFile, but that
returns a buffered FILE * stream, which in many cases is not the nicest API
to work with. So add function called OpenTransientFile, which returns a
unbuffered fd that's cleaned up like the FILE* returned by AllocateFile().
This plugs a few rare fd leaks in error cases:
1. copy_file() - fixed by by using OpenTransientFile instead of BasicOpenFile
2. XLogFileInit() - fixed by adding close() calls to the error cases. Can't
use OpenTransientFile here because the fd is supposed to persist over
transaction boundaries.
3. lo_import/lo_export - fixed by using OpenTransientFile instead of
PathNameOpenFile.
In addition to plugging those leaks, this replaces many BasicOpenFile() calls
with OpenTransientFile() that were not leaking, because the code meticulously
closed the file on error. That wasn't strictly necessary, but IMHO it's good
for robustness.
The same leaks exist in older versions, but given the rarity of the issues,
I'm not backpatching this. Not yet, anyway - it might be good to backpatch
later, after this mechanism has had some more testing in master branch.
2012-11-27 09:25:50 +01:00
|
|
|
srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY, 0);
|
2005-08-02 21:02:32 +02:00
|
|
|
if (srcfd < 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not open file \"%s\": %m", fromfile)));
|
|
|
|
|
Add OpenTransientFile, with automatic cleanup at end-of-xact.
Files opened with BasicOpenFile or PathNameOpenFile are not automatically
cleaned up on error. That puts unnecessary burden on callers that only want
to keep the file open for a short time. There is AllocateFile, but that
returns a buffered FILE * stream, which in many cases is not the nicest API
to work with. So add function called OpenTransientFile, which returns a
unbuffered fd that's cleaned up like the FILE* returned by AllocateFile().
This plugs a few rare fd leaks in error cases:
1. copy_file() - fixed by by using OpenTransientFile instead of BasicOpenFile
2. XLogFileInit() - fixed by adding close() calls to the error cases. Can't
use OpenTransientFile here because the fd is supposed to persist over
transaction boundaries.
3. lo_import/lo_export - fixed by using OpenTransientFile instead of
PathNameOpenFile.
In addition to plugging those leaks, this replaces many BasicOpenFile() calls
with OpenTransientFile() that were not leaking, because the code meticulously
closed the file on error. That wasn't strictly necessary, but IMHO it's good
for robustness.
The same leaks exist in older versions, but given the rarity of the issues,
I'm not backpatching this. Not yet, anyway - it might be good to backpatch
later, after this mechanism has had some more testing in master branch.
2012-11-27 09:25:50 +01:00
|
|
|
dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
|
|
|
|
S_IRUSR | S_IWUSR);
|
2005-08-02 21:02:32 +02:00
|
|
|
if (dstfd < 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not create file \"%s\": %m", tofile)));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do the data copying.
|
|
|
|
*/
|
2010-02-26 03:01:40 +01:00
|
|
|
for (offset = 0;; offset += nbytes)
|
2003-05-15 19:59:17 +02:00
|
|
|
{
|
2010-07-06 21:19:02 +02:00
|
|
|
/* If we got a cancel signal during the copy of the file, quit */
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
2010-07-01 22:12:40 +02:00
|
|
|
|
2005-09-02 20:55:32 +02:00
|
|
|
nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
|
2005-08-02 21:02:32 +02:00
|
|
|
if (nbytes < 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not read file \"%s\": %m", fromfile)));
|
|
|
|
if (nbytes == 0)
|
|
|
|
break;
|
|
|
|
errno = 0;
|
|
|
|
if ((int) write(dstfd, buffer, nbytes) != nbytes)
|
2003-08-04 02:43:34 +02:00
|
|
|
{
|
2005-08-02 21:02:32 +02:00
|
|
|
/* if write didn't set errno, assume problem is no disk space */
|
|
|
|
if (errno == 0)
|
|
|
|
errno = ENOSPC;
|
|
|
|
ereport(ERROR,
|
2003-08-04 02:43:34 +02:00
|
|
|
(errcode_for_file_access(),
|
2005-08-02 21:02:32 +02:00
|
|
|
errmsg("could not write to file \"%s\": %m", tofile)));
|
2003-08-04 02:43:34 +02:00
|
|
|
}
|
2005-03-24 03:11:20 +01:00
|
|
|
|
2010-02-15 01:50:57 +01:00
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* We fsync the files later but first flush them to avoid spamming the
|
|
|
|
* cache and hopefully get the kernel to start writing them out before
|
Allow to trigger kernel writeback after a configurable number of writes.
Currently writes to the main data files of postgres all go through the
OS page cache. This means that some operating systems can end up
collecting a large number of dirty buffers in their respective page
caches. When these dirty buffers are flushed to storage rapidly, be it
because of fsync(), timeouts, or dirty ratios, latency for other reads
and writes can increase massively. This is the primary reason for
regular massive stalls observed in real world scenarios and artificial
benchmarks; on rotating disks stalls on the order of hundreds of seconds
have been observed.
On linux it is possible to control this by reducing the global dirty
limits significantly, reducing the above problem. But global
configuration is rather problematic because it'll affect other
applications; also PostgreSQL itself doesn't always generally want this
behavior, e.g. for temporary files it's undesirable.
Several operating systems allow some control over the kernel page
cache. Linux has sync_file_range(2), several posix systems have msync(2)
and posix_fadvise(2). sync_file_range(2) is preferable because it
requires no special setup, whereas msync() requires the to-be-flushed
range to be mmap'ed. For the purpose of flushing dirty data
posix_fadvise(2) is the worst alternative, as flushing dirty data is
just a side-effect of POSIX_FADV_DONTNEED, which also removes the pages
from the page cache. Thus the feature is enabled by default only on
linux, but can be enabled on all systems that have any of the above
APIs.
While desirable and likely possible this patch does not contain an
implementation for windows.
With the infrastructure added, writes made via checkpointer, bgwriter
and normal user backends can be flushed after a configurable number of
writes. Each of these sources of writes controlled by a separate GUC,
checkpointer_flush_after, bgwriter_flush_after and backend_flush_after
respectively; they're separate because the number of flushes that are
good are separate, and because the performance considerations of
controlled flushing for each of these are different.
A later patch will add checkpoint sorting - after that flushes from the
ckeckpoint will almost always be desirable. Bgwriter flushes are most of
the time going to be random, which are slow on lots of storage hardware.
Flushing in backends works well if the storage and bgwriter can keep up,
but if not it can have negative consequences. This patch is likely to
have negative performance consequences without checkpoint sorting, but
unfortunately so has sorting without flush control.
Discussion: alpine.DEB.2.10.1506011320000.28433@sto
Author: Fabien Coelho and Andres Freund
2016-02-19 21:13:05 +01:00
|
|
|
* the fsync comes.
|
2010-02-15 01:50:57 +01:00
|
|
|
*/
|
Allow to trigger kernel writeback after a configurable number of writes.
Currently writes to the main data files of postgres all go through the
OS page cache. This means that some operating systems can end up
collecting a large number of dirty buffers in their respective page
caches. When these dirty buffers are flushed to storage rapidly, be it
because of fsync(), timeouts, or dirty ratios, latency for other reads
and writes can increase massively. This is the primary reason for
regular massive stalls observed in real world scenarios and artificial
benchmarks; on rotating disks stalls on the order of hundreds of seconds
have been observed.
On linux it is possible to control this by reducing the global dirty
limits significantly, reducing the above problem. But global
configuration is rather problematic because it'll affect other
applications; also PostgreSQL itself doesn't always generally want this
behavior, e.g. for temporary files it's undesirable.
Several operating systems allow some control over the kernel page
cache. Linux has sync_file_range(2), several posix systems have msync(2)
and posix_fadvise(2). sync_file_range(2) is preferable because it
requires no special setup, whereas msync() requires the to-be-flushed
range to be mmap'ed. For the purpose of flushing dirty data
posix_fadvise(2) is the worst alternative, as flushing dirty data is
just a side-effect of POSIX_FADV_DONTNEED, which also removes the pages
from the page cache. Thus the feature is enabled by default only on
linux, but can be enabled on all systems that have any of the above
APIs.
While desirable and likely possible this patch does not contain an
implementation for windows.
With the infrastructure added, writes made via checkpointer, bgwriter
and normal user backends can be flushed after a configurable number of
writes. Each of these sources of writes controlled by a separate GUC,
checkpointer_flush_after, bgwriter_flush_after and backend_flush_after
respectively; they're separate because the number of flushes that are
good are separate, and because the performance considerations of
controlled flushing for each of these are different.
A later patch will add checkpoint sorting - after that flushes from the
ckeckpoint will almost always be desirable. Bgwriter flushes are most of
the time going to be random, which are slow on lots of storage hardware.
Flushing in backends works well if the storage and bgwriter can keep up,
but if not it can have negative consequences. This patch is likely to
have negative performance consequences without checkpoint sorting, but
unfortunately so has sorting without flush control.
Discussion: alpine.DEB.2.10.1506011320000.28433@sto
Author: Fabien Coelho and Andres Freund
2016-02-19 21:13:05 +01:00
|
|
|
pg_flush_data(dstfd, offset, nbytes);
|
2010-02-15 01:50:57 +01:00
|
|
|
}
|
2003-05-15 19:59:17 +02:00
|
|
|
|
Add OpenTransientFile, with automatic cleanup at end-of-xact.
Files opened with BasicOpenFile or PathNameOpenFile are not automatically
cleaned up on error. That puts unnecessary burden on callers that only want
to keep the file open for a short time. There is AllocateFile, but that
returns a buffered FILE * stream, which in many cases is not the nicest API
to work with. So add function called OpenTransientFile, which returns a
unbuffered fd that's cleaned up like the FILE* returned by AllocateFile().
This plugs a few rare fd leaks in error cases:
1. copy_file() - fixed by by using OpenTransientFile instead of BasicOpenFile
2. XLogFileInit() - fixed by adding close() calls to the error cases. Can't
use OpenTransientFile here because the fd is supposed to persist over
transaction boundaries.
3. lo_import/lo_export - fixed by using OpenTransientFile instead of
PathNameOpenFile.
In addition to plugging those leaks, this replaces many BasicOpenFile() calls
with OpenTransientFile() that were not leaking, because the code meticulously
closed the file on error. That wasn't strictly necessary, but IMHO it's good
for robustness.
The same leaks exist in older versions, but given the rarity of the issues,
I'm not backpatching this. Not yet, anyway - it might be good to backpatch
later, after this mechanism has had some more testing in master branch.
2012-11-27 09:25:50 +01:00
|
|
|
if (CloseTransientFile(dstfd))
|
2005-08-02 21:02:32 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not close file \"%s\": %m", tofile)));
|
|
|
|
|
Add OpenTransientFile, with automatic cleanup at end-of-xact.
Files opened with BasicOpenFile or PathNameOpenFile are not automatically
cleaned up on error. That puts unnecessary burden on callers that only want
to keep the file open for a short time. There is AllocateFile, but that
returns a buffered FILE * stream, which in many cases is not the nicest API
to work with. So add function called OpenTransientFile, which returns a
unbuffered fd that's cleaned up like the FILE* returned by AllocateFile().
This plugs a few rare fd leaks in error cases:
1. copy_file() - fixed by by using OpenTransientFile instead of BasicOpenFile
2. XLogFileInit() - fixed by adding close() calls to the error cases. Can't
use OpenTransientFile here because the fd is supposed to persist over
transaction boundaries.
3. lo_import/lo_export - fixed by using OpenTransientFile instead of
PathNameOpenFile.
In addition to plugging those leaks, this replaces many BasicOpenFile() calls
with OpenTransientFile() that were not leaking, because the code meticulously
closed the file on error. That wasn't strictly necessary, but IMHO it's good
for robustness.
The same leaks exist in older versions, but given the rarity of the issues,
I'm not backpatching this. Not yet, anyway - it might be good to backpatch
later, after this mechanism has had some more testing in master branch.
2012-11-27 09:25:50 +01:00
|
|
|
CloseTransientFile(srcfd);
|
2005-09-02 20:55:32 +02:00
|
|
|
|
|
|
|
pfree(buffer);
|
2003-05-15 19:59:17 +02:00
|
|
|
}
|