431 lines
9.7 KiB
Diff
431 lines
9.7 KiB
Diff
|
From 2554f389cd167ee28033b8885da3f92b798f7ed3 Mon Sep 17 00:00:00 2001
|
||
|
From: Peter Jones <pjones@redhat.com>
|
||
|
Date: Mon, 3 Feb 2020 13:26:08 -0500
|
||
|
Subject: [PATCH 80/86] Add efi_time_t and time conversion and formatting
|
||
|
utilities.
|
||
|
|
||
|
Signed-off-by: Peter Jones <pjones@redhat.com>
|
||
|
---
|
||
|
src/Makefile | 2 +-
|
||
|
src/include/efivar/efivar-time.h | 27 +++
|
||
|
src/include/efivar/efivar-types.h | 33 ++++
|
||
|
src/include/efivar/efivar.h | 2 +
|
||
|
src/libefivar.map.in | 12 ++
|
||
|
src/time.c | 272 ++++++++++++++++++++++++++++++
|
||
|
6 files changed, 347 insertions(+), 1 deletion(-)
|
||
|
create mode 100644 src/include/efivar/efivar-time.h
|
||
|
create mode 100644 src/time.c
|
||
|
|
||
|
diff --git a/src/Makefile b/src/Makefile
|
||
|
index 0783cb3b55f..b0ef8ec29a5 100644
|
||
|
--- a/src/Makefile
|
||
|
+++ b/src/Makefile
|
||
|
@@ -18,7 +18,7 @@ LIBEFIBOOT_SOURCES = crc32.c creator.c disk.c gpt.c loadopt.c path-helpers.c \
|
||
|
LIBEFIBOOT_OBJECTS = $(patsubst %.c,%.o,$(LIBEFIBOOT_SOURCES))
|
||
|
LIBEFIVAR_SOURCES = crc32.c dp.c dp-acpi.c dp-hw.c dp-media.c dp-message.c \
|
||
|
efivarfs.c error.c export.c guid.c guids.S guid-symbols.c \
|
||
|
- lib.c vars.c
|
||
|
+ lib.c vars.c time.c
|
||
|
LIBEFIVAR_OBJECTS = $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(LIBEFIVAR_SOURCES)))
|
||
|
EFIVAR_SOURCES = efivar.c
|
||
|
GENERATED_SOURCES = include/efivar/efivar-guids.h guid-symbols.c
|
||
|
diff --git a/src/include/efivar/efivar-time.h b/src/include/efivar/efivar-time.h
|
||
|
new file mode 100644
|
||
|
index 00000000000..04c243601f5
|
||
|
--- /dev/null
|
||
|
+++ b/src/include/efivar/efivar-time.h
|
||
|
@@ -0,0 +1,27 @@
|
||
|
+// SPDX-License-Identifier: LGPL-2.1
|
||
|
+/*
|
||
|
+ * efivar-time.h
|
||
|
+ * Copyright 2020 Peter Jones <pjones@redhat.com>
|
||
|
+ */
|
||
|
+
|
||
|
+#ifndef EFIVAR_TIME_H_
|
||
|
+#define EFIVAR_TIME_H_
|
||
|
+
|
||
|
+#include <stdbool.h>
|
||
|
+
|
||
|
+extern int tm_to_efi_time(const struct tm * const s, efi_time_t *d, bool tzadj);
|
||
|
+extern int efi_time_to_tm(const efi_time_t * const s, struct tm *d);
|
||
|
+
|
||
|
+extern char *efi_asctime(const efi_time_t * const time);
|
||
|
+extern char *efi_asctime_r(const efi_time_t * const time, char *buf);
|
||
|
+extern efi_time_t *efi_gmtime(const time_t *time);
|
||
|
+extern efi_time_t *efi_gmtime_r(const time_t *time, efi_time_t *result);
|
||
|
+extern efi_time_t *efi_localtime(const time_t *time);
|
||
|
+extern efi_time_t *efi_localtime_r(const time_t *time, efi_time_t *result);
|
||
|
+extern time_t efi_mktime(const efi_time_t * const time);
|
||
|
+
|
||
|
+extern char *efi_strptime(const char *s, const char *format, efi_time_t *time);
|
||
|
+extern size_t efi_strftime(char *s, size_t max, const char *format, const efi_time_t *time);
|
||
|
+
|
||
|
+#endif /* !EFIVAR_TIME_H_ */
|
||
|
+// vim:fenc=utf-8:tw=75:noet
|
||
|
diff --git a/src/include/efivar/efivar-types.h b/src/include/efivar/efivar-types.h
|
||
|
index 6fca8a495f4..ce22b6c12b3 100644
|
||
|
--- a/src/include/efivar/efivar-types.h
|
||
|
+++ b/src/include/efivar/efivar-types.h
|
||
|
@@ -51,6 +51,39 @@ typedef uint16_t efi_char16_t;
|
||
|
typedef unsigned long uintn_t;
|
||
|
typedef long intn_t;
|
||
|
|
||
|
+#define EFIVAR_HAVE_EFI_TIME_T 1
|
||
|
+
|
||
|
+/*
|
||
|
+ * This can never be correct in, as defined, in the face of leap seconds.
|
||
|
+ * Because seconds here are defined with a range of [0,59], we can't
|
||
|
+ * express leap seconds correctly there. Because TimeZone is specified in
|
||
|
+ * minutes West of UTC, rather than seconds (like struct tm), it can't be
|
||
|
+ * used to correct when we cross a leap second boundary condition. As a
|
||
|
+ * result, EFI_TIME can only express UT1, rather than UTC, and there's no
|
||
|
+ * way when converting to know wether the error has been taken into
|
||
|
+ * account, nor if it should be.
|
||
|
+ *
|
||
|
+ * As I write this, there is a 37 second error.
|
||
|
+ */
|
||
|
+typedef struct {
|
||
|
+ uint16_t year; // 1900 - 9999
|
||
|
+ uint8_t month; // 1 - 12
|
||
|
+ uint8_t day; // 1 - 31
|
||
|
+ uint8_t hour; // 0 - 23
|
||
|
+ uint8_t minute; // 0 - 59
|
||
|
+ uint8_t second; // 0 - 59 // ha ha only serious
|
||
|
+ uint8_t pad1; // 0
|
||
|
+ uint32_t nanosecond; // 0 - 999,999,999
|
||
|
+ int16_t timezone; // minutes from UTC or EFI_UNSPECIFIED_TIMEZONE
|
||
|
+ uint8_t daylight; // bitfield
|
||
|
+ uint8_t pad2; // 0
|
||
|
+} efi_time_t __attribute__((__aligned__(1)));
|
||
|
+
|
||
|
+#define EFI_TIME_ADJUST_DAYLIGHT ((uint8_t)0x01)
|
||
|
+#define EFI_TIME_IN_DAYLIGHT ((uint8_t)0x02)
|
||
|
+
|
||
|
+#define EFI_UNSPECIFIED_TIMEZONE ((uint16_t)0x07ff)
|
||
|
+
|
||
|
#define EFI_VARIABLE_NON_VOLATILE ((uint64_t)0x0000000000000001)
|
||
|
#define EFI_VARIABLE_BOOTSERVICE_ACCESS ((uint64_t)0x0000000000000002)
|
||
|
#define EFI_VARIABLE_RUNTIME_ACCESS ((uint64_t)0x0000000000000004)
|
||
|
diff --git a/src/include/efivar/efivar.h b/src/include/efivar/efivar.h
|
||
|
index 6b38ce8faf4..7518a3238c7 100644
|
||
|
--- a/src/include/efivar/efivar.h
|
||
|
+++ b/src/include/efivar/efivar.h
|
||
|
@@ -14,6 +14,7 @@
|
||
|
#include <stdio.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/types.h>
|
||
|
+#include <time.h>
|
||
|
#include <unistd.h>
|
||
|
#include <byteswap.h>
|
||
|
|
||
|
@@ -200,6 +201,7 @@ extern uint32_t efi_get_libefivar_version(void)
|
||
|
__attribute__((__visibility__("default")));
|
||
|
|
||
|
#include <efivar/efivar-dp.h>
|
||
|
+#include <efivar/efivar-time.h>
|
||
|
|
||
|
#endif /* EFIVAR_H */
|
||
|
|
||
|
diff --git a/src/libefivar.map.in b/src/libefivar.map.in
|
||
|
index f2505134c63..47d45456372 100644
|
||
|
--- a/src/libefivar.map.in
|
||
|
+++ b/src/libefivar.map.in
|
||
|
@@ -139,4 +139,16 @@ LIBEFIVAR_1.38 {
|
||
|
efi_guid_external_management;
|
||
|
efi_variable_alloc;
|
||
|
efi_variable_export_dmpstore;
|
||
|
+
|
||
|
+ tm_to_efi_time;
|
||
|
+ efi_time_to_tm;
|
||
|
+ efi_asctime;
|
||
|
+ efi_asctime_r;
|
||
|
+ efi_gmtime;
|
||
|
+ efi_gmtime_r;
|
||
|
+ efi_localtime;
|
||
|
+ efi_localtime_r;
|
||
|
+ efi_mktime;
|
||
|
+ efi_strptime;
|
||
|
+ efi_strftime;
|
||
|
} LIBEFIVAR_1.37;
|
||
|
diff --git a/src/time.c b/src/time.c
|
||
|
new file mode 100644
|
||
|
index 00000000000..f267fd193e6
|
||
|
--- /dev/null
|
||
|
+++ b/src/time.c
|
||
|
@@ -0,0 +1,272 @@
|
||
|
+// SPDX-License-Identifier: LGPL-2.1
|
||
|
+/*
|
||
|
+ * time.c - efi_time_t helper functions
|
||
|
+ * Copyright 2020 Peter Jones <pjones@redhat.com>
|
||
|
+ */
|
||
|
+
|
||
|
+#include "efivar.h"
|
||
|
+
|
||
|
+int
|
||
|
+efi_time_to_tm(const efi_time_t * const s, struct tm *d)
|
||
|
+{
|
||
|
+
|
||
|
+ if (!s || !d) {
|
||
|
+ errno = EINVAL;
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ d->tm_year = s->year - 1900;
|
||
|
+ d->tm_mon = s->month - 1;
|
||
|
+ d->tm_mday = s->day;
|
||
|
+ d->tm_hour = s->hour;
|
||
|
+ d->tm_min = s->minute;
|
||
|
+ /*
|
||
|
+ * Just ignore EFI's range problem here and pretend we're in UTC
|
||
|
+ * not UT1.
|
||
|
+ */
|
||
|
+ d->tm_sec = s->second;
|
||
|
+ d->tm_isdst = (s->daylight & EFI_TIME_IN_DAYLIGHT) ? 1 : 0;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+int
|
||
|
+tm_to_efi_time(const struct tm * const s, efi_time_t *d, bool tzadj)
|
||
|
+{
|
||
|
+ if (!s || !d) {
|
||
|
+ errno = EINVAL;
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ d->pad2 = 0;
|
||
|
+ d->daylight = s->tm_isdst ? EFI_TIME_IN_DAYLIGHT : 0;
|
||
|
+ d->timezone = 0;
|
||
|
+ d->nanosecond = 0;
|
||
|
+ d->pad1 = 0;
|
||
|
+ /*
|
||
|
+ * Just ignore EFI's range problem here and pretend we're in UTC
|
||
|
+ * not UT1.
|
||
|
+ */
|
||
|
+ d->second = s->tm_sec;
|
||
|
+ d->minute = s->tm_min;
|
||
|
+ d->hour = s->tm_hour;
|
||
|
+ d->day = s->tm_mday;
|
||
|
+ d->month = s->tm_mon + 1;
|
||
|
+ d->year = s->tm_year + 1900;
|
||
|
+
|
||
|
+ if (tzadj) {
|
||
|
+ tzset();
|
||
|
+ d->timezone = timezone / 60;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static char *otz_;
|
||
|
+static char *ntz_;
|
||
|
+
|
||
|
+static const char *
|
||
|
+newtz(int16_t timezone_)
|
||
|
+{
|
||
|
+ if (!otz_)
|
||
|
+ otz_ = strdup(secure_getenv("TZ"));
|
||
|
+
|
||
|
+ if (ntz_) {
|
||
|
+ free(ntz_);
|
||
|
+ ntz_ = NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (timezone_ == EFI_UNSPECIFIED_TIMEZONE) {
|
||
|
+ unsetenv("TZ");
|
||
|
+ } else {
|
||
|
+ char tzsign = timezone_ >= 0 ? '+' : '-';
|
||
|
+ int tzabs = tzsign == '+' ? timezone_ : -timezone_;
|
||
|
+ int16_t tzhours = tzabs / 60;
|
||
|
+ int16_t tzminutes = tzabs % 60;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * I have no idea what the right thing to do with DST is
|
||
|
+ * here, so I'm going to ignore it.
|
||
|
+ */
|
||
|
+ asprintf(&ntz_, "UTC%c%"PRId16":%"PRId16":00",
|
||
|
+ tzsign, tzhours, tzminutes);
|
||
|
+ setenv("TZ", ntz_, 1);
|
||
|
+ }
|
||
|
+ tzset();
|
||
|
+
|
||
|
+ return ntz_;
|
||
|
+}
|
||
|
+
|
||
|
+static const char *
|
||
|
+oldtz(void) {
|
||
|
+ if (ntz_) {
|
||
|
+ free(ntz_);
|
||
|
+ ntz_ = NULL;
|
||
|
+
|
||
|
+ if (otz_)
|
||
|
+ setenv("TZ", otz_, 1);
|
||
|
+ else
|
||
|
+ unsetenv("TZ");
|
||
|
+ }
|
||
|
+
|
||
|
+ tzset();
|
||
|
+
|
||
|
+ return otz_;
|
||
|
+}
|
||
|
+
|
||
|
+efi_time_t *
|
||
|
+efi_gmtime_r(const time_t *time, efi_time_t *result)
|
||
|
+{
|
||
|
+ struct tm tm = { 0 };
|
||
|
+
|
||
|
+ if (!time || !result) {
|
||
|
+ errno = EINVAL;
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ gmtime_r(time, &tm);
|
||
|
+ tm_to_efi_time(&tm, result, false);
|
||
|
+
|
||
|
+ return result;
|
||
|
+}
|
||
|
+
|
||
|
+efi_time_t *
|
||
|
+efi_gmtime(const time_t *time)
|
||
|
+{
|
||
|
+ static efi_time_t ret;
|
||
|
+
|
||
|
+ if (!time) {
|
||
|
+ errno = EINVAL;
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ efi_gmtime_r(time, &ret);
|
||
|
+
|
||
|
+ return &ret;
|
||
|
+}
|
||
|
+
|
||
|
+efi_time_t *
|
||
|
+efi_localtime_r(const time_t *time, efi_time_t *result)
|
||
|
+{
|
||
|
+ struct tm tm = { 0 };
|
||
|
+
|
||
|
+ if (!time || !result) {
|
||
|
+ errno = EINVAL;
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ localtime_r(time, &tm);
|
||
|
+ tm_to_efi_time(&tm, result, true);
|
||
|
+
|
||
|
+ return result;
|
||
|
+}
|
||
|
+
|
||
|
+efi_time_t *
|
||
|
+efi_localtime(const time_t *time)
|
||
|
+{
|
||
|
+ static efi_time_t ret;
|
||
|
+
|
||
|
+ if (!time) {
|
||
|
+ errno = EINVAL;
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ efi_localtime_r(time, &ret);
|
||
|
+
|
||
|
+ return &ret;
|
||
|
+}
|
||
|
+
|
||
|
+time_t
|
||
|
+efi_mktime(const efi_time_t * const time)
|
||
|
+{
|
||
|
+ struct tm tm = { 0 };
|
||
|
+ time_t ret;
|
||
|
+
|
||
|
+ if (!time) {
|
||
|
+ errno = EINVAL;
|
||
|
+ return (time_t)-1;
|
||
|
+ }
|
||
|
+
|
||
|
+ newtz(time->timezone);
|
||
|
+
|
||
|
+ efi_time_to_tm(time, &tm);
|
||
|
+ ret = mktime(&tm);
|
||
|
+
|
||
|
+ oldtz();
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+char *
|
||
|
+efi_strptime(const char *s, const char *format, efi_time_t *time)
|
||
|
+{
|
||
|
+ struct tm tm;
|
||
|
+ char *end;
|
||
|
+
|
||
|
+ if (!s || !format || !time) {
|
||
|
+ errno = EINVAL;
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ memset(&tm, 0, sizeof(tm));
|
||
|
+ end = strptime(s, format, &tm);
|
||
|
+ if (end != NULL && tm_to_efi_time(&tm, time, true) < 0)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ return end;
|
||
|
+}
|
||
|
+
|
||
|
+char *
|
||
|
+efi_asctime_r(const efi_time_t * const time, char *buf)
|
||
|
+{
|
||
|
+ struct tm tm;
|
||
|
+ char *ret;
|
||
|
+
|
||
|
+ newtz(time->timezone);
|
||
|
+
|
||
|
+ efi_time_to_tm(time, &tm);
|
||
|
+ ret = asctime_r(&tm, buf);
|
||
|
+
|
||
|
+ oldtz();
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+char *
|
||
|
+efi_asctime(const efi_time_t * const time)
|
||
|
+{
|
||
|
+ struct tm tm;
|
||
|
+ char *ret;
|
||
|
+
|
||
|
+ newtz(time->timezone);
|
||
|
+
|
||
|
+ efi_time_to_tm(time, &tm);
|
||
|
+ ret = asctime(&tm);
|
||
|
+
|
||
|
+ oldtz();
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+size_t
|
||
|
+efi_strftime(char *s, size_t max, const char *format, const efi_time_t *time)
|
||
|
+{
|
||
|
+ size_t ret = 0;
|
||
|
+ struct tm tm = { 0 };
|
||
|
+
|
||
|
+ if (!s || !format || !time) {
|
||
|
+ errno = EINVAL;
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ newtz(time->timezone);
|
||
|
+
|
||
|
+ efi_time_to_tm(time, &tm);
|
||
|
+ ret = strftime(s, max, format, &tm);
|
||
|
+
|
||
|
+ oldtz();
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+// vim:fenc=utf-8:tw=75:noet
|
||
|
--
|
||
|
2.24.1
|
||
|
|