Exemple #1
0
/* print mkdirs.sh */
void
dir_print_mkdirs_sh(FILE *out, const parray *files, const char *root)
{
	int i;

	for (i = 0; i < parray_num(files); i++)
	{
		pgFile *file = (pgFile *) parray_get(files, i);
		if (S_ISDIR(file->mode))
		{
			if (strstr(file->path, root) == file->path) {
				fprintf(out, "mkdir -m 700 -p %s\n", file->path + strlen(root)
					+ 1);
			}
			else {
				fprintf(out, "mkdir -m 700 -p %s\n", file->path);
			}
		}
	}

	fprintf(out, "\n");

	for (i = 0; i < parray_num(files); i++)
	{
		pgFile *file = (pgFile *) parray_get(files, i);
		if (S_ISLNK(file->mode))
		{
			fprintf(out, "rm -f %s\n", file->path + strlen(root) + 1);
			fprintf(out, "ln -s %s %s\n", file->linked, file->path + strlen(root) + 1);
		}
	}
}
Exemple #2
0
/*
 * Validate files in the backup with size or CRC.
 */
static bool
pgBackupValidateFiles(parray *files, const char *root, bool size_only)
{
    int		i;

    for (i = 0; i < parray_num(files); i++)
    {
        struct stat st;

        pgFile *file = (pgFile *) parray_get(files, i);

        if (interrupted)
            elog(ERROR_INTERRUPTED, _("interrupted during validate"));

        /* skipped backup while incremental backup */
        if (file->write_size == BYTES_INVALID || !S_ISREG(file->mode))
            continue;

        /* print progress */
        elog(LOG, _("(%d/%lu) %s"), i + 1, (unsigned long) parray_num(files),
             get_relative_path(file->path, root));

        /* always validate file size */
        if (stat(file->path, &st) == -1)
        {
            if (errno == ENOENT)
                elog(WARNING, _("backup file \"%s\" vanished"), file->path);
            else
                elog(ERROR_SYSTEM, _("can't stat backup file \"%s\": %s"),
                     get_relative_path(file->path, root), strerror(errno));
            return false;
        }
        if (file->write_size != st.st_size)
        {
            elog(WARNING, _("size of backup file \"%s\" must be %lu but %lu"),
                 get_relative_path(file->path, root),
                 (unsigned long) file->write_size,
                 (unsigned long) st.st_size);
            return false;
        }

        /* validate CRC too */
        if (!size_only)
        {
            pg_crc32	crc;

            crc = pgFileGetCRC(file);
            if (crc != file->crc)
            {
                elog(WARNING, _("CRC of backup file \"%s\" must be %X but %X"),
                     get_relative_path(file->path, root), file->crc, crc);
                return false;
            }
        }
    }

    return true;
}
Exemple #3
0
/* print file list */
void
dir_print_file_list(FILE *out, const parray *files, const char *root, const char *prefix)
{
	int i;
	int root_len = 0;

	/* calculate length of root directory portion */
	if (root)
	{
		root_len = strlen(root);
		if (root[root_len - 1] != '/')
			root_len++;
	}

	/* print each file in the list */
	for (i = 0; i < parray_num(files); i++)
	{
		pgFile *file = (pgFile *)parray_get(files, i);
		char path[MAXPGPATH];
		char *ptr = file->path;
		char type;

		/* omit root directory portion */
		if (root && strstr(ptr, root) == ptr)
			ptr = JoinPathEnd(ptr, root);

		/* append prefix if not NULL */
		if (prefix)
			join_path_components(path, prefix, ptr);
		else
			strcpy(path, ptr);

		if (S_ISREG(file->mode) && file->is_datafile)
			type = 'F';
		else if (S_ISREG(file->mode) && !file->is_datafile)
			type = 'f';
		else if (S_ISDIR(file->mode))
			type = 'd';
		else if (S_ISLNK(file->mode))
			type = 'l';
		else
			type = '?';

		fprintf(out, "%s %c %lu %u 0%o", path, type,
			(unsigned long) file->write_size,
			file->crc, file->mode & (S_IRWXU | S_IRWXG | S_IRWXO));

		if (S_ISLNK(file->mode))
			fprintf(out, " %s\n", file->linked);
		else
		{
			char timestamp[20];
			time2iso(timestamp, 20, file->mtime);
			fprintf(out, " %s\n", timestamp);
		}
	}
}
Exemple #4
0
/*
 * Concatinate two parray.
 * parray_concat() appends the copy of the content of src to the end of dest.
 */
