Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/bin/pg_combinebackup/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 7 kB image not shown  

Quelle  write_manifest.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * Write a new backup manifest.
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/bin/pg_combinebackup/write_manifest.c
 *
 *-------------------------------------------------------------------------
 */


#include "postgres_fe.h"

#include <fcntl.h>
#include <time.h>
#include <unistd.h>

#include "common/checksum_helper.h"
#include "common/file_perm.h"
#include "common/logging.h"
#include "lib/stringinfo.h"
#include "load_manifest.h"
#include "mb/pg_wchar.h"
#include "write_manifest.h"

struct manifest_writer
{
 char  pathname[MAXPGPATH];
 int   fd;
 StringInfoData buf;
 bool  first_file;
 bool  still_checksumming;
 pg_checksum_context manifest_ctx;
};

static void escape_json(StringInfo buf, const char *str);
static void flush_manifest(manifest_writer *mwriter);
static size_t hex_encode(const uint8 *src, size_t len, char *dst);

/*
 * Create a new backup manifest writer.
 *
 * The backup manifest will be written into a file named backup_manifest
 * in the specified directory.
 */

manifest_writer *
create_manifest_writer(char *directory, uint64 system_identifier)
{
 manifest_writer *mwriter = pg_malloc(sizeof(manifest_writer));

 snprintf(mwriter->pathname, MAXPGPATH, "%s/backup_manifest", directory);
 mwriter->fd = -1;
 initStringInfo(&mwriter->buf);
 mwriter->first_file = true;
 mwriter->still_checksumming = true;
 pg_checksum_init(&mwriter->manifest_ctx, CHECKSUM_TYPE_SHA256);

 appendStringInfo(&mwriter->buf,
      "{ \"PostgreSQL-Backup-Manifest-Version\": 2,\n"
      "\"System-Identifier\": " UINT64_FORMAT ",\n"
      "\"Files\": [",
      system_identifier);

 return mwriter;
}

/*
 * Add an entry for a file to a backup manifest.
 *
 * This is very similar to the backend's AddFileToBackupManifest, but
 * various adjustments are required due to frontend/backend differences
 * and other details.
 */

void
add_file_to_manifest(manifest_writer *mwriter, const char *manifest_path,
      uint64 size, time_t mtime,
      pg_checksum_type checksum_type,
      int checksum_length,
      uint8 *checksum_payload)
{
 int   pathlen = strlen(manifest_path);

 if (mwriter->first_file)
 {
  appendStringInfoChar(&mwriter->buf, '\n');
  mwriter->first_file = false;
 }
 else
  appendStringInfoString(&mwriter->buf, ",\n");

 if (pg_encoding_verifymbstr(PG_UTF8, manifest_path, pathlen) == pathlen)
 {
  appendStringInfoString(&mwriter->buf, "{ \"Path\": ");
  escape_json(&mwriter->buf, manifest_path);
  appendStringInfoString(&mwriter->buf, ", ");
 }
 else
 {
  appendStringInfoString(&mwriter->buf, "{ \"Encoded-Path\": \"");
  enlargeStringInfo(&mwriter->buf, 2 * pathlen);
  mwriter->buf.len += hex_encode((const uint8 *) manifest_path, pathlen,
            &mwriter->buf.data[mwriter->buf.len]);
  appendStringInfoString(&mwriter->buf, "\"");
 }

 appendStringInfo(&mwriter->buf, "\"Size\": %" PRIu64 ", ", size);

 appendStringInfoString(&mwriter->buf, "\"Last-Modified\": \"");
 enlargeStringInfo(&mwriter->buf, 128);
 mwriter->buf.len += strftime(&mwriter->buf.data[mwriter->buf.len], 128,
         "%Y-%m-%d %H:%M:%S %Z",
         gmtime(&mtime));
 appendStringInfoChar(&mwriter->buf, '"');

 if (mwriter->buf.len > 128 * 1024)
  flush_manifest(mwriter);

 if (checksum_length > 0)
 {
  appendStringInfo(&mwriter->buf,
       ", \"Checksum-Algorithm\": \"%s\", \"Checksum\": \"",
       pg_checksum_type_name(checksum_type));

  enlargeStringInfo(&mwriter->buf, 2 * checksum_length);
  mwriter->buf.len += hex_encode(checksum_payload, checksum_length,
            &mwriter->buf.data[mwriter->buf.len]);

  appendStringInfoChar(&mwriter->buf, '"');
 }

 appendStringInfoString(&mwriter->buf, " }");

 if (mwriter->buf.len > 128 * 1024)
  flush_manifest(mwriter);
}

/*
 * Finalize the backup_manifest.
 */

