LCOV - differential code coverage report
Current view: top level - src/backend/backup - basebackup_server.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 81.0 % 84 68 16 68
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 7 7 7
Baseline: 16@8cea358b128 Branches: 24.1 % 54 13 41 13
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (240..) days: 81.0 % 84 68 16 68
Function coverage date bins:
(240..) days: 100.0 % 7 7 7
Branch coverage date bins:
(240..) days: 24.1 % 54 13 41 13

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

Generated by: LCOV version 2.1-beta2-3-g6141622