parray *
parray_concat(parray *dest, const parray *src)
{
    /* expand head array */
    parray_expand(dest, dest->used + src->used);

    /* copy content of src after content of dest */
    memcpy(dest->data + dest->used, src->data, src->used * sizeof(void *));
    dest->used += parray_num(src);

    return dest;
}
Exemple #5
0
/*
 * Validate files in the backup and update its status to OK.
 * If any of files are corrupted, update its stutus to CORRUPT.
 */
int
do_validate(pgBackupRange *range)
{
    int		i;
    parray *backup_list;
    int ret;
    bool another_pg_rman = false;

    ret = catalog_lock();
    if (ret == 1)
        another_pg_rman = true;

    /* get backup list matches given range */
    backup_list = catalog_get_backup_list(range);
    if(!backup_list) {
        elog(ERROR_SYSTEM, _("can't process any more."));
    }
    parray_qsort(backup_list, pgBackupCompareId);
    for (i = 0; i < parray_num(backup_list); i++)
    {
        pgBackup *backup = (pgBackup *)parray_get(backup_list, i);

        /* clean extra backups (switch STATUS to ERROR) */
        if(!another_pg_rman &&
                (backup->status == BACKUP_STATUS_RUNNING || backup->status == BACKUP_STATUS_DELETING)) {
            backup->status = BACKUP_STATUS_ERROR;
            pgBackupWriteIni(backup);
        }

        /* Validate completed backups only. */
        if (backup->status != BACKUP_STATUS_DONE)
            continue;

        /* validate with CRC value and update status to OK */
        pgBackupValidate(backup, false, false, (HAVE_DATABASE(backup)));
    }

    /* cleanup */
    parray_walk(backup_list, pgBackupFree);
    parray_free(backup_list);

    catalog_unlock();

    return 0;
}
Exemple #6
0
/* copy contents of directory from_root into to_root */
void
dir_copy_files(const char *from_root, const char *to_root)
{
	int		i;
	parray *files = parray_new();

	/* don't copy root directory */
	dir_list_file(files, from_root, NULL, true, false);

	for (i = 0; i < parray_num(files); i++)
	{
		pgFile *file = (pgFile *) parray_get(files, i);

		if (S_ISDIR(file->mode))
		{
			char to_path[MAXPGPATH];
			join_path_components(to_path, to_root, file->path + strlen(from_root) + 1);
			if (verbose && !check)
				printf(_("create directory \"%s\"\n"),
					file->path + strlen(from_root) + 1);
			if (!check) {
				dir_create_dir(to_path, DIR_PERMISSION);
			}
			continue;
		}
		else if(S_ISREG(file->mode))
		{
			if (verbose && !check)
				printf(_("copy \"%s\"\n"),
					file->path + strlen(from_root) + 1);
			if (!check)
				copy_file(from_root, to_root, file, NO_COMPRESSION);
		}
	}

	/* cleanup */
	parray_walk(files, pgFileFree);
	parray_free(files);
}
Exemple #7
0
/*
 * Delete backup files of the backup and update the status of the backup to
 * BACKUP_STATUS_DELETED.
 */
