LCOV - differential code coverage report
Current view: top level - src/backend/backup - basebackup_server.c (source / functions) Coverage Total Hit LBC UIC UBC GIC GNC CBC EUB ECB
Current: Differential Code Coverage HEAD vs 15 Lines: 81.0 % 84 68 3 11 2 36 32 14 33
Current Date: 2023-04-08 17:13:01 Functions: 100.0 % 7 7 6 1 6
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (240..) days: 81.0 % 84 68 3 11 2 36 32 13 31
Legend: Lines: hit not hit Function coverage date bins:
(240..) days: 53.8 % 13 7 6 1 6

 Age         Owner                  TLA  Line data    Source code
                                  1                 : /*-------------------------------------------------------------------------
                                  2                 :  *
                                  3                 :  * basebackup_server.c
                                  4                 :  *    store basebackup archives on the server
                                  5                 :  *
                                  6                 :  * IDENTIFICATION
                                  7                 :  *    src/backend/backup/basebackup_server.c
                                  8                 :  *
                                  9                 :  *-------------------------------------------------------------------------
                                 10                 :  */
                                 11                 : #include "postgres.h"
                                 12                 : 
                                 13                 : #include "access/xact.h"
                                 14                 : #include "backup/basebackup.h"
                                 15                 : #include "backup/basebackup_sink.h"
                                 16                 : #include "catalog/pg_authid.h"
                                 17                 : #include "miscadmin.h"
                                 18                 : #include "storage/fd.h"
                                 19                 : #include "utils/acl.h"
                                 20                 : #include "utils/timestamp.h"
                                 21                 : #include "utils/wait_event.h"
                                 22                 : 
                                 23                 : typedef struct bbsink_server
                                 24                 : {
                                 25                 :     /* Common information for all types of sink. */
                                 26                 :     bbsink      base;
                                 27                 : 
                                 28                 :     /* Directory in which backup is to be stored. */
                                 29                 :     char       *pathname;
                                 30                 : 
                                 31                 :     /* Currently open file (or 0 if nothing open). */
                                 32                 :     File        file;
                                 33                 : 
                                 34                 :     /* Current file position. */
                                 35                 :     off_t       filepos;
                                 36                 : } bbsink_server;
                                 37                 : 
                                 38                 : static void bbsink_server_begin_archive(bbsink *sink,
                                 39                 :                                         const char *archive_name);
                                 40                 : static void bbsink_server_archive_contents(bbsink *sink, size_t len);
                                 41                 : static void bbsink_server_end_archive(bbsink *sink);
                                 42                 : static void bbsink_server_begin_manifest(bbsink *sink);
                                 43                 : static void bbsink_server_manifest_contents(bbsink *sink, size_t len);
                                 44                 : static void bbsink_server_end_manifest(bbsink *sink);
                                 45                 : 
                                 46                 : static const bbsink_ops bbsink_server_ops = {
                                 47                 :     .begin_backup = bbsink_forward_begin_backup,
                                 48                 :     .begin_archive = bbsink_server_begin_archive,
                                 49                 :     .archive_contents = bbsink_server_archive_contents,
                                 50                 :     .end_archive = bbsink_server_end_archive,
                                 51                 :     .begin_manifest = bbsink_server_begin_manifest,
                                 52                 :     .manifest_contents = bbsink_server_manifest_contents,
                                 53                 :     .end_manifest = bbsink_server_end_manifest,
                                 54                 :     .end_backup = bbsink_forward_end_backup,
                                 55                 :     .cleanup = bbsink_forward_cleanup
                                 56                 : };
                                 57                 : 
                                 58                 : /*
                                 59                 :  * Create a new 'server' bbsink.
                                 60                 :  */
                                 61                 : bbsink *
  509 rhaas                      62 CBC           7 : bbsink_server_new(bbsink *next, char *pathname)
                                 63                 : {
                                 64               7 :     bbsink_server *sink = palloc0(sizeof(bbsink_server));
                                 65                 : 
                                 66               7 :     *((const bbsink_ops **) &sink->base.bbs_ops) = &bbsink_server_ops;
                                 67               7 :     sink->pathname = pathname;
                                 68               7 :     sink->base.bbs_next = next;
                                 69                 : 
                                 70                 :     /* Replication permission is not sufficient in this case. */
  431                            71               7 :     StartTransactionCommand();
  372 mail                       72               7 :     if (!has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES))
  509 rhaas                      73 UBC           0 :         ereport(ERROR,
                                 74                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 75                 :                  errmsg("permission denied to create backup stored on server"),
                                 76                 :                  errdetail("Only roles with privileges of the \"%s\" role may create a backup stored on the server.",
                                 77                 :                            "pg_write_server_files")));
  431 rhaas                      78 GIC           7 :     CommitTransactionCommand();
                                 79                 : 
  509 rhaas                      80 ECB             :     /*
                                 81                 :      * It's not a good idea to store your backups in the same directory that
                                 82                 :      * you're backing up. If we allowed a relative path here, that could
                                 83                 :      * easily happen accidentally, so we don't. The user could still
                                 84                 :      * accomplish the same thing by including the absolute path to $PGDATA in
                                 85                 :      * the pathname, but that's likely an intentional bad decision rather than
                                 86                 :      * an accident.
                                 87                 :      */
  509 rhaas                      88 GIC           7 :     if (!is_absolute_path(pathname))
  509 rhaas                      89 UIC           0 :         ereport(ERROR,
  509 rhaas                      90 ECB             :                 (errcode(ERRCODE_INVALID_NAME),
  197 peter                      91 EUB             :                  errmsg("relative path not allowed for backup stored on server")));
                                 92                 : 
  509 rhaas                      93 GIC           7 :     switch (pg_check_dir(pathname))
                                 94                 :     {
  509 rhaas                      95 CBC           3 :         case 0:
                                 96                 : 
  509 rhaas                      97 ECB             :             /*
                                 98                 :              * Does not exist, so create it using the same permissions we'd
                                 99                 :              * use for a new subdirectory of the data directory itself.
                                100                 :              */
  509 rhaas                     101 GIC           3 :             if (MakePGDirectory(pathname) < 0)
  509 rhaas                     102 UIC           0 :                 ereport(ERROR,
  332 tgl                       103 ECB             :                         (errcode_for_file_access(),
  332 tgl                       104 EUB             :                          errmsg("could not create directory \"%s\": %m", pathname)));
  509 rhaas                     105 GIC           3 :             break;
                                106                 : 
  509 rhaas                     107 CBC           4 :         case 1:
                                108                 :             /* Exists, empty. */
                                109               4 :             break;
                                110                 : 
  509 rhaas                     111 LBC           0 :         case 2:
                                112                 :         case 3:
  509 rhaas                     113 EUB             :         case 4:
                                114                 :             /* Exists, not empty. */
  509 rhaas                     115 UIC           0 :             ereport(ERROR,
                                116                 :                     (errcode(ERRCODE_DUPLICATE_FILE),
  509 rhaas                     117 EUB             :                      errmsg("directory \"%s\" exists but is not empty",
                                118                 :                             pathname)));
                                119                 :             break;
                                120                 : 
  509 rhaas                     121 UIC           0 :         default:
                                122                 :             /* Access problem. */
  509 rhaas                     123 UBC           0 :             ereport(ERROR,
                                124                 :                     (errcode_for_file_access(),
  509 rhaas                     125 EUB             :                      errmsg("could not access directory \"%s\": %m",
                                126                 :                             pathname)));
                                127                 :     }
                                128                 : 
  509 rhaas                     129 GIC           7 :     return &sink->base;
                                130                 : }
  509 rhaas                     131 ECB             : 
                                132                 : /*
                                133                 :  * Open the correct output file for this archive.
                                134                 :  */
                                135                 : static void
  509 rhaas                     136 GIC           7 : bbsink_server_begin_archive(bbsink *sink, const char *archive_name)
                                137                 : {
  509 rhaas                     138 CBC           7 :     bbsink_server *mysink = (bbsink_server *) sink;
                                139                 :     char       *filename;
  509 rhaas                     140 ECB             : 
  509 rhaas                     141 GIC           7 :     Assert(mysink->file == 0);
                                142               7 :     Assert(mysink->filepos == 0);
  509 rhaas                     143 ECB             : 
  509 rhaas                     144 CBC           7 :     filename = psprintf("%s/%s", mysink->pathname, archive_name);
                                145                 : 
                                146               7 :     mysink->file = PathNameOpenFile(filename,
                                147                 :                                     O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
                                148               7 :     if (mysink->file <= 0)
  509 rhaas                     149 UIC           0 :         ereport(ERROR,
  509 rhaas                     150 ECB             :                 (errcode_for_file_access(),
  509 rhaas                     151 EUB             :                  errmsg("could not create file \"%s\": %m", filename)));
                                152                 : 
  509 rhaas                     153 GIC           7 :     pfree(filename);
                                154                 : 
  509 rhaas                     155 CBC           7 :     bbsink_forward_begin_archive(sink, archive_name);
  509 rhaas                     156 GIC           7 : }
  509 rhaas                     157 ECB             : 
                                158                 : /*
                                159                 :  * Write the data to the output file.
                                160                 :  */
                                161                 : static void
  509 rhaas                     162 GIC        7485 : bbsink_server_archive_contents(bbsink *sink, size_t len)
                                163                 : {
  509 rhaas                     164 CBC        7485 :     bbsink_server *mysink = (bbsink_server *) sink;
                                165                 :     int         nbytes;
  509 rhaas                     166 ECB             : 
  509 rhaas                     167 GIC        7485 :     nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
                                168                 :                        mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
  509 rhaas                     169 ECB             : 
  509 rhaas                     170 GIC        7485 :     if (nbytes != len)
                                171                 :     {
  509 rhaas                     172 LBC           0 :         if (nbytes < 0)
  509 rhaas                     173 UIC           0 :             ereport(ERROR,
  509 rhaas                     174 EUB             :                     (errcode_for_file_access(),
                                175                 :                      errmsg("could not write file \"%s\": %m",
                                176                 :                             FilePathName(mysink->file)),
                                177                 :                      errhint("Check free disk space.")));
                                178                 :         /* short write: complain appropriately */
  509 rhaas                     179 UIC           0 :         ereport(ERROR,
                                180                 :                 (errcode(ERRCODE_DISK_FULL),
  509 rhaas                     181 EUB             :                  errmsg("could not write file \"%s\": wrote only %d of %d bytes at offset %u",
                                182                 :                         FilePathName(mysink->file),
                                183                 :                         nbytes, (int) len, (unsigned) mysink->filepos),
                                184                 :                  errhint("Check free disk space.")));
                                185                 :     }
                                186                 : 
  509 rhaas                     187 GIC        7485 :     mysink->filepos += nbytes;
                                188                 : 
  509 rhaas                     189 CBC        7485 :     bbsink_forward_archive_contents(sink, len);
  509 rhaas                     190 GIC        7485 : }
  509 rhaas                     191 ECB             : 
                                192                 : /*
                                193                 :  * fsync and close the current output file.
                                194                 :  */
                                195                 : static void
  509 rhaas                     196 GIC           7 : bbsink_server_end_archive(bbsink *sink)
                                197                 : {
  509 rhaas                     198 CBC           7 :     bbsink_server *mysink = (bbsink_server *) sink;
                                199                 : 
  509 rhaas                     200 ECB             :     /*
                                201                 :      * We intentionally don't use data_sync_elevel here, because the server
                                202                 :      * shouldn't PANIC just because we can't guarantee that the backup has
                                203                 :      * been written down to disk. Running recovery won't fix anything in this
                                204                 :      * case anyway.
                                205                 :      */
  509 rhaas                     206 GIC           7 :     if (FileSync(mysink->file, WAIT_EVENT_BASEBACKUP_SYNC) < 0)
  509 rhaas                     207 UIC           0 :         ereport(ERROR,
  509 rhaas                     208 ECB             :                 (errcode_for_file_access(),
  509 rhaas                     209 EUB             :                  errmsg("could not fsync file \"%s\": %m",
                                210                 :                         FilePathName(mysink->file))));
                                211                 : 
                                212                 : 
                                213                 :     /* We're done with this file now. */
  509 rhaas                     214 GIC           7 :     FileClose(mysink->file);
                                215               7 :     mysink->file = 0;
  509 rhaas                     216 CBC           7 :     mysink->filepos = 0;
  509 rhaas                     217 ECB             : 
  509 rhaas                     218 CBC           7 :     bbsink_forward_end_archive(sink);
  509 rhaas                     219 GIC           7 : }
  509 rhaas                     220 ECB             : 
                                221                 : /*
                                222                 :  * Open the output file to which we will write the manifest.
                                223                 :  *
                                224                 :  * Just like pg_basebackup, we write the manifest first under a temporary
                                225                 :  * name and then rename it into place after fsync. That way, if the manifest
                                226                 :  * is there and under the correct name, the user can be sure that the backup
                                227                 :  * completed.
                                228                 :  */
                                229                 : static void
  509 rhaas                     230 GIC           7 : bbsink_server_begin_manifest(bbsink *sink)
                                231                 : {
  509 rhaas                     232 CBC           7 :     bbsink_server *mysink = (bbsink_server *) sink;
                                233                 :     char       *tmp_filename;
  509 rhaas                     234 ECB             : 
  509 rhaas                     235 GIC           7 :     Assert(mysink->file == 0);
                                236                 : 
  509 rhaas                     237 CBC           7 :     tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
                                238                 : 
                                239               7 :     mysink->file = PathNameOpenFile(tmp_filename,
                                240                 :                                     O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
                                241               7 :     if (mysink->file <= 0)
  509 rhaas                     242 UIC           0 :         ereport(ERROR,
  509 rhaas                     243 ECB             :                 (errcode_for_file_access(),
  509 rhaas                     244 EUB             :                  errmsg("could not create file \"%s\": %m", tmp_filename)));
                                245                 : 
  509 rhaas                     246 GIC           7 :     pfree(tmp_filename);
                                247                 : 
  509 rhaas                     248 CBC           7 :     bbsink_forward_begin_manifest(sink);
  509 rhaas                     249 GIC           7 : }
  509 rhaas                     250 ECB             : 
                                251                 : /*
                                252                 :  * Each chunk of manifest data is sent using a CopyData message.
                                253                 :  */
                                254                 : static void
  509 rhaas                     255 GIC          35 : bbsink_server_manifest_contents(bbsink *sink, size_t len)
                                256                 : {
  509 rhaas                     257 CBC          35 :     bbsink_server *mysink = (bbsink_server *) sink;
                                258                 :     int         nbytes;
  509 rhaas                     259 ECB             : 
  509 rhaas                     260 GIC          35 :     nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
                                261                 :                        mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
  509 rhaas                     262 ECB             : 
  509 rhaas                     263 GIC          35 :     if (nbytes != len)
                                264                 :     {
  509 rhaas                     265 LBC           0 :         if (nbytes < 0)
  509 rhaas                     266 UIC           0 :             ereport(ERROR,
  509 rhaas                     267 EUB             :                     (errcode_for_file_access(),
                                268                 :                      errmsg("could not write file \"%s\": %m",
                                269                 :                             FilePathName(mysink->file)),
                                270                 :                      errhint("Check free disk space.")));
                                271                 :         /* short write: complain appropriately */
  509 rhaas                     272 UIC           0 :         ereport(ERROR,
                                273                 :                 (errcode(ERRCODE_DISK_FULL),
  509 rhaas                     274 EUB             :                  errmsg("could not write file \"%s\": wrote only %d of %d bytes at offset %u",
                                275                 :                         FilePathName(mysink->file),
                                276                 :                         nbytes, (int) len, (unsigned) mysink->filepos),
                                277                 :                  errhint("Check free disk space.")));
                                278                 :     }
                                279                 : 
  509 rhaas                     280 GIC          35 :     mysink->filepos += nbytes;
                                281                 : 
  509 rhaas                     282 CBC          35 :     bbsink_forward_manifest_contents(sink, len);
  509 rhaas                     283 GIC          35 : }
  509 rhaas                     284 ECB             : 
                                285                 : /*
                                286                 :  * fsync the backup manifest, close the file, and then rename it into place.
                                287                 :  */
                                288                 : static void
  509 rhaas                     289 GIC           7 : bbsink_server_end_manifest(bbsink *sink)
                                290                 : {
  509 rhaas                     291 CBC           7 :     bbsink_server *mysink = (bbsink_server *) sink;
                                292                 :     char       *tmp_filename;
  509 rhaas                     293 ECB             :     char       *filename;
                                294                 : 
                                295                 :     /* We're done with this file now. */
  509 rhaas                     296 GIC           7 :     FileClose(mysink->file);
                                297               7 :     mysink->file = 0;
  509 rhaas                     298 ECB             : 
                                299                 :     /*
                                300                 :      * Rename it into place. This also fsyncs the temporary file, so we don't
                                301                 :      * need to do that here. We don't use data_sync_elevel here for the same
                                302                 :      * reasons as in bbsink_server_end_archive.
                                303                 :      */
  509 rhaas                     304 GIC           7 :     tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
                                305               7 :     filename = psprintf("%s/backup_manifest", mysink->pathname);
  509 rhaas                     306 CBC           7 :     durable_rename(tmp_filename, filename, ERROR);
                                307               7 :     pfree(filename);
                                308               7 :     pfree(tmp_filename);
  509 rhaas                     309 ECB             : 
  509 rhaas                     310 CBC           7 :     bbsink_forward_end_manifest(sink);
  509 rhaas                     311 GIC           7 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a