zipl/src: Implement '--dry-run' option for ngdumps

Use the directory specified by the shell environment variable TMPDIR
for temporary objects creation and ngdump job simulation. If TMPDIR is
not set, then use "/tmp" for the mentioned purposes;

While running ngdump job in 'dry-run' mode:
. Don't format/mount the target dump device. Instead, create the
  bootmap file and the meta-file at the temporary mount point without
  mounting anything to it. Thus, the mentioned files to be acrually
  created in the "proxy" file system owning the temporary mount point;
. Retrieve base disk info from the read-only dump device and
  complete that info with the block size of the proxy file system;

Separate the steps on retrieving/setting file system block size
into a dedicated procedure;

Use definitions instead of hardcoded file names;

Fix a bug in an error path (accessing freed memory);

Make misc_open_simulate() and misc_open_exclusive() static;

Update man pages with the requirements on the system environment
(resources) for ngdump job being executed in dry-run mode;

Provide hints for user (in stderr) in case when ngdump job in dry-run
mode failed due to inappropriate system environment.

Tested-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Eduard Shishkin
2025-02-17 10:29:26 +01:00
committed by Jan Höppner
parent 1e8edb6524
commit c39722aff2
13 changed files with 160 additions and 60 deletions
+3
View File
@@ -19,6 +19,8 @@
#include "zipl.h"
#include "lib/vtoc.h"
#define FS_MAP_ERROR "Could not get file mapping"
/* Type for representing disk block numbers */
typedef uint64_t blocknum_t;
@@ -102,6 +104,7 @@ int disk_type_is_eckd_ldl(struct disk_ext_type *ext_type);
int disk_type_is_nvme(struct disk_ext_type *ext_type);
int disk_type_is_eckd(disk_type_t type);
int disk_info_set_fs_block(const char *filename, struct disk_info *info);
int disk_get_info_from_file(const char* filename,
struct job_target_data* target,
struct disk_info** info);
+1
View File
@@ -20,5 +20,6 @@ void error_text(const char* fmt, ...);
void error_clear_reason(void);
void error_clear_text(void);
void error_print(void);
int is_error(const char *this);
#endif /* not ERROR_H */
+1
View File
@@ -261,5 +261,6 @@ int check_job_images_ngdump(struct job_dump_data* dump, char* name);
void job_dump_check_set_ngdump(struct job_data *job,
struct disk_ext_type *ext_type);
bool job_dump_is_ngdump(struct job_data *job);
void ngdump_delete_meta(const char *path);
#endif /* not JOB_H */
+1 -3
View File
@@ -33,8 +33,6 @@ void* misc_malloc(size_t size);
int misc_asprintf(char **out, const char *fmt, ...);
void* misc_calloc(size_t n, size_t size);
char* misc_strdup(const char* s);
int misc_open_exclusive(const char* filename);
int misc_open_simulate(const char *filename, struct misc_fd *mfd);
int misc_open_device(const char *filename, struct misc_fd *mfd, int simulate);
int misc_read(int fd, void* buffer, size_t count);
int misc_read_file(const char* filename, char** buffer, size_t* size,
@@ -49,7 +47,7 @@ int misc_get_file_buffer(const char* filename,
struct misc_file_buffer* buffer);
void misc_free_file_buffer(struct misc_file_buffer* file);
int misc_get_char(struct misc_file_buffer* file, off_t readahead);
char* misc_make_path(char* dirname, char* filename);
char *misc_make_path(const char *dirname, char *filename);
int misc_temp_dev(dev_t dev, int blockdev, char** devno);
int misc_temp_dev_from_file(char* file, char** devno);
void misc_free_temp_dev(char* device);
+2
View File
@@ -25,7 +25,9 @@
#define BOOTMAP_FILENAME "bootmap"
#define BOOTMAP_TEMPLATE_FILENAME "bootmap_temp.XXXXXX"
#define DUMP_TEMP_MOUNT_POINT_DIR "/tmp"
#define DUMP_TEMP_MOUNT_POINT_NAME "zipl-dump-mount-point-XXXXXX"
#define DUMP_META_FILE_NAME "ngdump.meta"
#define DEFAULTBOOT_SECTION "defaultboot"
+7
View File
@@ -366,6 +366,13 @@ error-checking is still done.
Note that when working with tapes, the current device position will be changed
even when specifying this option.
When working with ngdumps, the directory specified by the shell environment
variable TMPDIR is used for simulating the dump job. This directory should
belong to a "real" file system managing disk space addresses (like ext4, or
xfs), and have enough free space (matching the size of the specified dump
device). If TMPDIR is not set, then "/tmp" is used, which can lead to errors,
e.g. in case when it belongs to tmpfs.
.TP
.BR "\-f" " or " "\-\-force"
Omit validity checking while producing a multi-volume dump.
+87 -22
View File
@@ -1542,11 +1542,39 @@ static int prepare_build_program_table_file(struct job_data *job,
return -1;
}
bis->tmp_filename_created = 1;
/* Retrieve target device information. Note that we have to
* call disk_get_info_from_file() to also get the file system
* block size. */
if (disk_get_info_from_file(bis->filename, &job->target, &bis->info))
return -1;
/*
* Retrieve target device information and
* complete the info with the file system block size
*/
if (job->id == job_dump_partition && dry_run) {
/*
* ngdump job in dry-run mode.
*
* The dump device has read-only status.
* The bootmap and the meta-file to be created
* directly at the temporary mount point wthout
* mounting anything to it (thus, the mentioned
* files to be actually created in the "proxy"
* file system.
*
* Retrieve info from the dump device
* Retrieve file system block size from the proxy
* file system
*/
if (disk_get_info(job->data.dump.device,
&job->target, &bis->info))
return -1;
if (disk_info_set_fs_block(bis->filename, bis->info))
return -1;
} else {
/*
* ngdump or ipl job.
*/
if (disk_get_info_from_file(bis->filename,
&job->target,
&bis->info))
return -1;
}
if (!disk_is_appropriate(job, bis->info))
return -1;
if (verbose) {
@@ -1627,8 +1655,17 @@ static int bootmap_create_file(struct job_data *job, struct install_set *bis,
return 0;
}
void ngdump_delete_meta(const char *dir)
{
char *filename = NULL;
filename = misc_make_path(dir, DUMP_META_FILE_NAME);
unlink(filename);
free(filename);
}
/**
* Create a file with the short name "ngdump.meta" in the directory PATH.
* Create a file with the short name DUMP_META_FILE_NAME in the directory PATH.
* This file is required for NGDump stand-alone dumper, it's read/written
* by the dumper when it starts.
*/
@@ -1638,13 +1675,13 @@ static int ngdump_create_meta(const char *path)
FILE *fp;
int rc;
util_asprintf(&filename, "%s/ngdump.meta", path);
filename = misc_make_path(path, DUMP_META_FILE_NAME);
fp = fopen(filename, "w");
if (!fp) {
free(filename);
error_reason(strerror(errno));
error_text("Could not create file '%s'", filename);
free(filename);
return -1;
}
free(filename);
@@ -1665,6 +1702,40 @@ static int ngdump_create_meta(const char *path)
return 0;
}
static int ngdump_mount_device(struct job_data *job)
{
if (dry_run)
/*
* the bootmap and the meta-file to be stored
* directly at the temporary mount point without
* mounting any partition
*/
return 0;
/*
* the bootmap and the meta-file to be stored on the
* formatted and mounted dump partition
*/
if (mount(job->data.dump.device, job->target.bootmap_dir,
NGDUMP_FSTYPE, 0, NULL)) {
error_reason(strerror(errno));
error_text("Could not mount partition '%s':",
job->data.dump.device);
return -1;
}
job->dump_mounted = 1;
return 0;
}
static char *ngdump_dir(void)
{
return getenv("TMPDIR") ? : DUMP_TEMP_MOUNT_POINT_DIR;
}
static char *build_mount_point_pathname(void)
{
return misc_make_path(ngdump_dir(), DUMP_TEMP_MOUNT_POINT_NAME);
}
static int prepare_bootloader_ngdump(struct job_data *job,
struct install_set *bis)
{
@@ -1679,8 +1750,7 @@ static int prepare_bootloader_ngdump(struct job_data *job,
return -1;
assert(!job->target.bootmap_dir);
job->target.bootmap_dir = misc_make_path("/tmp",
DUMP_TEMP_MOUNT_POINT_NAME);
job->target.bootmap_dir = build_mount_point_pathname();
if (!job->target.bootmap_dir) {
error_reason(strerror(errno));
error_text("Could not make path for '%s'",
@@ -1695,25 +1765,20 @@ static int prepare_bootloader_ngdump(struct job_data *job,
return -1;
}
job->bootmap_dir_created = 1;
/*
* Mount partition where bootmap file and also a dump file will
* be stored.
*/
if (mount(job->data.dump.device, job->target.bootmap_dir,
NGDUMP_FSTYPE, 0, NULL)) {
error_reason(strerror(errno));
error_text("Could not mount partition '%s':",
job->data.dump.device);
if (ngdump_mount_device(job))
return -1;
}
job->dump_mounted = 1;
/*
* Build a single program table for List-Directed IPL
* See comments before install_bootloader() for details
*/
bis->print_details = 1;
if (bootmap_create_file(job, bis, BLKPTR_FORMAT_ID))
if (bootmap_create_file(job, bis, BLKPTR_FORMAT_ID)) {
if (dry_run && is_error(FS_MAP_ERROR))
fprintf(stderr,
"'%s' doesn't satisfy the requirements. Set TMPDIR properly\n",
ngdump_dir());
return -1;
}
return ngdump_create_meta(job->target.bootmap_dir);
}
+29 -18
View File
@@ -811,20 +811,15 @@ int disk_type_is_eckd(disk_type_t type)
type == disk_type_eckd_cdl);
}
int
disk_get_info_from_file(const char* filename, struct job_target_data* target,
struct disk_info** info)
/**
* Retrieve and set block size of the file system which contains FILENAME
*/
int disk_info_set_fs_block(const char *filename, struct disk_info *info)
{
struct stat stats;
char* device;
int blocksize;
int fd;
int rc;
if (stat(filename, &stats)) {
error_reason(strerror(errno));
return -1;
}
/* Retrieve file system block size */
fd = open(filename, O_RDONLY);
if (fd == -1) {
@@ -838,17 +833,33 @@ disk_get_info_from_file(const char* filename, struct job_target_data* target,
filename);
return -1;
}
/* Create temporary device file */
rc = misc_temp_dev(stats.st_dev, 1, &device);
if (rc)
info->fs_block_size = blocksize;
return 0;
}
/**
* Retrieve disk info of the device which contains FILENAME
* and set the filesystem block size
*/
int disk_get_info_from_file(const char *filename,
struct job_target_data *target,
struct disk_info **info)
{
struct stat stats;
char *device;
if (stat(filename, &stats)) {
error_reason(strerror(errno));
return -1;
/* Get device info */
rc = disk_get_info(device, target, info);
if (rc == 0)
(*info)->fs_block_size = blocksize;
/* Clean up */
}
if (misc_temp_dev(stats.st_dev, 1, &device))
return -1;
if (disk_get_info(device, target, info)) {
misc_free_temp_dev(device);
return -1;
}
misc_free_temp_dev(device);
return rc;
return disk_info_set_fs_block(filename, *info);
}
void disk_free_info(struct disk_info *info)
+5
View File
@@ -82,3 +82,8 @@ error_print(void)
else
fprintf(stderr, "Error: An unspecified error occurred\n");
}
int is_error(const char *this)
{
return strcmp(error_reason_string, this) == 0;
}
+1 -1
View File
@@ -96,7 +96,7 @@ int fs_map(int fd, uint64_t offset, blocknum_t *mapped,
/* FIEMAP failed, fall back to FIBMAP */
block = offset / fs_block_size;
if (ioctl(fd, FIBMAP, &block)) {
error_reason("Could not get file mapping");
error_reason(FS_MAP_ERROR);
free(fiemap);
return -1;
}
+3 -2
View File
@@ -1119,8 +1119,9 @@ install_dump(const char *device, struct job_target_data *target, uint64_t mem,
stage2dump_parms.mem_upper_limit = mem;
stage2dump_parms.no_compress = no_compress;
mfd.fd = misc_open_exclusive(device);
if (mfd.fd == -1) {
/* Check if @device is a tape device */
if (misc_open_device(device, &mfd, 0) == -1) {
error_text("Could not open dump device '%s'", device);
return -1;
}
+17 -10
View File
@@ -459,17 +459,24 @@ free_mvdump_data(struct job_mvdump_data* data)
free(data->device[i]);
}
void
job_free(struct job_data* job)
void job_free(struct job_data *job)
{
if (job->dump_mounted && umount(job->target.bootmap_dir))
fprintf(stderr, "Could not umount dump device at %s",
job->target.bootmap_dir);
if (job->bootmap_dir_created && rmdir(job->target.bootmap_dir))
fprintf(stderr, "Could not remove directory %s",
job->target.bootmap_dir);
if (job->dump_mounted && umount(job->target.bootmap_dir)) {
fprintf(stderr,
"Could not umount dump device at %s(%s)\n",
job->target.bootmap_dir,
strerror(errno));
}
if (job->bootmap_dir_created) {
/* remove the meta-file created in dry-run mode */
ngdump_delete_meta(job->target.bootmap_dir);
if (rmdir(job->target.bootmap_dir)) {
fprintf(stderr,
"Could not remove directory %s(%s)\n",
job->target.bootmap_dir,
strerror(errno));
}
}
free_target_data(&job->target);
free_envblk_data(&job->envblk);
free(job->name);
+3 -4
View File
@@ -88,8 +88,7 @@ misc_strdup(const char* s)
/* Open file exclusive */
int
misc_open_exclusive(const char* filename)
static int misc_open_exclusive(const char *filename)
{
int fd;
@@ -104,7 +103,7 @@ misc_open_exclusive(const char* filename)
/**
* Open file FILENAME for write simulation
*/
int misc_open_simulate(const char *filename, struct misc_fd *mfd)
static int misc_open_simulate(const char *filename, struct misc_fd *mfd)
{
/*
* In write simulation mode actual data are not written to the file,
@@ -301,7 +300,7 @@ misc_get_char(struct misc_file_buffer* file, off_t readahead)
char*
misc_make_path(char* dirname, char* filename)
misc_make_path(const char *dirname, char *filename)
{
char* result;
size_t len;