repart: smaller iso9660 tweaks (#41477)
This commit is contained in:
@@ -1354,8 +1354,6 @@ Features:
|
||||
* automatic boot assessment: add one more default success check that just waits
|
||||
for a bit after boot, and blesses the boot if the system stayed up that long.
|
||||
|
||||
* systemd-repart: add support for generating ISO9660 images
|
||||
|
||||
* systemd-repart: in addition to the existing "factory reset" mode (which
|
||||
simply empties existing partitions marked for that). add a mode where
|
||||
partitions marked for it are entirely removed. Use case: remove secondary OS
|
||||
|
||||
+54
-16
@@ -8,7 +8,7 @@
|
||||
#include "string-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
void no_iso9660_datetime(struct iso9660_datetime *ret) {
|
||||
void iso9660_datetime_zero(struct iso9660_datetime *ret) {
|
||||
assert(ret);
|
||||
|
||||
memcpy(ret->year, "0000", 4);
|
||||
@@ -21,7 +21,27 @@ void no_iso9660_datetime(struct iso9660_datetime *ret) {
|
||||
ret->zone = 0;
|
||||
}
|
||||
|
||||
int time_to_iso9660_datetime(usec_t usec, bool utc, struct iso9660_datetime *ret) {
|
||||
static int validate_tm(const struct tm *t) {
|
||||
assert(t);
|
||||
|
||||
/* Safety checks on bounded fields of struct tm, ranges as per tm(3type). Mostly in place because
|
||||
* ISO9660 date/time ranges and struct tm ranges differ. */
|
||||
|
||||
if (t->tm_mon < 0 || t->tm_mon > 11)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Month out of range.");
|
||||
if (t->tm_mday < 1 || t->tm_mday > 31)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Day of month out of range.");
|
||||
if (t->tm_hour < 0 || t->tm_hour > 23)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Hour out of range.");
|
||||
if (t->tm_min < 0 || t->tm_min > 59)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Minute out of range.");
|
||||
if (t->tm_sec < 0 || t->tm_sec > 60)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Seconds out of range.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iso9660_datetime_from_usec(usec_t usec, bool utc, struct iso9660_datetime *ret) {
|
||||
struct tm t;
|
||||
int r;
|
||||
|
||||
@@ -31,25 +51,33 @@ int time_to_iso9660_datetime(usec_t usec, bool utc, struct iso9660_datetime *ret
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = validate_tm(&t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (t.tm_year >= 10000 - 1900)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Year has more than 4 digits and is incompatible with ISO9660.");
|
||||
if (t.tm_year + 1900 < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Year is negative and is incompatible with ISO9660.");
|
||||
|
||||
long offset = t.tm_gmtoff / (15*60); /* The time zone is encoded by 15 minutes increments */
|
||||
if (offset < INT8_MIN || offset > INT8_MAX)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "GMT offset out of range.");
|
||||
|
||||
char buf[17];
|
||||
assert_cc(sizeof(buf)-1 == offsetof(struct iso9660_datetime, zone));
|
||||
/* Ignore leap seconds, no real hope for hardware. Deci-seconds always zero. */
|
||||
xsprintf(buf, "%04d%02d%02d%02d%02d%02d00",
|
||||
t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
|
||||
t.tm_hour, t.tm_min, MIN(t.tm_sec, 59));
|
||||
memcpy(ret, buf, sizeof(buf)-1);
|
||||
|
||||
/* The time zone is encoded by 15 minutes increments */
|
||||
ret->zone = t.tm_gmtoff / (15*60);
|
||||
ret->zone = offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int time_to_iso9660_dir_datetime(usec_t usec, bool utc, struct iso9660_dir_time *ret) {
|
||||
int iso9660_dir_datetime_from_usec(usec_t usec, bool utc, struct iso9660_dir_time *ret) {
|
||||
struct tm t;
|
||||
int r;
|
||||
|
||||
@@ -59,8 +87,16 @@ int time_to_iso9660_dir_datetime(usec_t usec, bool utc, struct iso9660_dir_time
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = validate_tm(&t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (t.tm_year < 0 || t.tm_year > UINT8_MAX)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Year is incompatible with ISO9660.");
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Year is incompatible with ISO9660.");
|
||||
|
||||
long offset = t.tm_gmtoff / (15*60); /* The time zone is encoded by 15 minutes increments */
|
||||
if (offset < INT8_MIN || offset > INT8_MAX)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "GMT offset out of range.");
|
||||
|
||||
*ret = (struct iso9660_dir_time) {
|
||||
.year = t.tm_year,
|
||||
@@ -70,29 +106,31 @@ int time_to_iso9660_dir_datetime(usec_t usec, bool utc, struct iso9660_dir_time
|
||||
.minute = t.tm_min,
|
||||
.second = MIN(t.tm_sec, 59),
|
||||
/* The time zone is encoded by 15 minutes increments */
|
||||
.offset = t.tm_gmtoff / (15*60),
|
||||
.offset = offset,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool valid_iso9660_string(const char *str, bool allow_a_chars) {
|
||||
static bool iso9660_valid_string(const char *str, bool allow_a_chars) {
|
||||
/* note that a-chars are not supposed to accept lower case letters, but it looks like common practice
|
||||
* to use them
|
||||
*/
|
||||
return in_charset(str, allow_a_chars ? UPPERCASE_LETTERS LOWERCASE_LETTERS DIGITS " _!\"%&'()*+,-./:;<=>?" : UPPERCASE_LETTERS DIGITS "_");
|
||||
}
|
||||
|
||||
int set_iso9660_string(char target[], size_t len, const char *source, bool allow_a_chars) {
|
||||
if (source && !valid_iso9660_string(source, allow_a_chars))
|
||||
return -EINVAL;
|
||||
int iso9660_set_string(char target[], size_t len, const char *source, bool allow_a_chars) {
|
||||
assert(target || len == 0);
|
||||
|
||||
if (source) {
|
||||
if (!iso9660_valid_string(source, allow_a_chars))
|
||||
return -EINVAL;
|
||||
|
||||
size_t slen = strlen(source);
|
||||
if (slen > len)
|
||||
return -EINVAL;
|
||||
void *p = mempcpy(target, source, slen);
|
||||
memset(p, ' ', len - slen);
|
||||
|
||||
memset(mempcpy(target, source, slen), ' ', len - slen);
|
||||
} else
|
||||
memset(target, ' ', len);
|
||||
|
||||
@@ -101,16 +139,16 @@ int set_iso9660_string(char target[], size_t len, const char *source, bool allow
|
||||
|
||||
bool iso9660_volume_name_valid(const char *name) {
|
||||
/* In theory the volume identifier should be d-chars, but in practice, a-chars are allowed */
|
||||
return valid_iso9660_string(name, /* allow_a_chars= */ true) &&
|
||||
return iso9660_valid_string(name, /* allow_a_chars= */ true) &&
|
||||
strlen(name) <= 32;
|
||||
}
|
||||
|
||||
bool iso9660_system_name_valid(const char *name) {
|
||||
return valid_iso9660_string(name, /* allow_a_chars= */ true) &&
|
||||
return iso9660_valid_string(name, /* allow_a_chars= */ true) &&
|
||||
strlen(name) <= 32;
|
||||
}
|
||||
|
||||
bool iso9660_publisher_name_valid(const char *name) {
|
||||
return valid_iso9660_string(name, /* allow_a_chars= */ true) &&
|
||||
return iso9660_valid_string(name, /* allow_a_chars= */ true) &&
|
||||
strlen(name) <= 128;
|
||||
}
|
||||
|
||||
@@ -127,14 +127,14 @@ struct _packed_ iso9660_primary_volume_descriptor {
|
||||
uint8_t file_structure_version; /* 1 */
|
||||
uint8_t unused_5;
|
||||
char application_used[512];
|
||||
char reserved[653];
|
||||
uint8_t reserved[653];
|
||||
};
|
||||
assert_cc(sizeof(struct iso9660_primary_volume_descriptor) == 2048);
|
||||
|
||||
struct _packed_ el_torito_validation_entry {
|
||||
uint8_t header_indicator;
|
||||
uint8_t platform;
|
||||
char reserved[2];
|
||||
uint8_t reserved[2];
|
||||
char id_string[24];
|
||||
le16_t checksum;
|
||||
uint8_t key_bytes[2];
|
||||
@@ -158,13 +158,13 @@ struct _packed_ el_torito_section_header {
|
||||
char id_string[28];
|
||||
};
|
||||
|
||||
void no_iso9660_datetime(struct iso9660_datetime *ret);
|
||||
int time_to_iso9660_datetime(usec_t usec, bool utc, struct iso9660_datetime *ret);
|
||||
int time_to_iso9660_dir_datetime(usec_t usec, bool utc, struct iso9660_dir_time *ret);
|
||||
int set_iso9660_string(char target[], size_t len, const char *source, bool allow_a_chars);
|
||||
void iso9660_datetime_zero(struct iso9660_datetime *ret);
|
||||
int iso9660_datetime_from_usec(usec_t usec, bool utc, struct iso9660_datetime *ret);
|
||||
int iso9660_dir_datetime_from_usec(usec_t usec, bool utc, struct iso9660_dir_time *ret);
|
||||
int iso9660_set_string(char target[], size_t len, const char *source, bool allow_a_chars);
|
||||
|
||||
static inline void set_iso9660_const_string(char target[], size_t len, const char *source, bool allow_a_chars) {
|
||||
assert_se(set_iso9660_string(target, len, source, allow_a_chars) == 0);
|
||||
static inline void iso9660_set_const_string(char target[], size_t len, const char *source, bool allow_a_chars) {
|
||||
assert_se(iso9660_set_string(target, len, source, allow_a_chars) == 0);
|
||||
}
|
||||
|
||||
bool iso9660_volume_name_valid(const char *name);
|
||||
|
||||
+47
-21
@@ -7741,6 +7741,8 @@ static int write_primary_descriptor(
|
||||
const char *publisher_id) {
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
struct iso9660_primary_volume_descriptor desc = {
|
||||
.header = {
|
||||
.type = 1,
|
||||
@@ -7769,43 +7771,43 @@ static int write_primary_descriptor(
|
||||
}
|
||||
};
|
||||
|
||||
set_iso9660_const_string(desc.header.identifier, sizeof(desc.header.identifier), "CD001", /* allow_a_chars= */ true);
|
||||
iso9660_set_const_string(desc.header.identifier, sizeof(desc.header.identifier), "CD001", /* allow_a_chars= */ true);
|
||||
|
||||
r = time_to_iso9660_dir_datetime(usec, utc, &desc.root_directory_entry.time);
|
||||
r = iso9660_dir_datetime_from_usec(usec, utc, &desc.root_directory_entry.time);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = set_iso9660_string(desc.system_identifier, sizeof(desc.system_identifier), system_id, /* allow_a_chars= */ true);
|
||||
r = iso9660_set_string(desc.system_identifier, sizeof(desc.system_identifier), system_id, /* allow_a_chars= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* In theory the volume identifier should be d-chars, but in practice, a-chars are allowed */
|
||||
r = set_iso9660_string(desc.volume_identifier, sizeof(desc.volume_identifier), volume_id, /* allow_a_chars= */ true);
|
||||
r = iso9660_set_string(desc.volume_identifier, sizeof(desc.volume_identifier), volume_id, /* allow_a_chars= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
set_iso9660_const_string(desc.volume_set_identifier, sizeof(desc.volume_set_identifier), NULL, /* allow_a_chars= */ false);
|
||||
iso9660_set_const_string(desc.volume_set_identifier, sizeof(desc.volume_set_identifier), NULL, /* allow_a_chars= */ false);
|
||||
|
||||
r = set_iso9660_string(desc.publisher_identifier, sizeof(desc.publisher_identifier), publisher_id, /* allow_a_chars= */ true);
|
||||
r = iso9660_set_string(desc.publisher_identifier, sizeof(desc.publisher_identifier), publisher_id, /* allow_a_chars= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
set_iso9660_const_string(desc.data_preparer_identifier, sizeof(desc.data_preparer_identifier), NULL, /* allow_a_chars= */ true);
|
||||
set_iso9660_const_string(desc.application_identifier, sizeof(desc.application_identifier), "SYSTEMD-REPART", /* allow_a_chars= */ true);
|
||||
set_iso9660_const_string(desc.copyright_file_identifier, sizeof(desc.copyright_file_identifier), NULL, /* allow_a_chars= */ false);
|
||||
set_iso9660_const_string(desc.abstract_file_identifier, sizeof(desc.abstract_file_identifier), NULL, /* allow_a_chars= */ false);
|
||||
set_iso9660_const_string(desc.bibliographic_file_identifier, sizeof(desc.bibliographic_file_identifier), NULL, /* allow_a_chars= */ false);
|
||||
iso9660_set_const_string(desc.data_preparer_identifier, sizeof(desc.data_preparer_identifier), NULL, /* allow_a_chars= */ true);
|
||||
iso9660_set_const_string(desc.application_identifier, sizeof(desc.application_identifier), "SYSTEMD-REPART", /* allow_a_chars= */ true);
|
||||
iso9660_set_const_string(desc.copyright_file_identifier, sizeof(desc.copyright_file_identifier), NULL, /* allow_a_chars= */ false);
|
||||
iso9660_set_const_string(desc.abstract_file_identifier, sizeof(desc.abstract_file_identifier), NULL, /* allow_a_chars= */ false);
|
||||
iso9660_set_const_string(desc.bibliographic_file_identifier, sizeof(desc.bibliographic_file_identifier), NULL, /* allow_a_chars= */ false);
|
||||
|
||||
r = time_to_iso9660_datetime(usec, utc, &desc.volume_creation_date);
|
||||
r = iso9660_datetime_from_usec(usec, utc, &desc.volume_creation_date);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = time_to_iso9660_datetime(usec, utc, &desc.volume_modification_date);
|
||||
r = iso9660_datetime_from_usec(usec, utc, &desc.volume_modification_date);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
no_iso9660_datetime(&desc.volume_expiration_date);
|
||||
no_iso9660_datetime(&desc.volume_effective_date);
|
||||
iso9660_datetime_zero(&desc.volume_expiration_date);
|
||||
iso9660_datetime_zero(&desc.volume_effective_date);
|
||||
|
||||
ssize_t s = pwrite(fd, &desc, sizeof(desc), ISO9660_PRIMARY_DESCRIPTOR*ISO9660_BLOCK_SIZE);
|
||||
if (s < 0)
|
||||
@@ -7817,6 +7819,8 @@ static int write_primary_descriptor(
|
||||
}
|
||||
|
||||
static int write_eltorito_descriptor(int fd, uint32_t catalog_sector) {
|
||||
assert(fd >= 0);
|
||||
|
||||
struct iso9660_eltorito_descriptor desc = {
|
||||
.header = {
|
||||
.type = 0,
|
||||
@@ -7825,7 +7829,7 @@ static int write_eltorito_descriptor(int fd, uint32_t catalog_sector) {
|
||||
.boot_catalog_sector = htole32(catalog_sector),
|
||||
};
|
||||
|
||||
set_iso9660_const_string(desc.header.identifier, sizeof(desc.header.identifier), "CD001", /* allow_a_chars= */ true);
|
||||
iso9660_set_const_string(desc.header.identifier, sizeof(desc.header.identifier), "CD001", /* allow_a_chars= */ true);
|
||||
|
||||
strncpy(desc.boot_system_identifier, "EL TORITO SPECIFICATION", sizeof(desc.boot_system_identifier));
|
||||
|
||||
@@ -7839,6 +7843,8 @@ static int write_eltorito_descriptor(int fd, uint32_t catalog_sector) {
|
||||
}
|
||||
|
||||
static int write_terminal_descriptor(int fd) {
|
||||
assert(fd >= 0);
|
||||
|
||||
struct iso9660_terminal_descriptor desc = {
|
||||
.header = {
|
||||
.type = 255,
|
||||
@@ -7846,7 +7852,7 @@ static int write_terminal_descriptor(int fd) {
|
||||
},
|
||||
};
|
||||
|
||||
set_iso9660_const_string(desc.header.identifier, sizeof(desc.header.identifier), "CD001", /* allow_a_chars= */ true);
|
||||
iso9660_set_const_string(desc.header.identifier, sizeof(desc.header.identifier), "CD001", /* allow_a_chars= */ true);
|
||||
|
||||
ssize_t s = pwrite(fd, &desc, sizeof(desc), ISO9660_TERMINAL_DESCRIPTOR*ISO9660_BLOCK_SIZE);
|
||||
if (s < 0)
|
||||
@@ -7858,6 +7864,7 @@ static int write_terminal_descriptor(int fd) {
|
||||
}
|
||||
|
||||
static uint16_t calculate_validation_entry_checksum(const void *p, size_t size) {
|
||||
assert(p || size == 0);
|
||||
assert(size % 2 == 0);
|
||||
|
||||
uint16_t checksum = 0;
|
||||
@@ -7869,6 +7876,8 @@ static uint16_t calculate_validation_entry_checksum(const void *p, size_t size)
|
||||
}
|
||||
|
||||
static int write_boot_catalog(int fd, uint32_t load_block) {
|
||||
assert(fd >= 0);
|
||||
|
||||
struct el_torito_validation_entry ve = {
|
||||
.header_indicator = 1,
|
||||
.platform = 0xef, /* EFI */
|
||||
@@ -7911,9 +7920,16 @@ static int write_boot_catalog(int fd, uint32_t load_block) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_directories(int fd, usec_t usec, bool utc, uint32_t root_sector) {
|
||||
static int write_directories(
|
||||
int fd,
|
||||
usec_t usec,
|
||||
bool utc,
|
||||
uint32_t root_sector) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
uint32_t dir_size = 2*sizeof(struct iso9660_directory_entry); /* 2 entries with ident size 1: . and .. */
|
||||
|
||||
struct iso9660_directory_entry self = {
|
||||
@@ -7929,7 +7945,7 @@ static int write_directories(int fd, usec_t usec, bool utc, uint32_t root_sector
|
||||
.ident[0] = 0, /* special value for self */
|
||||
};
|
||||
|
||||
r = time_to_iso9660_dir_datetime(usec, utc, &self.time);
|
||||
r = iso9660_dir_datetime_from_usec(usec, utc, &self.time);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -7948,7 +7964,7 @@ static int write_directories(int fd, usec_t usec, bool utc, uint32_t root_sector
|
||||
|
||||
// TODO: we should probably add some text file explaining there is no content through ISO9660
|
||||
|
||||
r = time_to_iso9660_dir_datetime(usec, utc, &parent.time);
|
||||
r = iso9660_dir_datetime_from_usec(usec, utc, &parent.time);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -7967,9 +7983,19 @@ static int write_directories(int fd, usec_t usec, bool utc, uint32_t root_sector
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_eltorito(int fd, usec_t usec, bool utc, uint32_t load_block, const char *system_id, const char *volume_id, const char *publisher_id) {
|
||||
static int write_eltorito(
|
||||
int fd,
|
||||
usec_t usec,
|
||||
bool utc,
|
||||
uint32_t load_block, /* in iso9660 blocks */
|
||||
const char *system_id,
|
||||
const char *volume_id,
|
||||
const char *publisher_id) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = write_primary_descriptor(fd, ISO9660_ROOT_DIRECTORY, usec, utc, system_id, volume_id, publisher_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
Reference in New Issue
Block a user