void
finalize_manifest(manifest_writer *mwriter,
      manifest_wal_range *first_wal_range)
{
 uint8  checksumbuf[PG_SHA256_DIGEST_LENGTH];
 int   len;
 manifest_wal_range *wal_range;

 /* Terminate the list of files. */
 appendStringInfoString(&mwriter->buf, "\n],\n");

 /* Start a list of LSN ranges. */
 appendStringInfoString(&mwriter->buf, "\"WAL-Ranges\": [\n");

 for (wal_range = first_wal_range; wal_range != NULL;
   wal_range = wal_range->next)
  appendStringInfo(&mwriter->buf,
       "%s{ \"Timeline\": %u, \"Start-LSN\": \"%X/%X\", \"End-LSN\": \"%X/%X\" }",
       wal_range == first_wal_range ? "" : ",\n",
       wal_range->tli,
       LSN_FORMAT_ARGS(wal_range->start_lsn),
       LSN_FORMAT_ARGS(wal_range->end_lsn));

 /* Terminate the list of WAL ranges. */
 appendStringInfoString(&mwriter->buf, "\n],\n");

 /* Flush accumulated data and update checksum calculation. */
 flush_manifest(mwriter);

 /* Checksum only includes data up to this point. */
 mwriter->still_checksumming = false;

 /* Compute and insert manifest checksum. */
 appendStringInfoString(&mwriter->buf, "\"Manifest-Checksum\": \"");
 enlargeStringInfo(&mwriter->buf, 2 * PG_SHA256_DIGEST_STRING_LENGTH);
 len = pg_checksum_final(&mwriter->manifest_ctx, checksumbuf);
 Assert(len == PG_SHA256_DIGEST_LENGTH);
 mwriter->buf.len +=
  hex_encode(checksumbuf, len, &mwriter->buf.data[mwriter->buf.len]);
 appendStringInfoString(&mwriter->buf, "\"}\n");

 /* Flush the last manifest checksum itself. */
 flush_manifest(mwriter);

 /* Close the file. */
 if (close(mwriter->fd) != 0)
  pg_fatal("could not close file \"%s\": %m", mwriter->pathname);
 mwriter->fd = -1;
}

/*
 * Produce a JSON string literal, properly escaping characters in the text.
 */

static void
escape_json(StringInfo buf, const char *str)
{
 const char *p;

 appendStringInfoCharMacro(buf, '"');
 for (p = str; *p; p++)
 {
  switch (*p)
  {
   case '\b':
    appendStringInfoString(buf, "\\b");
    break;
   case '\f':
    appendStringInfoString(buf, "\\f");
    break;
   case '\n':
    appendStringInfoString(buf, "\\n");
    break;
   case '\r':
    appendStringInfoString(buf, "\\r");
    break;
   case '\t':
    appendStringInfoString(buf, "\\t");
    break;
   case '"':
    appendStringInfoString(buf, "\\\"");
    break;
   case '\\':
    appendStringInfoString(buf, "\\\\");
    break;
   default:
    if ((unsigned char) *p < ' ')
     appendStringInfo(buf, "\\u%04x", (int) *p);
    else
     appendStringInfoCharMacro(buf, *p);
    break;
  }
 }
 appendStringInfoCharMacro(buf, '"');
}

/*
 * Flush whatever portion of the backup manifest we have generated and
 * buffered in memory out to a file on disk.
 *
 * The first call to this function will create the file. After that, we
 * keep it open and just append more data.
 */

static void
flush_manifest(manifest_writer *mwriter)
{
 if (mwriter->fd == -1 &&
  (mwriter->fd = open(mwriter->pathname,
       O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
       pg_file_create_mode)) < 0)
  pg_fatal("could not open file \"%s\": %m", mwriter->pathname);

 if (mwriter->buf.len > 0)
 {
  ssize_t  wb;

  wb = write(mwriter->fd, mwriter->buf.data, mwriter->buf.len);
  if (wb != mwriter->buf.len)
  {
   if (wb < 0)
    pg_fatal("could not write file \"%s\": %m", mwriter->pathname);
   else
    pg_fatal("could not write file \"%s\": wrote %d of %d",
       mwriter->pathname, (int) wb, mwriter->buf.len);
  }

  if (mwriter->still_checksumming &&
   pg_checksum_update(&mwriter->manifest_ctx,
          (uint8 *) mwriter->buf.data,
          mwriter->buf.len) < 0)
   pg_fatal("could not update checksum of file \"%s\"",
      mwriter->pathname);
  resetStringInfo(&mwriter->buf);
 }
}

/*
 * Encode bytes using two hexadecimal digits for each one.
 */

static size_t
hex_encode(const uint8 *src, size_t len, char *dst)
{
 const uint8 *end = src + len;

 while (src < end)
 {
  unsigned n1 = (*src >> 4) & 0xF;
  unsigned n2 = *src & 0xF;

  *dst++ = n1 < 10 ? '0' + n1 : 'a' + n1 - 10;
  *dst++ = n2 < 10 ? '0' + n2 : 'a' + n2 - 10;
  ++src;
 }

 return len * 2;
}

Messung V0.5 in Prozent
C=91 H=95 G=92

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.