static int
pgBackupDeleteFiles(pgBackup *backup)
{
	int		i;
	char	path[MAXPGPATH];
	char	timestamp[20];
	parray *files;

	/*
	 * If the backup was deleted already, there is nothing to do.
	 */
	if (backup->status == BACKUP_STATUS_DELETED)
		return 0;

	time2iso(timestamp, lengthof(timestamp), backup->start_time);

	elog(INFO, "delete: %s", timestamp);

	/*
	 * Update STATUS to BACKUP_STATUS_DELETING in preparation for the case which
	 * the error occurs before deleting all backup files.
	 */
	if (!check)
	{
		backup->status = BACKUP_STATUS_DELETING;
		pgBackupWriteIni(backup);
	}

	/* list files to be deleted */
	files = parray_new();
	pgBackupGetPath(backup, path, lengthof(path), DATABASE_DIR);
	dir_list_file(files, path, NULL, true, true);

	/* delete leaf node first */
	parray_qsort(files, pgFileComparePathDesc);
	for (i = 0; i < parray_num(files); i++)
	{
		pgFile *file = (pgFile *) parray_get(files, i);

		/* print progress */
		elog(LOG, "delete file(%d/%lu) \"%s\"", i + 1,
				(unsigned long) parray_num(files), file->path);

		/* skip actual deletion in check mode */
		if (!check)
		{
			if (remove(file->path))
			{
				elog(WARNING, "can't remove \"%s\": %s", file->path,
					strerror(errno));
				parray_walk(files, pgFileFree);
				parray_free(files);
				return 1;
			}
		}
	}

	/*
	 * After deleting all of the backup files, update STATUS to
	 * BACKUP_STATUS_DELETED.
	 */
	if (!check)
	{
		backup->status = BACKUP_STATUS_DELETED;
		pgBackupWriteIni(backup);
	}

	parray_walk(files, pgFileFree);
	parray_free(files);

	return 0;
}
Exemple #8
0
int
do_delete(pgBackupRange *range)
{
	int			i;
	int			ret;
	parray	   *backup_list;
	bool		do_delete = false;
	XLogRecPtr	oldest_lsn = InvalidXLogRecPtr;
	TimeLineID	oldest_tli;

	/* DATE are always required */
	if (!pgBackupRangeIsValid(range))
		elog(ERROR, "required delete range option not specified: delete DATE");

	/* Lock backup catalog */
	ret = catalog_lock();
	if (ret == -1)
		elog(ERROR, "can't lock backup catalog.");
	else if (ret == 1)
		elog(ERROR,
			"another pg_arman is running, stop delete.");

	/* Get complete list of backups */
	backup_list = catalog_get_backup_list(NULL);
	if (!backup_list)
		elog(ERROR, "No backup list found, can't process any more.");

	/* Find backups to be deleted */
	for (i = 0; i < parray_num(backup_list); i++)
	{
		pgBackup *backup = (pgBackup *) parray_get(backup_list, i);

		/* delete backup and update status to DELETED */
		if (do_delete)
		{
			/* check for interrupt */
			if (interrupted)
				elog(ERROR, "interrupted during delete backup");

			pgBackupDeleteFiles(backup);
			continue;
		}

		/* Found the latest full backup */
		if (backup->backup_mode >= BACKUP_MODE_FULL &&
			backup->status == BACKUP_STATUS_OK &&
			backup->start_time <= range->begin)
		{
			do_delete = true;
			oldest_lsn = backup->start_lsn;
			oldest_tli = backup->tli;
		}
	}

	/* release catalog lock */
	catalog_unlock();

	/* cleanup */
	parray_walk(backup_list, pgBackupFree);
	parray_free(backup_list);

	/*
	 * Delete in archive WAL segments that are not needed anymore. The oldest
	 * segment to be kept is the first segment that the oldest full backup
	 * found around needs to keep.
	 */
	if (!XLogRecPtrIsInvalid(oldest_lsn))
	{
		XLogSegNo   targetSegNo;
		char		oldestSegmentNeeded[MAXFNAMELEN];
		DIR		   *arcdir;
		struct dirent *arcde;
		char		wal_file[MAXPGPATH];
		int			rc;

		XLByteToSeg(oldest_lsn, targetSegNo);
		XLogFileName(oldestSegmentNeeded, oldest_tli, targetSegNo);
		elog(LOG, "Removing segments older than %s", oldestSegmentNeeded);

		/*
		 * Now is time to do the actual work and to remove all the segments
		 * not needed anymore.
		 */
		if ((arcdir = opendir(arclog_path)) != NULL)
		{
			while (errno = 0, (arcde = readdir(arcdir)) != NULL)
			{
				/*
				 * We ignore the timeline part of the XLOG segment identifiers in
				 * deciding whether a segment is still needed.  This ensures that
				 * we won't prematurely remove a segment from a parent timeline.
				 * We could probably be a little more proactive about removing
				 * segments of non-parent timelines, but that would be a whole lot
				 * more complicated.
				 *
				 * We use the alphanumeric sorting property of the filenames to
				 * decide which ones are earlier than the exclusiveCleanupFileName
				 * file. Note that this means files are not removed in the order
				 * they were originally written, in case this worries you.
				 */
				if ((IsXLogFileName(arcde->d_name) ||
					 IsPartialXLogFileName(arcde->d_name)) &&
					strcmp(arcde->d_name + 8, oldestSegmentNeeded + 8) < 0)
				{
					/*
					 * Use the original file name again now, including any
					 * extension that might have been chopped off before testing
					 * the sequence.
					 */
					snprintf(wal_file, MAXPGPATH, "%s/%s",
							 arclog_path, arcde->d_name);

					rc = unlink(wal_file);
					if (rc != 0)
					{
						elog(WARNING, "could not remove file \"%s\": %s",
							 wal_file, strerror(errno));
						break;
					}
					elog(LOG, "removed WAL segment \"%s\"", wal_file);
				}
			}
			if (errno)
				elog(WARNING, "could not read archive location \"%s\": %s",
					 arclog_path, strerror(errno));
			if (closedir(arcdir))
				elog(WARNING, "could not close archive location \"%s\": %s",
					 arclog_path, strerror(errno));
		}
		else
			elog(WARNING, "could not open archive location \"%s\": %s",
				 arclog_path, strerror(errno));
	}

	return 0;
}
Exemple #9
0
/*
 * Delete backups that are older than KEEP_xxx_DAYS and have more generations
 * than KEEP_xxx_FILES.
 */
