1 /* 2 * Copyright (c) 1989, 1998 by Sun Microsystems, Inc. 3 * All rights reserved. 4 */ 5 6 /* 7 * Copyright (c) 1980 Regents of the University of California. 8 * All rights reserved. The Berkeley software License Agreement 9 * specifies the terms and conditions for redistribution. 10 */ 11 12 #pragma ident "%Z%%M% %I% %E% SMI" 13 14 #ifndef KERNEL 15 #define KERNEL 16 #endif 17 18 #include <sys/param.h> 19 #include <sys/time.h> 20 #include <sys/buf.h> 21 #include <sys/conf.h> 22 #include <sys/sysmacros.h> 23 #include <sys/kmem.h> 24 #include <sys/vfs.h> 25 #include <sys/debug.h> 26 #include <sys/errno.h> 27 #include <sys/fs/pc_fs.h> 28 #include <sys/fs/pc_label.h> 29 #include <sys/fs/pc_dir.h> 30 #include <sys/fs/pc_node.h> 31 32 /* 33 * Structure returned by gmtime and localtime calls (see ctime(3)). 34 */ 35 struct tm { 36 short tm_sec; 37 short tm_min; 38 short tm_hour; 39 short tm_mday; 40 short tm_mon; 41 short tm_year; 42 short tm_wday; 43 short tm_yday; 44 short tm_isdst; 45 }; 46 47 void pc_tvtopct(timestruc_t *, struct pctime *); 48 void pc_pcttotv(struct pctime *, timestruc_t *); 49 int pc_validchar(char); 50 51 static struct tm *localtime(time_t *tim); 52 static int sunday(struct tm *, int); 53 static int dysize(int); 54 static struct tm *gmtime(int); 55 static time_t ctime(struct tm *); 56 57 /* The cm struct defines tm_year relative to 1900 */ 58 #define YEAR_ZERO 1900 59 60 /* 61 * convert timestruct to pctime 62 */ 63 void 64 pc_tvtopct( 65 timestruc_t *tvp, /* time input */ 66 struct pctime *pctp) /* pctime output */ 67 { 68 struct tm *ctp; 69 70 ctp = localtime(&tvp->tv_sec); 71 #define setfield(S, FIELD, SFT, MSK) \ 72 S = (ltohs(S) & ~(MSK << SFT)) | (((FIELD) & MSK) << SFT); S = htols(S); 73 74 setfield(pctp->pct_time, ctp->tm_sec / 2, SECSHIFT, SECMASK); 75 setfield(pctp->pct_time, ctp->tm_min, MINSHIFT, MINMASK); 76 setfield(pctp->pct_time, ctp->tm_hour, HOURSHIFT, HOURMASK); 77 setfield(pctp->pct_date, ctp->tm_mday, DAYSHIFT, DAYMASK); 78 setfield(pctp->pct_date, ctp->tm_mon + 1, MONSHIFT, MONMASK); 79 setfield(pctp->pct_date, ctp->tm_year - 80, YEARSHIFT, YEARMASK); 80 #undef setfield 81 } 82 83 /* 84 * convert pctime to timeval 85 */ 86 void 87 pc_pcttotv( 88 struct pctime *pctp, /* ptime input */ 89 timestruc_t *tvp) /* tinmeval output */ 90 { 91 struct tm tm; 92 93 #define getfield(S, SFT, M) (((int)(ltohs(S)) >> SFT) & M) 94 tm.tm_sec = getfield(pctp->pct_time, SECSHIFT, SECMASK) * 2; 95 tm.tm_min = getfield(pctp->pct_time, MINSHIFT, MINMASK); 96 tm.tm_hour = getfield(pctp->pct_time, HOURSHIFT, HOURMASK); 97 tm.tm_mday = getfield(pctp->pct_date, DAYSHIFT, DAYMASK); 98 tm.tm_mon = getfield(pctp->pct_date, MONSHIFT, MONMASK) - 1; 99 tm.tm_year = 80 + getfield(pctp->pct_date, YEARSHIFT, YEARMASK); 100 #undef getfield 101 tvp->tv_nsec = 0; 102 tvp->tv_sec = ctime(&tm); 103 } 104 105 /* 106 * This routine converts time as follows. 107 * The epoch is 0000 Jan 1 1970 GMT. 108 * The argument time is in seconds since then. 109 * The localtime(t) entry returns a pointer to an array 110 * containing 111 * seconds (0-59) 112 * minutes (0-59) 113 * hours (0-23) 114 * day of month (1-31) 115 * month (0-11) 116 * year-1900 117 * weekday (0-6, Sun is 0) 118 * day of the year 119 * daylight savings flag 120 * 121 * The routine calls the system to determine the local 122 * timezone and whether Daylight Saving Time is permitted locally. 123 * (DST is then determined by the current local rules) 124 * 125 * The routine does not work 126 * in Saudi Arabia which runs on Solar time. 127 * 128 */ 129 130 static int dmsize[12] = 131 { 132 31, 133 28, 134 31, 135 30, 136 31, 137 30, 138 31, 139 31, 140 30, 141 31, 142 30, 143 31 144 }; 145 146 /* 147 * The following table is used for 1974 and 1975 and 148 * gives the day number of the first day after the Sunday of the 149 * change. 150 */ 151 struct dstab { 152 int dayyr; 153 int daylb; 154 int dayle; 155 }; 156 157 static struct dstab usdaytab[] = { 158 1974, 5, 333, /* 1974: Jan 6 - last Sun. in Nov */ 159 1975, 58, 303, /* 1975: Last Sun. in Feb - last Sun in Oct */ 160 0, 119, 303, /* all other years: end Apr - end Oct */ 161 }; 162 static struct dstab ausdaytab[] = { 163 1970, 400, 0, /* 1970: no daylight saving at all */ 164 1971, 303, 0, /* 1971: daylight saving from Oct 31 */ 165 1972, 303, 58, /* 1972: Jan 1 -> Feb 27 & Oct 31 -> dec 31 */ 166 0, 303, 65, /* others: -> Mar 7, Oct 31 -> */ 167 }; 168 169 /* 170 * The European tables ... based on hearsay 171 * Believed correct for: 172 * WE: Great Britain, Ireland, Portugal 173 * ME: Belgium, Luxembourg, Netherlands, Denmark, Norway, 174 * Austria, Poland, Czechoslovakia, Sweden, Switzerland, 175 * DDR, DBR, France, Spain, Hungary, Italy, Jugoslavia 176 * Eastern European dst is unknown, we'll make it ME until someone speaks up. 177 * EE: Bulgaria, Finland, Greece, Rumania, Turkey, Western Russia 178 */ 179 static struct dstab wedaytab[] = { 180 1983, 86, 303, /* 1983: end March - end Oct */ 181 1984, 86, 303, /* 1984: end March - end Oct */ 182 1985, 86, 303, /* 1985: end March - end Oct */ 183 0, 400, 0, /* others: no daylight saving at all ??? */ 184 }; 185 186 static struct dstab medaytab[] = { 187 1983, 86, 272, /* 1983: end March - end Sep */ 188 1984, 86, 272, /* 1984: end March - end Sep */ 189 1985, 86, 272, /* 1985: end March - end Sep */ 190 0, 400, 0, /* others: no daylight saving at all ??? */ 191 }; 192 193 static struct dayrules { 194 int dst_type; /* number obtained from system */ 195 int dst_hrs; /* hours to add when dst on */ 196 struct dstab *dst_rules; /* one of the above */ 197 enum {STH, NTH} dst_hemi; /* southern, northern hemisphere */ 198 } dayrules [] = { 199 DST_USA, 1, usdaytab, NTH, 200 DST_AUST, 1, ausdaytab, STH, 201 DST_WET, 1, wedaytab, NTH, 202 DST_MET, 1, medaytab, NTH, 203 DST_EET, 1, medaytab, NTH, /* XXX */ 204 -1, 205 }; 206 207 struct pcfs_args pc_tz; /* this is set by pcfs_mount */ 208 209 static struct tm * 210 localtime(time_t *tim) 211 { 212 int dayno; 213 struct tm *ct; 214 int dalybeg, daylend; 215 struct dayrules *dr; 216 struct dstab *ds; 217 int year; 218 int copyt; 219 220 copyt = *tim - (int)pc_tz.secondswest; 221 ct = gmtime(copyt); 222 dayno = ct->tm_yday; 223 for (dr = dayrules; dr->dst_type >= 0; dr++) 224 if (dr->dst_type == pc_tz.dsttime) 225 break; 226 if (dr->dst_type >= 0) { 227 year = ct->tm_year + 1900; 228 for (ds = dr->dst_rules; ds->dayyr; ds++) { 229 if (ds->dayyr == year) { 230 break; 231 } 232 } 233 dalybeg = ds->daylb; /* first Sun after dst starts */ 234 daylend = ds->dayle; /* first Sun after dst ends */ 235 dalybeg = sunday(ct, dalybeg); 236 daylend = sunday(ct, daylend); 237 switch (dr->dst_hemi) { 238 case NTH: 239 if (!( 240 (dayno > dalybeg || 241 (dayno == dalybeg && ct->tm_hour >= 2)) && 242 (dayno < daylend || 243 (dayno == daylend && ct->tm_hour < 1)))) { 244 return (ct); 245 } 246 break; 247 case STH: 248 if (!( 249 (dayno > dalybeg || 250 (dayno == dalybeg && ct->tm_hour >= 2)) || 251 (dayno < daylend || 252 (dayno == daylend && ct->tm_hour < 2)))) { 253 return (ct); 254 } 255 break; 256 default: 257 return (ct); 258 } 259 copyt += dr->dst_hrs*60*60; 260 ct = gmtime(copyt); 261 ct->tm_isdst++; 262 } 263 return (ct); 264 } 265 266 /* 267 * The argument is a 0-origin day number. 268 * The value is the day number of the first 269 * Sunday on or after the day. 270 */ 271 static int 272 sunday(struct tm *t, int d) 273 { 274 if (d >= 58) 275 d += dysize(YEAR_ZERO + t->tm_year) - 365; 276 return (d - (d - t->tm_yday + t->tm_wday + 700) % 7); 277 } 278 279 static int 280 dysize(int y) 281 { 282 if (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) 283 return (366); 284 return (365); 285 } 286 287 static struct tm * 288 gmtime(int tim) 289 { 290 int d0, d1; 291 int hms, day; 292 short *tp; 293 static struct tm xtime; 294 295 /* 296 * break initial number into days 297 */ 298 hms = tim % 86400; 299 day = tim / 86400; 300 if (hms < 0) { 301 hms += 86400; 302 day -= 1; 303 } 304 tp = (short *)&xtime; 305 306 /* 307 * generate hours:minutes:seconds 308 */ 309 *tp++ = hms%60; 310 d1 = hms/60; 311 *tp++ = d1%60; 312 d1 /= 60; 313 *tp++ = (short)d1; 314 315 /* 316 * day is the day number. 317 * generate day of the week. 318 * The addend is 4 mod 7 (1/1/1970 was Thursday) 319 */ 320 321 xtime.tm_wday = (day+7340036)%7; 322 323 /* 324 * year number 325 */ 326 if (day >= 0) 327 for (d1 = 70; day >= dysize(YEAR_ZERO + d1); d1++) 328 day -= dysize(YEAR_ZERO + d1); 329 else 330 for (d1 = 70; day < 0; d1--) 331 day += dysize(YEAR_ZERO + d1 - 1); 332 xtime.tm_year = (short)d1; 333 xtime.tm_yday = d0 = day; 334 335 /* 336 * generate month 337 */ 338 339 if (dysize(YEAR_ZERO + d1) == 366) 340 dmsize[1] = 29; 341 for (d1 = 0; d0 >= dmsize[d1]; d1++) 342 d0 -= dmsize[d1]; 343 dmsize[1] = 28; 344 *tp++ = d0+1; 345 *tp++ = (short)d1; 346 xtime.tm_isdst = 0; 347 return (&xtime); 348 } 349 350 /* 351 * convert year, month, day, hour, minute, sec to (int)time. 352 */ 353 static time_t 354 ctime(struct tm *tp) 355 { 356 int i; 357 time_t ct; 358 359 if (tp->tm_mon < 0 || tp->tm_mon > 11 || 360 tp->tm_mday < 1 || tp->tm_mday > 31 || 361 tp->tm_hour < 0 || tp->tm_hour > 23 || 362 tp->tm_min < 0 || tp->tm_min > 59 || 363 tp->tm_sec < 0 || tp->tm_sec > 59) { 364 return (0); 365 } 366 ct = 0; 367 for (i = /* 19 */ 70; i < tp->tm_year; i++) 368 ct += dysize(YEAR_ZERO + i); 369 /* Leap year */ 370 if (dysize(YEAR_ZERO + tp->tm_year) == 366 && tp->tm_mon >= 2) 371 ct++; 372 i = tp->tm_mon + 1; 373 while (--i) 374 ct += dmsize[i-1]; 375 ct += tp->tm_mday-1; 376 ct = 24*ct + tp->tm_hour; 377 ct = 60*ct + tp->tm_min; 378 ct = 60*ct + tp->tm_sec; 379 /* convert to GMT assuming local time */ 380 ct += (int)pc_tz.secondswest; 381 /* now fix up local daylight time */ 382 if (localtime(&ct)->tm_isdst) 383 ct -= 60*60; 384 return (ct); 385 } 386 387 /* 388 * Determine whether a character is valid for a pc 8.3 file system file name. 389 * The Windows 95 Resource Kit claims that these are valid: 390 * uppercase letters and numbers 391 * blank 392 * ASCII characters greater than 127 393 * $%'-_@~`!()^#& 394 * Long file names can also have 395 * lowercase letters 396 * +,;=[] 397 */ 398 int 399 pc_validchar(char c) 400 { 401 char *cp; 402 int n; 403 static char valtab[] = { 404 "$#&@!%()-{}<>`_^~|' " 405 }; 406 407 /* 408 * Should be "$#&@!%()-{}`_^~' " ?? 409 * From experiment in DOSWindows, *+=|\[];:",<>.?/ are illegal. 410 * See IBM DOS4.0 Tech Ref. B-57. 411 */ 412 413 if (c >= 'A' && c <= 'Z') 414 return (1); 415 if (c >= '0' && c <= '9') 416 return (1); 417 cp = valtab; 418 n = sizeof (valtab); 419 while (n--) { 420 if (c == *cp++) 421 return (1); 422 } 423 return (0); 424 } 425 426 /* 427 * Determine whether a character is valid for a pc 8.3 file system file name. 428 * The Windows 95 Resource Kit claims that these are valid: 429 * uppercase letters and numbers 430 * blank 431 * ASCII characters greater than 127 432 * $%'-_@~`!()^#& 433 * Long file names can also have 434 * lowercase letters 435 * +,;=[]. 436 */ 437 int 438 pc_valid_lfn_char(char c) 439 { 440 char *cp; 441 int n; 442 static char valtab[] = { 443 "+,;=[].$#&@!%()-{}<>`_^~|' " 444 }; 445 446 if (c >= 'a' && c <= 'z') 447 return (1); 448 if (c >= 'A' && c <= 'Z') 449 return (1); 450 if (c >= '0' && c <= '9') 451 return (1); 452 cp = valtab; 453 n = sizeof (valtab); 454 while (n--) { 455 if (c == *cp++) 456 return (1); 457 } 458 return (0); 459 } 460 461 int 462 pc_valid_long_fn(char *namep) 463 { 464 char *tmp; 465 466 for (tmp = namep; *tmp != '\0'; tmp++) 467 if (!pc_valid_lfn_char(*tmp)) 468 return (0); 469 if ((tmp - namep) >= PCMAXNAMLEN) 470 return (0); 471 return (1); 472 } 473 474 int 475 pc_fname_ext_to_name(char *namep, char *fname, char *ext, int foldcase) 476 { 477 int i; 478 char *tp = namep; 479 char c; 480 481 i = PCFNAMESIZE; 482 while (i-- && ((c = *fname) != ' ')) { 483 if (!(c == '.' || pc_validchar(c))) { 484 return (-1); 485 } 486 if (foldcase) 487 *tp++ = tolower(c); 488 else 489 *tp++ = c; 490 fname++; 491 } 492 if (*ext != ' ') { 493 *tp++ = '.'; 494 i = PCFEXTSIZE; 495 while (i-- && ((c = *ext) != ' ')) { 496 if (!pc_validchar(c)) { 497 return (-1); 498 } 499 if (foldcase) 500 *tp++ = tolower(c); 501 else 502 *tp++ = c; 503 ext++; 504 } 505 } 506 *tp = '\0'; 507 return (0); 508 } 509