LCOV - code coverage report
Current view: top level - src/backend/backup - basebackup_server.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 77.8 % 81 63
Test Date: 2026-03-03 02:14:47 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit

            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 *
      60            6 : bbsink_server_new(bbsink *next, char *pathname)
      61              : {
      62            6 :     bbsink_server *sink = palloc0_object(bbsink_server);
      63              : 
      64            6 :     *((const bbsink_ops **) &sink->base.bbs_ops) = &bbsink_server_ops;
      65            6 :     sink->pathname = pathname;
      66            6 :     sink->base.bbs_next = next;
      67              : 
      68              :     /* Replication permission is not sufficient in this case. */
      69            6 :     StartTransactionCommand();
      70            6 :     if (!has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES))
      71            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")));
      76            6 :     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              :      */
      86            6 :     if (!is_absolute_path(pathname))
      87            0 :         ereport(ERROR,
      88              :                 (errcode(ERRCODE_INVALID_NAME),
      89              :                  errmsg("relative path not allowed for backup stored on server")));
      90              : 
      91            6 :     switch (pg_check_dir(pathname))
      92              :     {
      93            6 :         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            6 :             if (MakePGDirectory(pathname) < 0)
     100            0 :                 ereport(ERROR,
     101              :                         (errcode_for_file_access(),
     102              :                          errmsg("could not create directory \"%s\": %m", pathname)));
     103            6 :             break;
     104              : 
     105            0 :         case 1:
     106              :             /* Exists, empty. */
     107            0 :             break;
     108              : 
     109            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              : 
     127            6 :     return &sink->base;
     128              : }
     129              : 
     130              : /*
     131              :  * Open the correct output file for this archive.
     132              :  */
     133              : static void
     134           10 : bbsink_server_begin_archive(bbsink *sink, const char *archive_name)
     135              : {
     136           10 :     bbsink_server *mysink = (bbsink_server *) sink;
     137              :     char       *filename;
     138              : 
     139              :     Assert(mysink->file == 0);
     140              :     Assert(mysink->filepos == 0);
     141              : 
     142           10 :     filename = psprintf("%s/%s", mysink->pathname, archive_name);
     143              : 
     144           10 :     mysink->file = PathNameOpenFile(filename,
     145              :                                     O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
     146           10 :     if (mysink->file <= 0)
     147            0 :         ereport(ERROR,
     148              :                 (errcode_for_file_access(),
     149              :                  errmsg("could not create file \"%s\": %m", filename)));
     150              : 
     151           10 :     pfree(filename);
     152              : 
     153           10 :     bbsink_forward_begin_archive(sink, archive_name);
     154           10 : }
     155              : 
     156              : /*
     157              :  * Write the data to the output file.
     158              :  */
     159              : static void
     160         7442 : bbsink_server_archive_contents(bbsink *sink, size_t len)
     161              : {
     162         7442 :     bbsink_server *mysink = (bbsink_server *) sink;
     163              :     int         nbytes;
     164              : 
     165         7442 :     nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
     166              :                        mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
     167              : 
     168         7442 :     if (nbytes != len)
     169              :     {
     170            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 %zu bytes at offset %u",
     180              :                         FilePathName(mysink->file),
     181              :                         nbytes, len, (unsigned) mysink->filepos),
     182              :                  errhint("Check free disk space.")));
     183              :     }
     184              : 
     185         7442 :     mysink->filepos += nbytes;
     186              : 
     187         7442 :     bbsink_forward_archive_contents(sink, len);
     188         7442 : }
     189              : 
     190              : /*
     191              :  * fsync and close the current output file.
     192              :  */
     193              : static void
     194           10 : bbsink_server_end_archive(bbsink *sink)
     195              : {
     196           10 :     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           10 :     if (FileSync(mysink->file, WAIT_EVENT_BASEBACKUP_SYNC) < 0)
     205            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. */
     212           10 :     FileClose(mysink->file);
     213           10 :     mysink->file = 0;
     214           10 :     mysink->filepos = 0;
     215              : 
     216           10 :     bbsink_forward_end_archive(sink);
     217           10 : }
     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            6 : bbsink_server_begin_manifest(bbsink *sink)
     229              : {
     230            6 :     bbsink_server *mysink = (bbsink_server *) sink;
     231              :     char       *tmp_filename;
     232              : 
     233              :     Assert(mysink->file == 0);
     234              : 
     235            6 :     tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
     236              : 
     237            6 :     mysink->file = PathNameOpenFile(tmp_filename,
     238              :                                     O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
     239            6 :     if (mysink->file <= 0)
     240            0 :         ereport(ERROR,
     241              :                 (errcode_for_file_access(),
     242              :                  errmsg("could not create file \"%s\": %m", tmp_filename)));
     243              : 
     244            6 :     pfree(tmp_filename);
     245              : 
     246            6 :     bbsink_forward_begin_manifest(sink);
     247            6 : }
     248              : 
     249              : /*
     250              :  * Each chunk of manifest data is sent using a CopyData message.
     251              :  */
     252              : static void
     253           30 : bbsink_server_manifest_contents(bbsink *sink, size_t len)
     254              : {
     255           30 :     bbsink_server *mysink = (bbsink_server *) sink;
     256              :     int         nbytes;
     257              : 
     258           30 :     nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
     259              :                        mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
     260              : 
     261           30 :     if (nbytes != len)
     262              :     {
     263            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 %zu bytes at offset %u",
     273              :                         FilePathName(mysink->file),
     274              :                         nbytes, len, (unsigned) mysink->filepos),
     275              :                  errhint("Check free disk space.")));
     276              :     }
     277              : 
     278           30 :     mysink->filepos += nbytes;
     279              : 
     280           30 :     bbsink_forward_manifest_contents(sink, len);
     281           30 : }
     282              : 
     283              : /*
     284              :  * fsync the backup manifest, close the file, and then rename it into place.
     285              :  */
     286              : static void
     287            6 : bbsink_server_end_manifest(bbsink *sink)
     288              : {
     289            6 :     bbsink_server *mysink = (bbsink_server *) sink;
     290              :     char       *tmp_filename;
     291              :     char       *filename;
     292              : 
     293              :     /* We're done with this file now. */
     294            6 :     FileClose(mysink->file);
     295            6 :     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            6 :     tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
     303            6 :     filename = psprintf("%s/backup_manifest", mysink->pathname);
     304            6 :     durable_rename(tmp_filename, filename, ERROR);
     305            6 :     pfree(filename);
     306            6 :     pfree(tmp_filename);
     307              : 
     308            6 :     bbsink_forward_end_manifest(sink);
     309            6 : }
        

Generated by: LCOV version 2.0-1