]> granicus.if.org Git - postgresql/commitdiff
Address set of issues with errno handling
authorMichael Paquier <michael@paquier.xyz>
Mon, 25 Jun 2018 02:20:19 +0000 (11:20 +0900)
committerMichael Paquier <michael@paquier.xyz>
Mon, 25 Jun 2018 02:20:19 +0000 (11:20 +0900)
System calls mixed up in error code paths are causing two issues which
several code paths have not correctly handled:
1) For write() calls, sometimes the system may return less bytes than
what has been written without errno being set.  Some paths were careful
enough to consider that case, and assumed that errno should be set to
ENOSPC, other calls missed that.
2) errno generated by a system call is overwritten by other system calls
which may succeed once an error code path is taken, causing what is
reported to the user to be incorrect.

This patch uses the brute-force approach of correcting all those code
paths.  Some refactoring could happen in the future, but this is let as
future work, which is not targeted for back-branches anyway.

Author: Michael Paquier
Reviewed-by: Ashutosh Sharma
Discussion: https://postgr.es/m/20180622061535.GD5215@paquier.xyz

src/backend/access/heap/rewriteheap.c
src/backend/access/transam/twophase.c
src/backend/access/transam/xlog.c
src/backend/access/transam/xlogutils.c
src/backend/replication/basebackup.c
src/backend/replication/logical/origin.c
src/backend/replication/logical/reorderbuffer.c
src/backend/replication/logical/snapbuild.c
src/backend/replication/slot.c
src/bin/pg_basebackup/walmethods.c

index 680c529580f08db5de0a040d738d9c18206d0812..601efa089d0e187fda896d9113545f45bb28dd2c 100644 (file)
@@ -1169,9 +1169,14 @@ heap_xlog_logical_rewrite(XLogReaderState *r)
        /* write out tail end of mapping file (again) */
        pgstat_report_wait_start(WAIT_EVENT_LOGICAL_REWRITE_MAPPING_WRITE);
        if (write(fd, data, len) != len)
+       {
+               /* if write didn't set errno, assume problem is no disk space */
+               if (errno == 0)
+                       errno = ENOSPC;
                ereport(ERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not write to file \"%s\": %m", path)));
+       }
        pgstat_report_wait_end();
 
        /*
index 6d82566adb5ebea52fb772ccd0aeba928d9db456..bda746bf63dfdd93d9027c7d0f0f885fc15e630b 100644 (file)
@@ -1214,12 +1214,17 @@ ReadTwoPhaseFile(TransactionId xid, bool give_warnings)
         */
        if (fstat(fd, &stat))
        {
+               int                     save_errno = errno;
+
                CloseTransientFile(fd);
                if (give_warnings)
+               {
+                       errno = save_errno;
                        ereport(WARNING,
                                        (errcode_for_file_access(),
                                         errmsg("could not stat two-phase state file \"%s\": %m",
                                                        path)));
+               }
                return NULL;
        }
 
@@ -1247,13 +1252,18 @@ ReadTwoPhaseFile(TransactionId xid, bool give_warnings)
        pgstat_report_wait_start(WAIT_EVENT_TWOPHASE_FILE_READ);
        if (read(fd, buf, stat.st_size) != stat.st_size)
        {
+               int                     save_errno = errno;
+
                pgstat_report_wait_end();
                CloseTransientFile(fd);
                if (give_warnings)
+               {
+                       errno = save_errno;
                        ereport(WARNING,
                                        (errcode_for_file_access(),
                                         errmsg("could not read two-phase state file \"%s\": %m",
                                                        path)));
+               }
                pfree(buf);
                return NULL;
        }
