Thank you for doing this, it worked perfectly for me when I tested it
on x86-64 in a VM.
Testing performed:
1. Set various files on FAT32 and Ext4 file systems with various dates
in the future (2038, 2040, 2100, etc.) and confirmed correct date /
time display via GRUB 'ls' command.
2. Ran your provided updated unit test suite
3. Confirmed booting of a Linux OS in a VM with the updated GRUB worked normally
4. Ran the updated 'grub_datetime2unixtime' function for all years
[1900 to 2999] / months [0 to 13] / days [0 to 32] with combinations
of random in / out of bounds hours/minutes/seconds - confirmed
identical results to an alternate implementation. Also ran this with
various compiler sanitizers enabled - no issues seen.
5. Ran the updated 'grub_datetime2unixtime' function with maximum
(65535) and minimum (1) valid year / month / day / hour / min / second
- no issues seen.
6. I ran coverage on the updated 'grub_datetime2unixtime' and was able
to cover all statements except the overflow check at the end (not
saying this has to be taken out - maybe be a good sanity check):
if ((datetime->year > 1980 && ret < 0)
339003: 155: || (datetime->year < 1960 && ret > 0))
#####: 156: return 0;
Sincerely,
Andrew
On Sat, Aug 17, 2024 at 12:31 PM Vladimir Serbinenko <[email protected]> wrote:
>
> Signed-off-by: Vladimir Serbinenko <[email protected]>
> ---
> grub-core/lib/datetime.c | 31 ++++++++++++++++++++++++-------
> include/grub/datetime.h | 15 +++++++--------
> 2 files changed, 31 insertions(+), 15 deletions(-)
>
> diff --git a/grub-core/lib/datetime.c b/grub-core/lib/datetime.c
> index 9120128ca..e4bdc5c4f 100644
> --- a/grub-core/lib/datetime.c
> +++ b/grub-core/lib/datetime.c
> @@ -59,6 +59,8 @@ grub_get_weekday_name (struct grub_datetime *datetime)
> #define SECPERDAY (24*SECPERHOUR)
> #define DAYSPERYEAR 365
> #define DAYSPER4YEARS (4*DAYSPERYEAR+1)
> +#define DAYSPER100YEARS (100*DAYSPERYEAR+24)
> +#define DAYSPER400YEARS (400*DAYSPERYEAR+97)
>
>
> void
> @@ -71,7 +73,7 @@ grub_unixtime2datetime (grub_int64_t nix, struct
> grub_datetime *datetime)
> /* Convenience: let's have 3 consecutive non-bissextile years
> at the beginning of the counting date. So count from 1901. */
> int days_epoch;
> - /* Number of days since 1st Januar, 1901. */
> + /* Number of days since 1st January, 1 (proleptic). */
> unsigned days;
> /* Seconds into current day. */
> unsigned secs_in_day;
> @@ -87,9 +89,25 @@ grub_unixtime2datetime (grub_int64_t nix, struct
> grub_datetime *datetime)
> days_epoch = grub_divmod64 (nix, SECPERDAY, NULL);
>
> secs_in_day = nix - days_epoch * SECPERDAY;
> - days = days_epoch + 69 * DAYSPERYEAR + 17;
> + days = days_epoch + 369 * DAYSPERYEAR + 17 + 24 * 3 + 4 * DAYSPER400YEARS;
>
> - datetime->year = 1901 + 4 * (days / DAYSPER4YEARS);
> + datetime->year = 1 + 400 * (days / DAYSPER400YEARS);
> + days %= DAYSPER400YEARS;
> +
> + /* On 31st December of bissextile years 365 days from the beginning
> + of the year elapsed but year isn't finished yet */
> + if (days / DAYSPER100YEARS == 4)
> + {
> + datetime->year += 396;
> + days -= 396*DAYSPERYEAR + 96;
> + }
> + else
> + {
> + datetime->year += 100 * (days / DAYSPER100YEARS);
> + days %= DAYSPER100YEARS;
> + }
> +
> + datetime->year += 4 * (days / DAYSPER4YEARS);
> days %= DAYSPER4YEARS;
> /* On 31st December of bissextile years 365 days from the beginning
> of the year elapsed but year isn't finished yet */
> @@ -103,11 +121,10 @@ grub_unixtime2datetime (grub_int64_t nix, struct
> grub_datetime *datetime)
> datetime->year += days / DAYSPERYEAR;
> days %= DAYSPERYEAR;
> }
> + int isbisextile = datetime->year % 4 == 0 && (datetime->year % 100 != 0 ||
> datetime->year % 400 == 0);
> for (i = 0; i < 12
> - && days >= (i==1 && datetime->year % 4 == 0
> - ? 29 : months[i]); i++)
> - days -= (i==1 && datetime->year % 4 == 0
> - ? 29 : months[i]);
> + && days >= (i==1 && isbisextile ? 29 : months[i]); i++)
> + days -= (i==1 && isbisextile ? 29 : months[i]);
> datetime->month = i + 1;
> datetime->day = 1 + days;
> datetime->hour = (secs_in_day / SECPERHOUR);
> diff --git a/include/grub/datetime.h b/include/grub/datetime.h
> index bcec636f0..9289b0d00 100644
> --- a/include/grub/datetime.h
> +++ b/include/grub/datetime.h
> @@ -54,7 +54,7 @@ void grub_unixtime2datetime (grub_int64_t nix,
> static inline int
> grub_datetime2unixtime (const struct grub_datetime *datetime, grub_int64_t
> *nix)
> {
> - grub_int32_t ret;
> + grub_int64_t ret;
> int y4, ay;
> const grub_uint16_t monthssum[12]
> = { 0,
> @@ -75,15 +75,11 @@ grub_datetime2unixtime (const struct grub_datetime
> *datetime, grub_int64_t *nix)
> const int SECPERHOUR = 60 * SECPERMIN;
> const int SECPERDAY = 24 * SECPERHOUR;
> const int SECPERYEAR = 365 * SECPERDAY;
> - const int SECPER4YEARS = 4 * SECPERYEAR + SECPERDAY;
> + const grub_int64_t SECPER4YEARS = 4 * SECPERYEAR + SECPERDAY;
>
> - if (datetime->year > 2038 || datetime->year < 1901)
> - return 0;
> if (datetime->month > 12 || datetime->month < 1)
> return 0;
>
> - /* In the period of validity of unixtime all years divisible by 4
> - are bissextile*/
> /* Convenience: let's have 3 consecutive non-bissextile years
> at the beginning of the epoch. So count from 1973 instead of 1970 */
> ret = 3 * SECPERYEAR + SECPERDAY;
> @@ -94,13 +90,16 @@ grub_datetime2unixtime (const struct grub_datetime
> *datetime, grub_int64_t *nix)
> ret += y4 * SECPER4YEARS;
> ret += ay * SECPERYEAR;
>
> + ret -= ((datetime->year - 1) / 100 - (datetime->year - 1) / 400 - 15) *
> SECPERDAY;
> +
> ret += monthssum[datetime->month - 1] * SECPERDAY;
> - if (ay == 3 && datetime->month >= 3)
> + int isbisextile = ay == 3 && (datetime->year % 100 != 0 || datetime->year
> % 400 == 0);
> + if (isbisextile && datetime->month >= 3)
> ret += SECPERDAY;
>
> ret += (datetime->day - 1) * SECPERDAY;
> if ((datetime->day > months[datetime->month - 1]
> - && (!ay || datetime->month != 2 || datetime->day != 29))
> + && !(isbisextile && datetime->month == 2 && datetime->day == 29))
> || datetime->day < 1)
> return 0;
>
> --
> 2.39.2
>
>
> _______________________________________________
> Grub-devel mailing list
> [email protected]
> https://lists.gnu.org/mailman/listinfo/grub-devel
_______________________________________________
Grub-devel mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/grub-devel