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 15:15:32 Functions: 100.0 % 7 7 6 1 6
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           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 *
      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. */
      71               7 :     StartTransactionCommand();
      72               7 :     if (!has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES))
      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")));
      78 GIC           7 :     CommitTransactionCommand();
      79                 : 
      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                 :      */
      88 GIC           7 :     if (!is_absolute_path(pathname))
      89 UIC           0 :         ereport(ERROR,
      90 ECB             :                 (errcode(ERRCODE_INVALID_NAME),
      91 EUB             :                  errmsg("relative path not allowed for backup stored on server")));
      92                 : 
      93 GIC           7 :     switch (pg_check_dir(pathname))
      94                 :     {
      95 CBC           3 :         case 0:
      96                 : 
      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                 :              */
     101 GIC           3 :             if (MakePGDirectory(pathname) < 0)
     102 UIC           0 :                 ereport(ERROR,
     103 ECB             :                         (errcode_for_file_access(),
     104 EUB             :                          errmsg("could not create directory \"%s\": %m", pathname)));
     105 GIC           3 :             break;
     106                 : 
     107 CBC           4 :         case 1:
     108                 :             /* Exists, empty. */
     109               4 :             break;
     110                 : 
     111 LBC           0 :         case 2:
     112                 :         case 3:
     113 EUB             :         case 4:
     114                 :             /* Exists, not empty. */
     115 UIC           0 :             ereport(ERROR,
     116                 :                     (errcode(ERRCODE_DUPLICATE_FILE),
     117 EUB             :                      errmsg("directory \"%s\" exists but is not empty",
     118                 :                             pathname)));
     119                 :             break;
     120                 : 
     121 UIC           0 :         default:
     122                 :             /* Access problem. */
     123 UBC           0 :             ereport(ERROR,
     124                 :                     (errcode_for_file_access(),
     125 EUB             :                      errmsg("could not access directory \"%s\": %m",
     126                 :                             pathname)));
     127                 :     }
     128                 : 
     129 GIC           7 :     return &sink->base;
     130                 : }
     131 ECB             : 
     132                 : /*
     133                 :  * Open the correct output file for this archive.
     134                 :  */
     135                 : static void
     136 GIC           7 : bbsink_server_begin_archive(bbsink *sink, const char *archive_name)
     137                 : {
     138 CBC           7 :     bbsink_server *mysink = (bbsink_server *) sink;
     139                 :     char       *filename;
     140 ECB             : 
     141 GIC           7 :     Assert(mysink->file == 0);
     142               7 :     Assert(mysink->filepos == 0);
     143 ECB             : 
     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)
     149 UIC           0 :         ereport(ERROR,
     150 ECB             :                 (errcode_for_file_access(),
     151 EUB             :                  errmsg("could not create file \"%s\": %m", filename)));
     152                 : 
     153 GIC           7 :     pfree(filename);
     154                 : 
     155 CBC           7 :     bbsink_forward_begin_archive(sink, archive_name);
     156 GIC           7 : }
     157 ECB             : 
     158                 : /*
     159                 :  * Write the data to the output file.
     160                 :  */
     161                 : static void
     162 GIC        7485 : bbsink_server_archive_contents(bbsink *sink, size_t len)
     163                 : {
     164 CBC        7485 :     bbsink_server *mysink = (bbsink_server *) sink;
     165                 :     int         nbytes;
     166 ECB             : 
     167 GIC        7485 :     nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
     168                 :                        mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
     169 ECB             : 
     170 GIC        7485 :     if (nbytes != len)
     171                 :     {
     172 LBC           0 :         if (nbytes < 0)
     173 UIC           0 :             ereport(ERROR,
     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 */
     179 UIC           0 :         ereport(ERROR,
     180                 :                 (errcode(ERRCODE_DISK_FULL),
     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                 : 
     187 GIC        7485 :     mysink->filepos += nbytes;
     188                 : 
     189 CBC        7485 :     bbsink_forward_archive_contents(sink, len);
     190 GIC        7485 : }
     191 ECB             : 
     192                 : /*
     193                 :  * fsync and close the current output file.
     194                 :  */
     195                 : static void
     196 GIC           7 : bbsink_server_end_archive(bbsink *sink)
     197                 : {
     198 CBC           7 :     bbsink_server *mysink = (bbsink_server *) sink;
     199                 : 
     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                 :      */
     206 GIC           7 :     if (FileSync(mysink->file, WAIT_EVENT_BASEBACKUP_SYNC) < 0)
     207 UIC           0 :         ereport(ERROR,
     208 ECB             :                 (errcode_for_file_access(),
     209 EUB             :                  errmsg("could not fsync file \"%s\": %m",
     210                 :                         FilePathName(mysink->file))));
     211                 : 
     212                 : 
     213                 :     /* We're done with this file now. */
     214 GIC           7 :     FileClose(mysink->file);
     215               7 :     mysink->file = 0;
     216 CBC           7 :     mysink->filepos = 0;
     217 ECB             : 
     218 CBC           7 :     bbsink_forward_end_archive(sink);
     219 GIC           7 : }
     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
     230 GIC           7 : bbsink_server_begin_manifest(bbsink *sink)
     231                 : {
     232 CBC           7 :     bbsink_server *mysink = (bbsink_server *) sink;
     233                 :     char       *tmp_filename;
     234 ECB             : 
     235 GIC           7 :     Assert(mysink->file == 0);
     236                 : 
     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)
     242 UIC           0 :         ereport(ERROR,
     243 ECB             :                 (errcode_for_file_access(),
     244 EUB             :                  errmsg("could not create file \"%s\": %m", tmp_filename)));
     245                 : 
     246 GIC           7 :     pfree(tmp_filename);
     247                 : 
     248 CBC           7 :     bbsink_forward_begin_manifest(sink);
     249 GIC           7 : }
     250 ECB             : 
     251                 : /*
     252                 :  * Each chunk of manifest data is sent using a CopyData message.
     253                 :  */
     254                 : static void
     255 GIC          35 : bbsink_server_manifest_contents(bbsink *sink, size_t len)
     256                 : {
     257 CBC          35 :     bbsink_server *mysink = (bbsink_server *) sink;
     258                 :     int         nbytes;
     259 ECB             : 
     260 GIC          35 :     nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
     261                 :                        mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
     262 ECB             : 
     263 GIC          35 :     if (nbytes != len)
     264                 :     {
     265 LBC           0 :         if (nbytes < 0)
     266 UIC           0 :             ereport(ERROR,
     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 */
     272 UIC           0 :         ereport(ERROR,
     273                 :                 (errcode(ERRCODE_DISK_FULL),
     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                 : 
     280 GIC          35 :     mysink->filepos += nbytes;
     281                 : 
     282 CBC          35 :     bbsink_forward_manifest_contents(sink, len);
     283 GIC          35 : }
     284 ECB             : 
     285                 : /*
     286                 :  * fsync the backup manifest, close the file, and then rename it into place.
     287                 :  */
     288                 : static void
     289 GIC           7 : bbsink_server_end_manifest(bbsink *sink)
     290                 : {
     291 CBC           7 :     bbsink_server *mysink = (bbsink_server *) sink;
     292                 :     char       *tmp_filename;
     293 ECB             :     char       *filename;
     294                 : 
     295                 :     /* We're done with this file now. */
     296 GIC           7 :     FileClose(mysink->file);
     297               7 :     mysink->file = 0;
     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                 :      */
     304 GIC           7 :     tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
     305               7 :     filename = psprintf("%s/backup_manifest", mysink->pathname);
     306 CBC           7 :     durable_rename(tmp_filename, filename, ERROR);
     307               7 :     pfree(filename);
     308               7 :     pfree(tmp_filename);
     309 ECB             : 
     310 CBC           7 :     bbsink_forward_end_manifest(sink);
     311 GIC           7 : }
        

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