/* * List files, symbolic links and directories in the directory "root" and add * pgFile objects to "files". We add "root" to "files" if add_root is true. * * If the sub-directory name is in "exclude" list, the sub-directory itself is * listed but the contents of the sub-directory is ignored. * * When omit_symlink is true, symbolic link is ignored and only file or * directory llnked to will be listed. */ void dir_list_file(parray *files, const char *root, const char *exclude[], bool omit_symlink, bool add_root) { char path[MAXPGPATH]; char buf[MAXPGPATH * 2]; char black_item[MAXPGPATH * 2]; parray *black_list = NULL; join_path_components(path, backup_path, PG_BLACK_LIST); if (root && pgdata && strcmp(root, pgdata) == 0 && fileExists(path)) { FILE *black_list_file = NULL; black_list = parray_new(); black_list_file = fopen(path, "r"); if (black_list_file == NULL) ereport(ERROR, (errcode(ERROR_SYSTEM), errmsg("could not open black_list: %s", strerror(errno)))); while (fgets(buf, lengthof(buf), black_list_file) != NULL) { join_path_components(black_item, pgdata, buf); if (black_item[strlen(black_item) - 1] == '\n') black_item[strlen(black_item) - 1] = '\0'; if (black_item[0] == '#' || black_item[0] == '\0') continue; parray_append(black_list, black_item); } fclose(black_list_file); parray_qsort(black_list, BlackListCompare); dir_list_file_internal(files, root, exclude, omit_symlink, add_root, black_list); } else dir_list_file_internal(files, root, exclude, omit_symlink, add_root, NULL); }
/* * 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; }
/* * Construct parray of pgFile from the file list. * If root is not NULL, path will be absolute path. */ parray * dir_read_file_list(const char *root, const char *file_txt) { FILE *fp; parray *files; char buf[MAXPGPATH * 2]; fp = fopen(file_txt, "rt"); if (fp == NULL) ereport(ERROR, ((errno == ENOENT ? errcode(ERROR_CORRUPTED) : errcode(ERROR_SYSTEM)), errmsg("could not open \"%s\": %s", file_txt, strerror(errno)))); files = parray_new(); while (fgets(buf, lengthof(buf), fp)) { char path[MAXPGPATH]; char type; unsigned long write_size; pg_crc32c crc; unsigned int mode; /* bit length of mode_t depends on platforms */ struct tm tm; pgFile *file; memset(&tm, 0, sizeof(tm)); if (sscanf(buf, "%s %c %lu %u %o %d-%d-%d %d:%d:%d", path, &type, &write_size, &crc, &mode, &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 11) { ereport(ERROR, (errcode(ERROR_CORRUPTED), errmsg("invalid format found in \"%s\"", file_txt))); } if (type != 'f' && type != 'F' && type != 'd' && type != 'l') { ereport(ERROR, (errcode(ERROR_CORRUPTED), errmsg("invalid type '%c' found in \"%s\"", type, file_txt))); } tm.tm_isdst = -1; file = (pgFile *) pgut_malloc(offsetof(pgFile, path) + (root ? strlen(root) + 1 : 0) + strlen(path) + 1); tm.tm_year -= 1900; tm.tm_mon -= 1; file->mtime = mktime(&tm); file->mode = mode | ((type == 'f' || type == 'F') ? S_IFREG : type == 'd' ? S_IFDIR : type == 'l' ? S_IFLNK : 0); file->size = 0; file->read_size = 0; file->write_size = write_size; file->crc = crc; file->is_datafile = (type == 'F' ? true : false); file->linked = NULL; if (root) sprintf(file->path, "%s/%s", root, path); else strcpy(file->path, path); parray_append(files, file); } fclose(fp); /* file.txt is sorted, so this qsort is redundant */ parray_qsort(files, pgFileComparePath); return files; }
void dir_list_file_internal(parray *files, const char *root, const char *exclude[], bool omit_symlink, bool add_root, parray *black_list) { pgFile *file; file = pgFileNew(root, omit_symlink); if (file == NULL) return; /* skip if the file is in black_list defined by user */ if (black_list && parray_bsearch(black_list, root, BlackListCompare)) { /* found in black_list. skip this item */ return; } if (add_root) parray_append(files, file); /* chase symbolic link chain and find regular file or directory */ while (S_ISLNK(file->mode)) { ssize_t len; char linked[MAXPGPATH]; len = readlink(file->path, linked, sizeof(linked)); if (len == -1) { ereport(ERROR, (errcode(ERROR_SYSTEM), errmsg("could not read link \"%s\": %s", file->path, strerror(errno)))); } linked[len] = '\0'; file->linked = pgut_strdup(linked); /* make absolute path to read linked file */ if (linked[0] != '/') { char dname[MAXPGPATH]; char absolute[MAXPGPATH]; strncpy(dname, file->path, lengthof(dname)); join_path_components(absolute, dirname(dname), linked); file = pgFileNew(absolute, omit_symlink); } else file = pgFileNew(file->linked, omit_symlink); /* linked file is not found, stop following link chain */ if (file == NULL) return; parray_append(files, file); } /* * If the entry was a directory, add it to the list and add call this * function recursivelly. * If the directory name is in the exclude list, do not list the contents. */ while (S_ISDIR(file->mode)) { int i; bool skip = false; DIR *dir; struct dirent *dent; char *dirname; /* skip entry which matches exclude list */ dirname = strrchr(file->path, '/'); if (dirname == NULL) dirname = file->path; else dirname++; /* * If the item in the exclude list starts with '/', compare to the * absolute path of the directory. Otherwise compare to the directory * name portion. */ for (i = 0; exclude && exclude[i]; i++) { if (exclude[i][0] == '/') { if (strcmp(file->path, exclude[i]) == 0) { skip = true; break; } } else { if (strcmp(dirname, exclude[i]) == 0) { skip = true; break; } } } if (skip) break; /* open directory and list contents */ dir = opendir(file->path); if (dir == NULL) { if (errno == ENOENT) { /* maybe the direcotry was removed */ return; } ereport(ERROR, (errcode(ERROR_SYSTEM), errmsg("could not open directory \"%s\": %s", file->path, strerror(errno)))); } errno = 0; while ((dent = readdir(dir))) { char child[MAXPGPATH]; /* skip entries point current dir or parent dir */ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue; join_path_components(child, file->path, dent->d_name); dir_list_file_internal(files, child, exclude, omit_symlink, true, black_list); } if (errno && errno != ENOENT) { int errno_tmp = errno; closedir(dir); ereport(ERROR, (errcode(ERROR_SYSTEM), errmsg("could not read directory \"%s\": %s", file->path, strerror(errno_tmp)))); } closedir(dir); break; /* pseudo loop */ } parray_qsort(files, pgFileComparePath); }
/* * 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; }