void
pgBackupDelete(int keep_generations, int keep_days)
{
	int		i;
	parray *backup_list;
	int		backup_num;
	time_t	days_threshold = current.start_time - (keep_days * 60 * 60 * 24);

	if (verbose)
	{
		char generations_str[100];
		char days_str[100];

		if (keep_generations == KEEP_INFINITE)
			strncpy(generations_str, "INFINITE",
					lengthof(generations_str));
		else
			snprintf(generations_str, lengthof(generations_str),
					"%d", keep_generations);

		if (keep_days == KEEP_INFINITE)
			strncpy(days_str, "INFINITE", lengthof(days_str));
		else
			snprintf(days_str, lengthof(days_str), "%d", keep_days);

		elog(LOG, "deleted old backups (generations=%s, days=%s)",
			 generations_str, days_str);
	}

	/* Leave if an infinite generation of backups is kept */
	if (keep_generations == KEEP_INFINITE && keep_days == KEEP_INFINITE)
	{
		elog(LOG, "%s() infinite", __FUNCTION__);
		return;
	}

	/* Get a complete list of backups. */
	backup_list = catalog_get_backup_list(NULL);

	/* Find target backups to be deleted */
	backup_num = 0;
	for (i = 0; i < parray_num(backup_list); i++)
	{
		pgBackup   *backup = (pgBackup *) parray_get(backup_list, i);
		int			backup_num_evaluate = backup_num;

		elog(LOG, "%s() %lu", __FUNCTION__, backup->start_time);

		/*
		 * When a validate full backup was found, we can delete the
		 * backup that is older than it using the number of generations.
		 */
		if (backup->backup_mode == BACKUP_MODE_FULL &&
			backup->status == BACKUP_STATUS_OK)
			backup_num++;

		/* Evaluate if this backup is eligible for removal */
		if (backup_num_evaluate + 1 <= keep_generations &&
			keep_generations != KEEP_INFINITE)
		{
			/* Do not include the latest full backup in this count */
			elog(LOG, "%s() backup are only %d", __FUNCTION__, backup_num);
			continue;
		}
		else if (backup->start_time >= days_threshold &&
				 keep_days != KEEP_INFINITE)
		{
			/*
			 * If the start time of the backup is older than the threshold and
			 * there are enough generations of full backups, delete the backup.
			 */
			elog(LOG, "%s() %lu is not older than %lu", __FUNCTION__,
				backup->start_time, days_threshold);
			continue;
		}

		elog(LOG, "%s() %lu is older than %lu", __FUNCTION__,
			backup->start_time, days_threshold);

		/* delete backup and update status to DELETED */
		pgBackupDeleteFiles(backup);
	}

	/* cleanup */
	parray_walk(backup_list, pgBackupFree);
	parray_free(backup_list);
}