DF-2956 / fattime_host.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | /* Host-side math harness for sys/kern/subr_fattime.c (verbatim function bodies, * types adapted: time_t = int64_t). Verifies: * A) read-path round-trip across the FULL valid FAT date range (find DAY*day wrap) * B) write-path with post-2107 / huge tv_sec (year-field + t2 truncation) * C) read-path month 0/13-15, hour>23, min>59 fields (garbage-but-bounded?) * D) day=0 (known DF-0199, confirm only) */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <time.h> typedef int64_t time_t2; #define DAY (24 * 60 * 60) #define YEAR 365 #define LYC (4 * YEAR + 1) #define T1980 (10 * 365 + 2) #define JAN 31 #define FEB (JAN + 28) #define MAR (FEB + 31) #define APR (MAR + 30) #define MAY (APR + 31) #define JUN (MAY + 30) #define JUL (JUN + 31) #define AUG (JUL + 31) #define SEP (AUG + 30) #define OCT (SEP + 31) #define NOV (OCT + 30) #define DEC (NOV + 31) #define ENC(y,m) (((y) << 9) | ((m) << 5)) static const struct { uint16_t days; uint16_t coded; } mtab[48] = { { 0 + 0 * YEAR, ENC(0, 1) }, { JAN + 0 * YEAR, ENC(0, 2) }, { FEB + 0 * YEAR + 1, ENC(0, 3) }, { MAR + 0 * YEAR + 1, ENC(0, 4) }, { APR + 0 * YEAR + 1, ENC(0, 5) }, { MAY + 0 * YEAR + 1, ENC(0, 6) }, { JUN + 0 * YEAR + 1, ENC(0, 7) }, { JUL + 0 * YEAR + 1, ENC(0, 8) }, { AUG + 0 * YEAR + 1, ENC(0, 9) }, { SEP + 0 * YEAR + 1, ENC(0, 10) }, { OCT + 0 * YEAR + 1, ENC(0, 11) }, { NOV + 0 * YEAR + 1, ENC(0, 12) }, { DEC + 0 * YEAR + 1, ENC(1, 1) }, { JAN + 1 * YEAR + 1, ENC(1, 2) }, { FEB + 1 * YEAR + 1, ENC(1, 3) }, { MAR + 1 * YEAR + 1, ENC(1, 4) }, { APR + 1 * YEAR + 1, ENC(1, 5) }, { MAY + 1 * YEAR + 1, ENC(1, 6) }, { JUN + 1 * YEAR + 1, ENC(1, 7) }, { JUL + 1 * YEAR + 1, ENC(1, 8) }, { AUG + 1 * YEAR + 1, ENC(1, 9) }, { SEP + 1 * YEAR + 1, ENC(1, 10) }, { OCT + 1 * YEAR + 1, ENC(1, 11) }, { NOV + 1 * YEAR + 1, ENC(1, 12) }, { DEC + 1 * YEAR + 1, ENC(2, 1) }, { JAN + 2 * YEAR + 1, ENC(2, 2) }, { FEB + 2 * YEAR + 1, ENC(2, 3) }, { MAR + 2 * YEAR + 1, ENC(2, 4) }, { APR + 2 * YEAR + 1, ENC(2, 5) }, { MAY + 2 * YEAR + 1, ENC(2, 6) }, { JUN + 2 * YEAR + 1, ENC(2, 7) }, { JUL + 2 * YEAR + 1, ENC(2, 8) }, { AUG + 2 * YEAR + 1, ENC(2, 9) }, { SEP + 2 * YEAR + 1, ENC(2, 10) }, { OCT + 2 * YEAR + 1, ENC(2, 11) }, { NOV + 2 * YEAR + 1, ENC(2, 12) }, { DEC + 2 * YEAR + 1, ENC(3, 1) }, { JAN + 3 * YEAR + 1, ENC(3, 2) }, { FEB + 3 * YEAR + 1, ENC(3, 3) }, { MAR + 3 * YEAR + 1, ENC(3, 4) }, { APR + 3 * YEAR + 1, ENC(3, 5) }, { MAY + 3 * YEAR + 1, ENC(3, 6) }, { JUN + 3 * YEAR + 1, ENC(3, 7) }, { JUL + 3 * YEAR + 1, ENC(3, 8) }, { AUG + 3 * YEAR + 1, ENC(3, 9) }, { SEP + 3 * YEAR + 1, ENC(3, 10) }, { OCT + 3 * YEAR + 1, ENC(3, 11) }, { NOV + 3 * YEAR + 1, ENC(3, 12) } }; #define DCOD(m, y, l) ((m) + YEAR * (y) + (l)) static const uint16_t daytab[64] = { 0, DCOD( 0, 0, 0), DCOD(JAN, 0, 0), DCOD(FEB, 0, 1), DCOD(MAR, 0, 1), DCOD(APR, 0, 1), DCOD(MAY, 0, 1), DCOD(JUN, 0, 1), DCOD(JUL, 0, 1), DCOD(AUG, 0, 1), DCOD(SEP, 0, 1), DCOD(OCT, 0, 1), DCOD(NOV, 0, 1), DCOD(DEC, 0, 1), 0, 0, 0, DCOD( 0, 1, 1), DCOD(JAN, 1, 1), DCOD(FEB, 1, 1), DCOD(MAR, 1, 1), DCOD(APR, 1, 1), DCOD(MAY, 1, 1), DCOD(JUN, 1, 1), DCOD(JUL, 1, 1), DCOD(AUG, 1, 1), DCOD(SEP, 1, 1), DCOD(OCT, 1, 1), DCOD(NOV, 1, 1), DCOD(DEC, 1, 1), 0, 0, 0, DCOD( 0, 2, 1), DCOD(JAN, 2, 1), DCOD(FEB, 2, 1), DCOD(MAR, 2, 1), DCOD(APR, 2, 1), DCOD(MAY, 2, 1), DCOD(JUN, 2, 1), DCOD(JUL, 2, 1), DCOD(AUG, 2, 1), DCOD(SEP, 2, 1), DCOD(OCT, 2, 1), DCOD(NOV, 2, 1), DCOD(DEC, 2, 1), 0, 0, 0, DCOD( 0, 3, 1), DCOD(JAN, 3, 1), DCOD(FEB, 3, 1), DCOD(MAR, 3, 1), DCOD(APR, 3, 1), DCOD(MAY, 3, 1), DCOD(JUN, 3, 1), DCOD(JUL, 3, 1), DCOD(AUG, 3, 1), DCOD(SEP, 3, 1), DCOD(OCT, 3, 1), DCOD(NOV, 3, 1), DCOD(DEC, 3, 1), 0, 0 }; static void timespec2fattime(const struct timespec *tsp, int utc, uint16_t *ddp, uint16_t *dtp, uint8_t *dhp) { time_t2 t1; unsigned t2, l, m; t1 = tsp->tv_sec; if (!utc) t1 -= 0; if (dhp != NULL) *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; if (dtp != NULL) { *dtp = (t1 / 2) % 30; *dtp |= ((t1 / 60) % 60) << 5; *dtp |= ((t1 / 3600) % 24) << 11; } if (ddp != NULL) { t2 = t1 / DAY; if (t2 < T1980) { *ddp = 0x0021; } else { t2 -= T1980; if (t2 >= ((2100 - 1980) / 4 * LYC + FEB)) t2++; l = t2 / LYC; *ddp = (l * 4) << 9; t2 -= l * LYC; m = t2 / 32; while (m < 47 && mtab[m + 1].days <= t2) m++; *ddp += mtab[m].coded; t2 -= mtab[m].days - 1; *ddp |= t2; } } } static void fattime2timespec(unsigned dd, unsigned dt, unsigned dh, int utc, struct timespec *tsp) { unsigned day; tsp->tv_sec = (dt & 0x1f) << 1; tsp->tv_sec += ((dt & 0x7e0) >> 5) * 60; tsp->tv_sec += ((dt & 0xf800) >> 11) * 3600; tsp->tv_sec += dh / 100; tsp->tv_nsec = (dh % 100) * 10000000; day = (dd & 0x1f) - 1; day += LYC * ((dd >> 11) & 0x1f); day += daytab[(dd >> 5) & 0x3f]; if (day >= ((2100 - 1980) / 4 * LYC + FEB)) day--; day += T1980; tsp->tv_sec += DAY * day; if (!utc) tsp->tv_sec += 0; } static void show(const char *tag, int64_t sec, long nsec) { char b[64]; time_t t = (time_t)sec; struct tm tm; gmtime_r(&t, &tm); strftime(b, sizeof b, "%Y-%m-%d %H:%M:%S", &tm); printf("%-28s %14lld.%09ld %s UTC\n", tag, (long long)sec, nsec, b); } int main(void) { struct timespec ts; uint16_t d, t; uint8_t p; /* ---- A: full-range round trip ---- */ printf("=== A: round-trip every day 1980-01-01 .. 2107-12-31 ===\n"); int64_t first_bad = 0, bad_count = 0; for (int64_t sec = 315532800LL; sec <= 4354819199LL; sec += 86400) { ts.tv_sec = sec; ts.tv_nsec = 0; d = t = p = 0; timespec2fattime(&ts, 0, &d, &t, &p); ts.tv_sec = 0; ts.tv_nsec = 0; fattime2timespec(d, t, p, 0, &ts); if (ts.tv_sec != sec) { if (!first_bad) first_bad = sec; bad_count++; } } show("first round-trip failure", first_bad, 0); printf("total failing days (of 46699): %lld\n", (long long)bad_count); /* demonstrate a specific late date */ int64_t s2107 = 4354761599LL; /* 2107-12-31 23:59:59 minus odd sec */ ts.tv_sec = 4354750800LL; ts.tv_nsec = 0; /* 2107-12-31 ~20:20 */ d = t = p = 0; timespec2fattime(&ts, 0, &d, &t, &p); printf("encoded dd=%04x t=%04x (y=%d m=%d d=%d)\n", d, t, ((d >> 9) & 0x7f) + 1980, (d >> 5) & 0x0f, d & 0x1f); ts.tv_sec = 0; ts.tv_nsec = 0; fattime2timespec(d, t, p, 0, &ts); show("2107-12-31 decodes to", ts.tv_sec, ts.tv_nsec); /* ---- B: write path beyond 2107 ---- */ printf("=== B: write path, post-2107 tv_sec (utimes, no upper bound) ===\n"); int64_t inputs[] = { 4354819200LL, /* 2108-01-01 00:00:00 */ 68718441600LL, /* 3917-06-06ish? no: 2200-11-... compute below */ 103072070400LL, /* ~3566 */ 0x7fffffffffffLL, /* huge */ 4611686018427387904LL /* 2^62 */ }; for (unsigned i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) { ts.tv_sec = inputs[i]; ts.tv_nsec = 0; d = t = p = 0; timespec2fattime(&ts, 0, &d, &t, &p); ts.tv_sec = 0; ts.tv_nsec = 0; fattime2timespec(d, t, p, 0, &ts); printf("in "); show("", inputs[i], 0); printf(" -> dd=%04x dt=%04x -> ", d, t); show("", ts.tv_sec, ts.tv_nsec); } /* ---- C: crafted read-path fields ---- */ printf("=== C: crafted dd/dt fields (month 0/13-15, hh=31, mm=63) ===\n"); unsigned cases[][3] = { { (127u<<9)|(12u<<5)|31u, (23u<<11)|(59u<<5)|29u, 0 }, /* worst valid */ { (127u<<9)|(0u<<5)|15u, 0, 0 }, /* month 0 */ { (127u<<9)|(13u<<5)|15u, 0, 0 }, /* month 13 */ { (127u<<9)|(15u<<5)|31u, 0, 0 }, /* month 15 */ { (0u<<9)|(1u<<5)|1u, (31u<<11)|(63u<<5)|31u, 255 }, /* hh31 mm63 ss62 dh255 */ }; for (unsigned i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) { ts.tv_sec = 0; ts.tv_nsec = 0; fattime2timespec(cases[i][0], cases[i][1], cases[i][2], 0, &ts); printf("dd=%04x dt=%04x dh=%02x -> ", cases[i][0], cases[i][1], cases[i][2]); show("", ts.tv_sec, ts.tv_nsec); printf(" nsec in [0,1e9)? %s\n", (ts.tv_nsec >= 0 && ts.tv_nsec < 1000000000) ? "YES" : "NO!"); } /* ---- D: day=0 known DF-0199 ---- */ printf("=== D: day=0 (known DF-0199) ===\n"); ts.tv_sec = 0; ts.tv_nsec = 0; fattime2timespec((1u<<9)|(1u<<5)|0u, 0, 0, 0, &ts); show("day 0 decodes to", ts.tv_sec, ts.tv_nsec); return 0; } |