LCOV - code coverage report
Current view: top level - src/backend/backup - basebackup_server.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 65 81 80.2 %
Date: 2023-12-11 14:10:49 Functions: 7 7 100.0 %
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.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           8 : bbsink_server_new(bbsink *next, char *pathname)
      63             : {
      64           8 :     bbsink_server *sink = palloc0(sizeof(bbsink_server));
      65             : 
      66           8 :     *((const bbsink_ops **) &sink->base.bbs_ops) = &bbsink_server_ops;
      67           8 :     sink->pathname = pathname;
      68           8 :     sink->base.bbs_next = next;
      69             : 
      70             :     /* Replication permission is not sufficient in this case. */
      71           8 :     StartTransactionCommand();
      72           8 :     if (!has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES))
      73           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           8 :     CommitTransactionCommand();
      79             : 
      80             :     /*
      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           8 :     if (!is_absolute_path(pathname))
      89           0 :         ereport(ERROR,
      90             :                 (errcode(ERRCODE_INVALID_NAME),
      91             :                  errmsg("relative path not allowed for backup stored on server")));
      92             : 
      93           8 :     switch (pg_check_dir(pathname))
      94             :     {
      95           6 :         case 0:
      96             : 
      97             :             /*
      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           6 :             if (MakePGDirectory(pathname) < 0)
     102           0 :                 ereport(ERROR,
     103             :                         (errcode_for_file_access(),
     104             :                          errmsg("could not create directory \"%s\": %m", pathname)));
     105           6 :             break;
     106             : 
     107           2 :         case 1:
     108             :             /* Exists, empty. */
     109           2 :             break;
     110             : 
     111           0 :         case 2:
     112             :         case 3:
     113             :         case 4:
     114             :             /* Exists, not empty. */
     115           0 :             ereport(ERROR,
     116             :                     (errcode(ERRCODE_DUPLICATE_FILE),
     117             :                      errmsg("directory \"%s\" exists but is not empty",
     118             :                             pathname)));
     119             :             break;
     120             : 
     121           0 :         default:
     122             :             /* Access problem. */
     123           0 :             ereport(ERROR,
     124             :                     (errcode_for_file_access(),
     125             :                      errmsg("could not access directory \"%s\": %m",
     126             :                             pathname)));
     127             :     }
     128             : 
     129           8 :     return &sink->base;
     130             : }
     131             : 
     132             : /*
     133             :  * Open the correct output file for this archive.
     134             :  */
     135             : static void
     136           8 : bbsink_server_begin_archive(bbsink *sink, const char *archive_name)
     137             : {
     138           8 :     bbsink_server *mysink = (bbsink_server *) sink;
     139             :     char       *filename;
     140             : 
     141             :     Assert(mysink->file == 0);
     142             :     Assert(mysink->filepos == 0);
     143             : 
     144           8 :     filename = psprintf("%s/%s", mysink->pathname, archive_name);
     145             : 
     146           8 :     mysink->file = PathNameOpenFile(filename,
     147             :                                     O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
     148           8 :     if (mysink->file <= 0)
     149           0 :         ereport(ERROR,
     150             :                 (errcode_for_file_access(),
     151             :                  errmsg("could not create file \"%s\": %m", filename)));
     152             : 
     153           8 :     pfree(filename);
     154             : 
     155           8 :     bbsink_forward_begin_archive(sink, archive_name);
     156           8 : }
     157             : 
     158             : /*
     159             :  * Write the data to the output file.
     160             :  */
     161             : static void
     162       14282 : bbsink_server_archive_contents(bbsink *sink, size_t len)
     163             : {
     164       14282 :     bbsink_server *mysink = (bbsink_server *) sink;
     165             :     int         nbytes;
     166             : 
     167       14282 :     nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
     168             :                        mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
     169             : 
     170       14282 :     if (nbytes != len)
     171             :     {
     172           0 :         if (nbytes < 0)
     173           0 :             ereport(ERROR,
     174             :                     (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           0 :         ereport(ERROR,
     180             :                 (errcode(ERRCODE_DISK_FULL),
     181             :                  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       14282 :     mysink->filepos += nbytes;
     188             : 
     189       14282 :     bbsink_forward_archive_contents(sink, len);
     190       14282 : }
     191             : 
     192             : /*
     193             :  * fsync and close the current output file.
     194             :  */
     195             : static void
     196           8 : bbsink_server_end_archive(bbsink *sink)
     197             : {
     198           8 :     bbsink_server *mysink = (bbsink_server *) sink;
     199             : 
     200             :     /*
     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           8 :     if (FileSync(mysink->file, WAIT_EVENT_BASEBACKUP_SYNC) < 0)
     207           0 :         ereport(ERROR,
     208             :                 (errcode_for_file_access(),
     209             :                  errmsg("could not fsync file \"%s\": %m",
     210             :                         FilePathName(mysink->file))));
     211             : 
     212             : 
     213             :     /* We're done with this file now. */
     214           8 :     FileClose(mysink->file);
     215           8 :     mysink->file = 0;
     216           8 :     mysink->filepos = 0;
     217             : 
     218           8 :     bbsink_forward_end_archive(sink);
     219           8 : }
     220             : 
     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           8 : bbsink_server_begin_manifest(bbsink *sink)
     231             : {
     232           8 :     bbsink_server *mysink = (bbsink_server *) sink;
     233             :     char       *tmp_filename;
     234             : 
     235             :     Assert(mysink->file == 0);
     236             : 
     237           8 :     tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
     238             : 
     239           8 :     mysink->file = PathNameOpenFile(tmp_filename,
     240             :                                     O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
     241           8 :     if (mysink->file <= 0)
     242           0 :         ereport(ERROR,
     243             :                 (errcode_for_file_access(),
     244             :                  errmsg("could not create file \"%s\": %m", tmp_filename)));
     245             : 
     246           8 :     pfree(tmp_filename);
     247             : 
     248           8 :     bbsink_forward_begin_manifest(sink);
     249           8 : }
     250             : 
     251             : /*
     252             :  * Each chunk of manifest data is sent using a CopyData message.
     253             :  */
     254             : static void
     255          40 : bbsink_server_manifest_contents(bbsink *sink, size_t len)
     256             : {
     257          40 :     bbsink_server *mysink = (bbsink_server *) sink;
     258             :     int         nbytes;
     259             : 
     260          40 :     nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
     261             :                        mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
     262             : 
     263          40 :     if (nbytes != len)
     264             :     {
     265           0 :         if (nbytes < 0)
     266           0 :             ereport(ERROR,
     267             :                     (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           0 :         ereport(ERROR,
     273             :                 (errcode(ERRCODE_DISK_FULL),
     274             :                  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          40 :     mysink->filepos += nbytes;
     281             : 
     282          40 :     bbsink_forward_manifest_contents(sink, len);
     283          40 : }
     284             : 
     285             : /*
     286             :  * fsync the backup manifest, close the file, and then rename it into place.
     287             :  */
     288             : static void
     289           8 : bbsink_server_end_manifest(bbsink *sink)
     290             : {
     291           8 :     bbsink_server *mysink = (bbsink_server *) sink;
     292             :     char       *tmp_filename;
     293             :     char       *filename;
     294             : 
     295             :     /* We're done with this file now. */
     296           8 :     FileClose(mysink->file);
     297           8 :     mysink->file = 0;
     298             : 
     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           8 :     tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
     305           8 :     filename = psprintf("%s/backup_manifest", mysink->pathname);
     306           8 :     durable_rename(tmp_filename, filename, ERROR);
     307           8 :     pfree(filename);
     308           8 :     pfree(tmp_filename);
     309             : 
     310           8 :     bbsink_forward_end_manifest(sink);
     311           8 : }

Generated by: LCOV version 1.14