@@ -1597,16 +1607,26 @@ RecreateTwoPhaseFile(TransactionId xid, void *content, int len)
        pgstat_report_wait_start(WAIT_EVENT_TWOPHASE_FILE_WRITE);
        if (write(fd, content, len) != len)
        {
+               int                     save_errno = errno;
+
                pgstat_report_wait_end();
                CloseTransientFile(fd);
+
+               /* if write didn't set errno, assume problem is no disk space */
+               errno = save_errno ? save_errno : ENOSPC;
                ereport(ERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not write two-phase state file: %m")));
        }
        if (write(fd, &statefile_crc, sizeof(pg_crc32c)) != sizeof(pg_crc32c))
        {
+               int                     save_errno = errno;
+
                pgstat_report_wait_end();
                CloseTransientFile(fd);
+
+               /* if write didn't set errno, assume problem is no disk space */
+               errno = save_errno ? save_errno : ENOSPC;
                ereport(ERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not write two-phase state file: %m")));
@@ -1620,7 +1640,10 @@ RecreateTwoPhaseFile(TransactionId xid, void *content, int len)
        pgstat_report_wait_start(WAIT_EVENT_TWOPHASE_FILE_SYNC);
        if (pg_fsync(fd) != 0)
        {
+               int                     save_errno = errno;
+
                CloseTransientFile(fd);
+               errno = save_errno;
                ereport(ERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not fsync two-phase state file: %m")));
index 0cb1e9981a8b674e38523119dcb43b9ba701686e..d3bfe41485d54004d19bc245a0961cd5cfc3d5b5 100644 (file)
@@ -3243,7 +3243,10 @@ XLogFileInit(XLogSegNo logsegno, bool *use_existent, bool use_lock)
        pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_SYNC);
        if (pg_fsync(fd) != 0)
        {
+               int                     save_errno = errno;
+
                close(fd);
+               errno = save_errno;
                ereport(ERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not fsync file \"%s\": %m", tmppath)));
@@ -11590,8 +11593,10 @@ retry:
        if (lseek(readFile, (off_t) readOff, SEEK_SET) < 0)
        {
                char            fname[MAXFNAMELEN];
+               int                     save_errno = errno;
 
                XLogFileName(fname, curFileTLI, readSegNo);
+               errno = save_errno;
                ereport(emode_for_corrupt_record(emode, targetPagePtr + reqLen),
                                (errcode_for_file_access(),
                                 errmsg("could not seek in log segment %s to offset %u: %m",
@@ -11603,9 +11608,11 @@ retry:
        if (read(readFile, readBuf, XLOG_BLCKSZ) != XLOG_BLCKSZ)
        {
                char            fname[MAXFNAMELEN];
+               int                     save_errno = errno;
 
                pgstat_report_wait_end();
                XLogFileName(fname, curFileTLI, readSegNo);
+               errno = save_errno;
                ereport(emode_for_corrupt_record(emode, targetPagePtr + reqLen),
                                (errcode_for_file_access(),
                                 errmsg("could not read from log segment %s, offset %u: %m",
index bbae733d658be6ebed17ff8e0e844a069ccf35ea..40f1198d715326d0818b9da2a2a361b9bb25848c 100644 (file)
@@ -716,9 +716,11 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
                        if (lseek(sendFile, (off_t) startoff, SEEK_SET) < 0)
                        {
                                char            path[MAXPGPATH];
+                               int                     save_errno = errno;
 
                                XLogFilePath(path, tli, sendSegNo);
 
+                               errno = save_errno;
                                ereport(ERROR,
                                                (errcode_for_file_access(),
                                                 errmsg("could not seek in log segment %s to offset %u: %m",
@@ -739,9 +741,11 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
                if (readbytes <= 0)
                {
                        char            path[MAXPGPATH];
+                       int                     save_errno = errno;
 
                        XLogFilePath(path, tli, sendSegNo);
 
+                       errno = save_errno;
                        ereport(ERROR,
                                        (errcode_for_file_access(),
                                         errmsg("could not read from log segment %s, offset %u, length %lu: %m",
index 4a98eb9bdc0337029b5d47c8844eca69ab800e07..ba4937bbd76931962143534078e835d9b82de34b 100644 (file)
@@ -463,6 +463,8 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
                        fp = AllocateFile(pathbuf, "rb");
                        if (fp == NULL)
                        {
+                               int                     save_errno = errno;
+
                                /*
                                 * Most likely reason for this is that the file was already
                                 * removed by a checkpoint, so check for that to get a better
@@ -470,6 +472,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
                                 */
                                CheckXLogRemoved(segno, tli);
 
+                               errno = save_errno;
                                ereport(ERROR,
                                                (errcode_for_file_access(),
                                                 errmsg("could not open file \"%s\": %m", pathbuf)));
index 88fa35a9625d8bbf7f270c97ef36650713591b38..6a1d8a63d92f4700a915b7f726cbf892926c6abd 100644 (file)
@@ -579,7 +579,12 @@ CheckPointReplicationOrigin(void)
        /* write magic */
        if ((write(tmpfd, &magic, sizeof(magic))) != sizeof(magic))
        {
+               int                     save_errno = errno;
+
                CloseTransientFile(tmpfd);
+
+               /* if write didn't set errno, assume problem is no disk space */
+               errno = save_errno ? save_errno : ENOSPC;
                ereport(PANIC,
                                (errcode_for_file_access(),
                                 errmsg("could not write to file \"%s\": %m",
@@ -618,7 +623,12 @@ CheckPointReplicationOrigin(void)
                if ((write(tmpfd, &disk_state, sizeof(disk_state))) !=
                        sizeof(disk_state))
                {
+                       int                     save_errno = errno;
+
                        CloseTransientFile(tmpfd);
+
+                       /* if write didn't set errno, assume problem is no disk space */
+                       errno = save_errno ? save_errno : ENOSPC;
                        ereport(PANIC,
                                        (errcode_for_file_access(),
                                         errmsg("could not write to file \"%s\": %m",
@@ -634,7 +644,12 @@ CheckPointReplicationOrigin(void)
        FIN_CRC32C(crc);
        if ((write(tmpfd, &crc, sizeof(crc))) != sizeof(crc))
        {
+               int                     save_errno = errno;
+
                CloseTransientFile(tmpfd);
+
+               /* if write didn't set errno, assume problem is no disk space */
+               errno = save_errno ? save_errno : ENOSPC;
                ereport(PANIC,
                                (errcode_for_file_access(),
                                 errmsg("could not write to file \"%s\": %m",
index 9e5720756feb616edf31190e216a02f2bbe5c9b0..4c6035634666a0970634fb19df81bb7b8cd7f0f8 100644 (file)
@@ -2309,7 +2309,9 @@ ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
                int                     save_errno = errno;
 
                CloseTransientFile(fd);
-               errno = save_errno;
+
+               /* if write didn't set errno, assume problem is no disk space */
+               errno = save_errno ? save_errno : ENOSPC;
                ereport(ERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not write to data file for XID %u: %m",
index fba57a0470caed0156e62d22d08fae0e36808704..506e7790785a150bf3f177722ac84d2c7a0f5aee 100644 (file)
@@ -1606,7 +1606,12 @@ SnapBuildSerialize(SnapBuild *builder, XLogRecPtr lsn)
        pgstat_report_wait_start(WAIT_EVENT_SNAPBUILD_WRITE);
        if ((write(fd, ondisk, needed_length)) != needed_length)
        {
+               int                     save_errno = errno;
+
                CloseTransientFile(fd);
+
+               /* if write didn't set errno, assume problem is no disk space */
+               errno = save_errno ? save_errno : ENOSPC;
                ereport(ERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not write to file \"%s\": %m", tmppath)));
@@ -1624,7 +1629,10 @@ SnapBuildSerialize(SnapBuild *builder, XLogRecPtr lsn)
        pgstat_report_wait_start(WAIT_EVENT_SNAPBUILD_SYNC);
        if (pg_fsync(fd) != 0)
        {
+               int                     save_errno = errno;
+
                CloseTransientFile(fd);
+               errno = save_errno;
                ereport(ERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not fsync file \"%s\": %m", tmppath)));
@@ -1709,7 +1717,10 @@ SnapBuildRestore(SnapBuild *builder, XLogRecPtr lsn)
        pgstat_report_wait_end();
        if (readBytes != SnapBuildOnDiskConstantSize)
        {
+               int                     save_errno = errno;
+
                CloseTransientFile(fd);
+               errno = save_errno;
                ereport(ERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not read file \"%s\", read %d of %d: %m",
@@ -1737,7 +1748,10 @@ SnapBuildRestore(SnapBuild *builder, XLogRecPtr lsn)
        pgstat_report_wait_end();
        if (readBytes != sizeof(SnapBuild))
        {
+               int                     save_errno = errno;
+
                CloseTransientFile(fd);
+               errno = save_errno;
                ereport(ERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not read file \"%s\", read %d of %d: %m",
@@ -1754,7 +1768,10 @@ SnapBuildRestore(SnapBuild *builder, XLogRecPtr lsn)
        pgstat_report_wait_end();
        if (readBytes != sz)
        {
+               int                     save_errno = errno;
+
                CloseTransientFile(fd);
+               errno = save_errno;
                ereport(ERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not read file \"%s\", read %d of %d: %m",
@@ -1770,7 +1787,10 @@ SnapBuildRestore(SnapBuild *builder, XLogRecPtr lsn)
        pgstat_report_wait_end();
        if (readBytes != sz)
        {
+               int                     save_errno = errno;
+
                CloseTransientFile(fd);
+               errno = save_errno;
                ereport(ERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not read file \"%s\", read %d of %d: %m",
index a8a16f55e98747b61a480f1bfd307cb0f2f34b36..cf5baf686ee8dc205b22cd794e9f968b522f1f96 100644 (file)
@@ -1268,7 +1268,9 @@ SaveSlotToPath(ReplicationSlot *slot, const char *dir, int elevel)
 
                pgstat_report_wait_end();
                CloseTransientFile(fd);
-               errno = save_errno;
+
+               /* if write didn't set errno, assume problem is no disk space */
+               errno = save_errno ? save_errno : ENOSPC;
                ereport(elevel,
                                (errcode_for_file_access(),
                                 errmsg("could not write to file \"%s\": %m",
@@ -1372,7 +1374,10 @@ RestoreSlotFromDisk(const char *name)
        pgstat_report_wait_start(WAIT_EVENT_REPLICATION_SLOT_RESTORE_SYNC);
        if (pg_fsync(fd) != 0)
        {
+               int                     save_errno = errno;
+
                CloseTransientFile(fd);
+               errno = save_errno;
                ereport(PANIC,
                                (errcode_for_file_access(),
                                 errmsg("could not fsync file \"%s\": %m",
index 02d368b2427c7696286d6c68c712fa7079c5c817..a48aacf7661e76fd5446e3c1303d00fc7fbe3f41 100644 (file)
@@ -127,7 +127,11 @@ dir_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_
 
                                pg_free(zerobuf);
                                close(fd);
-                               errno = save_errno;
+
+                               /*
+                                * If write didn't set errno, assume problem is no disk space.
+                                */
+                               errno = save_errno ? save_errno : ENOSPC;
                                return NULL;
                        }
                }
@@ -441,7 +445,14 @@ tar_write_compressed_data(void *buf, size_t count, bool flush)
                        size_t          len = ZLIB_OUT_SIZE - tar_data->zp->avail_out;
 
                        if (write(tar_data->fd, tar_data->zlibOut, len) != len)
+                       {
+                               /*
+                                * If write didn't set errno, assume problem is no disk space.
+                                */
+                               if (errno == 0)
+                                       errno = ENOSPC;
                                return false;
+                       }
 
                        tar_data->zp->next_out = tar_data->zlibOut;
                        tar_data->zp->avail_out = ZLIB_OUT_SIZE;
@@ -621,7 +632,8 @@ tar_open_for_write(const char *pathname, const char *temp_suffix, size_t pad_to_
                        save_errno = errno;
                        pg_free(tar_data->currentfile);
                        tar_data->currentfile = NULL;
-                       errno = save_errno;
+                       /* if write didn't set errno, assume problem is no disk space */
+                       errno = save_errno ? save_errno : ENOSPC;
                        return NULL;
                }
        }
@@ -816,7 +828,12 @@ tar_close(Walfile f, WalCloseMethod method)
        if (!tar_data->compression)
        {
                if (write(tar_data->fd, tf->header, 512) != 512)
+               {
+                       /* if write didn't set errno, assume problem is no disk space */
+                       if (errno == 0)
+                               errno = ENOSPC;
                        return -1;
+               }
        }
 #ifdef HAVE_LIBZ
        else
@@ -882,7 +899,12 @@ tar_finish(void)
        if (!tar_data->compression)
        {
                if (write(tar_data->fd, zerobuf, sizeof(zerobuf)) != sizeof(zerobuf))
+               {
+                       /* if write didn't set errno, assume problem is no disk space */
+                       if (errno == 0)
+                               errno = ENOSPC;
                        return false;
+               }
        }
 #ifdef HAVE_LIBZ
        else
@@ -909,7 +931,15 @@ tar_finish(void)
                                size_t          len = ZLIB_OUT_SIZE - tar_data->zp->avail_out;
 
                                if (write(tar_data->fd, tar_data->zlibOut, len) != len)
+                               {
+                                       /*
+                                        * If write didn't set errno, assume problem is no disk
+                                        * space.
+                                        */
+                                       if (errno == 0)
+                                               errno = ENOSPC;
                                        return false;
+                               }
                        }
                        if (r == Z_STREAM_END)
                                break;