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: 2024-04-26 22:11:34 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_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           8 : bbsink_server_new(bbsink *next, char *pathname)
      61             : {
      62           8 :     bbsink_server *sink = palloc0(sizeof(bbsink_server));
      63             : 
      64           8 :     *((const bbsink_ops **) &sink->base.bbs_ops) = &bbsink_server_ops;
      65           8 :     sink->pathname = pathname;
      66           8 :     sink->base.bbs_next = next;
      67             : 
      68             :     /* Replication permission is not sufficient in this case. */
      69           8 :     StartTransactionCommand();
      70           8 :     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           8 :     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           8 :     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           8 :     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           2 :         case 1:
     106             :             /* Exists, empty. */
     107           2 :             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           8 :     return &sink->base;
     128             : }
     129             : 
     130             : /*
     131             :  * Open the correct output file for this archive.
     132             :  */
     133             : static void
     134           8 : bbsink_server_begin_archive(bbsink *sink, const char *archive_name)
     135             : {
     136           8 :     bbsink_server *mysink = (bbsink_server *) sink;
     137             :     char       *filename;
     138             : 
     139             :     Assert(mysink->file == 0);
     140             :     Assert(mysink->filepos == 0);
     141             : 
     142           8 :     filename = psprintf("%s/%s", mysink->pathname, archive_name);
     143             : 
     144           8 :     mysink->file = PathNameOpenFile(filename,
     145             :                                     O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
     146           8 :     if (mysink->file <= 0)
     147           0 :         ereport(ERROR,
     148             :                 (errcode_for_file_access(),
     149             :                  errmsg("could not create file \"%s\": %m", filename)));
     150             : 
     151           8 :     pfree(filename);
     152             : 
     153           8 :     bbsink_forward_begin_archive(sink, archive_name);
     154           8 : }
     155             : 
     156             : /*
     157             :  * Write the data to the output file.
     158             :  */
     159             : static void
     160       14246 : bbsink_server_archive_contents(bbsink *sink, size_t len)
     161             : {
     162       14246 :     bbsink_server *mysink = (bbsink_server *) sink;
     163             :     int         nbytes;
     164             : 
     165       14246 :     nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
     166             :                        mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
     167             : 
     168       14246 :     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 %d bytes at offset %u",
     180             :                         FilePathName(mysink->file),
     181             :                         nbytes, (int) len, (unsigned) mysink->filepos),
     182             :                  errhint("Check free disk space.")));
     183             :     }
     184             : 
     185       14246 :     mysink->filepos += nbytes;
     186             : 
     187       14246 :     bbsink_forward_archive_contents(sink, len);
     188       14246 : }
     189             : 
     190             : /*
     191             :  * fsync and close the current output file.
     192             :  */
     193             : static void
     194           8 : bbsink_server_end_archive(bbsink *sink)
     195             : {
     196           8 :     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           8 :     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           8 :     FileClose(mysink->file);
     213           8 :     mysink->file = 0;
     214           8 :     mysink->filepos = 0;
     215             : 
     216           8 :     bbsink_forward_end_archive(sink);
     217           8 : }
     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           8 : bbsink_server_begin_manifest(bbsink *sink)
     229             : {
     230           8 :     bbsink_server *mysink = (bbsink_server *) sink;
     231             :     char       *tmp_filename;
     232             : 
     233             :     Assert(mysink->file == 0);
     234             : 
     235           8 :     tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
     236             : 
     237           8 :     mysink->file = PathNameOpenFile(tmp_filename,
     238             :                                     O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
     239           8 :     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           8 :     pfree(tmp_filename);
     245             : 
     246           8 :     bbsink_forward_begin_manifest(sink);
     247           8 : }
     248             : 
     249             : /*
     250             :  * Each chunk of manifest data is sent using a CopyData message.
     251             :  */
     252             : static void
     253          40 : bbsink_server_manifest_contents(bbsink *sink, size_t len)
     254             : {
     255          40 :     bbsink_server *mysink = (bbsink_server *) sink;
     256             :     int         nbytes;
     257             : 
     258          40 :     nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
     259             :                        mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
     260             : 
     261          40 :     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 %d bytes at offset %u",
     273             :                         FilePathName(mysink->file),
     274             :                         nbytes, (int) len, (unsigned) mysink->filepos),
     275             :                  errhint("Check free disk space.")));
     276             :     }
     277             : 
     278          40 :     mysink->filepos += nbytes;
     279             : 
     280          40 :     bbsink_forward_manifest_contents(sink, len);
     281          40 : }
     282             : 
     283             : /*
     284             :  * fsync the backup manifest, close the file, and then rename it into place.
     285             :  */
     286             : static void
     287           8 : bbsink_server_end_manifest(bbsink *sink)
     288             : {
     289           8 :     bbsink_server *mysink = (bbsink_server *) sink;
     290             :     char       *tmp_filename;
     291             :     char       *filename;
     292             : 
     293             :     /* We're done with this file now. */
     294           8 :     FileClose(mysink->file);
     295           8 :     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           8 :     tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
     303           8 :     filename = psprintf("%s/backup_manifest", mysink->pathname);
     304           8 :     durable_rename(tmp_filename, filename, ERROR);
     305           8 :     pfree(filename);
     306           8 :     pfree(tmp_filename);
     307             : 
     308           8 :     bbsink_forward_end_manifest(sink);
     309           8 : }

Generated by: LCOV version 1.14