10Sstevel@tonic-gate /* 2*2720Sfrankho * CDDL HEADER START 3*2720Sfrankho * 4*2720Sfrankho * The contents of this file are subject to the terms of the 5*2720Sfrankho * Common Development and Distribution License (the "License"). 6*2720Sfrankho * You may not use this file except in compliance with the License. 7*2720Sfrankho * 8*2720Sfrankho * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*2720Sfrankho * or http://www.opensolaris.org/os/licensing. 10*2720Sfrankho * See the License for the specific language governing permissions 11*2720Sfrankho * and limitations under the License. 12*2720Sfrankho * 13*2720Sfrankho * When distributing Covered Code, include this CDDL HEADER in each 14*2720Sfrankho * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*2720Sfrankho * If applicable, add the following below this CDDL HEADER, with the 16*2720Sfrankho * fields enclosed by brackets "[]" replaced with your own identifying 17*2720Sfrankho * information: Portions Copyright [yyyy] [name of copyright owner] 18*2720Sfrankho * 19*2720Sfrankho * CDDL HEADER END 20*2720Sfrankho */ 21*2720Sfrankho /* 22*2720Sfrankho * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23*2720Sfrankho * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate /* 270Sstevel@tonic-gate * Copyright (c) 1980 Regents of the University of California. 280Sstevel@tonic-gate * All rights reserved. The Berkeley software License Agreement 290Sstevel@tonic-gate * specifies the terms and conditions for redistribution. 300Sstevel@tonic-gate */ 310Sstevel@tonic-gate 320Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 330Sstevel@tonic-gate 340Sstevel@tonic-gate #ifndef KERNEL 350Sstevel@tonic-gate #define KERNEL 360Sstevel@tonic-gate #endif 370Sstevel@tonic-gate 380Sstevel@tonic-gate #include <sys/param.h> 390Sstevel@tonic-gate #include <sys/time.h> 400Sstevel@tonic-gate #include <sys/conf.h> 410Sstevel@tonic-gate #include <sys/sysmacros.h> 420Sstevel@tonic-gate #include <sys/vfs.h> 430Sstevel@tonic-gate #include <sys/debug.h> 440Sstevel@tonic-gate #include <sys/errno.h> 45*2720Sfrankho #include <sys/cmn_err.h> 46*2720Sfrankho #include <sys/ddi.h> 47*2720Sfrankho #include <sys/sunddi.h> 48*2720Sfrankho #include <sys/byteorder.h> 490Sstevel@tonic-gate #include <sys/fs/pc_fs.h> 500Sstevel@tonic-gate #include <sys/fs/pc_label.h> 510Sstevel@tonic-gate #include <sys/fs/pc_dir.h> 520Sstevel@tonic-gate #include <sys/fs/pc_node.h> 530Sstevel@tonic-gate 540Sstevel@tonic-gate /* 55*2720Sfrankho * Convert time between DOS formats: 56*2720Sfrankho * - years since 1980 57*2720Sfrankho * - months/days/hours/minutes/seconds, local TZ 58*2720Sfrankho * and the UNIX format (seconds since 01/01/1970, 00:00:00 UT). 59*2720Sfrankho * 60*2720Sfrankho * Timezones are adjusted for via mount option arg (secondswest), 61*2720Sfrankho * but daylight savings time corrections are not made. Calculated 62*2720Sfrankho * time may therefore end up being wrong by an hour, but this: 63*2720Sfrankho * a) will happen as well if media is interchanged between 64*2720Sfrankho * two DOS/Windows-based systems that use different 65*2720Sfrankho * timezone settings 66*2720Sfrankho * b) is the best option we have unless we decide to put 67*2720Sfrankho * a full ctime(3C) framework into the kernel, including 68*2720Sfrankho * all conversion tables - AND keeping them current ... 690Sstevel@tonic-gate */ 700Sstevel@tonic-gate 71*2720Sfrankho int pc_tvtopct(timestruc_t *, struct pctime *); 72*2720Sfrankho void pc_pcttotv(struct pctime *, int64_t *); 730Sstevel@tonic-gate 740Sstevel@tonic-gate /* 75*2720Sfrankho * Macros/Definitons required to convert between DOS-style and 76*2720Sfrankho * UNIX-style time recording. 77*2720Sfrankho * DOS year zero is 1980. 780Sstevel@tonic-gate */ 79*2720Sfrankho static int daysinmonth[] = 80*2720Sfrankho { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 810Sstevel@tonic-gate 82*2720Sfrankho #define YEAR_ZERO 1980 83*2720Sfrankho #define YZ_SECS (((8 * 365) + (2 * 366)) * 86400) 84*2720Sfrankho #define FAT_ENDOFTIME \ 85*2720Sfrankho LE_16(23 << HOURSHIFT | 59 << MINSHIFT | (59/2) << SECSHIFT) 86*2720Sfrankho #define FAT_ENDOFDATE \ 87*2720Sfrankho LE_16(127 << YEARSHIFT | 12 << MONSHIFT | 31 << DAYSHIFT) 88*2720Sfrankho #define leap_year(y) \ 89*2720Sfrankho (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) 900Sstevel@tonic-gate 91*2720Sfrankho static int 92*2720Sfrankho days_in_year(int y) 93*2720Sfrankho { 94*2720Sfrankho return (leap_year((y)) ? 366 : 365); 95*2720Sfrankho } 960Sstevel@tonic-gate 97*2720Sfrankho static int 98*2720Sfrankho days_in_month(int m, int y) 99*2720Sfrankho { 100*2720Sfrankho if (m == 2 && leap_year(y)) 101*2720Sfrankho return (29); 102*2720Sfrankho else 103*2720Sfrankho return (daysinmonth[m-1]); 104*2720Sfrankho } 1050Sstevel@tonic-gate 1060Sstevel@tonic-gate struct pcfs_args pc_tz; /* this is set by pcfs_mount */ 1070Sstevel@tonic-gate 108*2720Sfrankho /* 109*2720Sfrankho * Convert time from UNIX to DOS format. 110*2720Sfrankho * Return EOVERFLOW in case no valid DOS time representation 111*2720Sfrankho * exists for the given UNIX time. 112*2720Sfrankho */ 113*2720Sfrankho int 114*2720Sfrankho pc_tvtopct( 115*2720Sfrankho timestruc_t *tvp, /* UNIX time input */ 116*2720Sfrankho struct pctime *pctp) /* pctime output */ 1170Sstevel@tonic-gate { 118*2720Sfrankho uint_t year, month, day, hour, min, sec; 119*2720Sfrankho int64_t unixtime; 120*2720Sfrankho 121*2720Sfrankho unixtime = (int64_t)tvp->tv_sec; 122*2720Sfrankho unixtime -= YZ_SECS; 123*2720Sfrankho unixtime -= pc_tz.secondswest; 124*2720Sfrankho if (unixtime <= 0) { 125*2720Sfrankho /* 126*2720Sfrankho * "before beginning of all time" for DOS ... 127*2720Sfrankho */ 128*2720Sfrankho return (EOVERFLOW); 129*2720Sfrankho } 130*2720Sfrankho for (year = YEAR_ZERO; unixtime >= days_in_year(year) * 86400; 131*2720Sfrankho year++) 132*2720Sfrankho unixtime -= 86400 * days_in_year(year); 133*2720Sfrankho 134*2720Sfrankho if (year > 127 + YEAR_ZERO) { 135*2720Sfrankho /* 136*2720Sfrankho * "past end of all time" for DOS - can happen 137*2720Sfrankho * on a 64bit kernel via utimes() syscall ... 138*2720Sfrankho */ 139*2720Sfrankho return (EOVERFLOW); 140*2720Sfrankho } 1410Sstevel@tonic-gate 142*2720Sfrankho for (month = 1; unixtime >= 86400 * days_in_month(month, year); 143*2720Sfrankho month++) 144*2720Sfrankho unixtime -= 86400 * days_in_month(month, year); 145*2720Sfrankho 146*2720Sfrankho year -= YEAR_ZERO; 147*2720Sfrankho 148*2720Sfrankho day = (int)(unixtime / 86400); 149*2720Sfrankho unixtime -= 86400 * day++; /* counting starts at 1 */ 150*2720Sfrankho 151*2720Sfrankho hour = (int)(unixtime / 3600); 152*2720Sfrankho unixtime -= 3600 * hour; 153*2720Sfrankho 154*2720Sfrankho min = (int)(unixtime / 60); 155*2720Sfrankho unixtime -= 60 * min; 156*2720Sfrankho 157*2720Sfrankho sec = (int)unixtime; 158*2720Sfrankho 159*2720Sfrankho PC_DPRINTF3(1, "ux2pc date: %d.%d.%d\n", day, month, YEAR_ZERO + year); 160*2720Sfrankho PC_DPRINTF3(1, "ux2pc time: %dh%dm%ds\n", hour, min, sec); 161*2720Sfrankho PC_DPRINTF1(1, "ux2pc unixtime: %lld\n", (long long)(unixtime)); 162*2720Sfrankho 163*2720Sfrankho ASSERT(year >= 0 && year < 128); 164*2720Sfrankho ASSERT(month >= 1 && month <= 12); 165*2720Sfrankho ASSERT(day >= 1 && day <= days_in_month(month, year)); 166*2720Sfrankho ASSERT(hour < 24); 167*2720Sfrankho ASSERT(min < 60); 168*2720Sfrankho ASSERT(sec < 60); 169*2720Sfrankho 170*2720Sfrankho pctp->pct_time = 171*2720Sfrankho LE_16(hour << HOURSHIFT | min << MINSHIFT | (sec / 2) << SECSHIFT); 172*2720Sfrankho pctp->pct_date = 173*2720Sfrankho LE_16(year << YEARSHIFT | month << MONSHIFT | day << DAYSHIFT); 174*2720Sfrankho 175*2720Sfrankho return (0); 1760Sstevel@tonic-gate } 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate /* 179*2720Sfrankho * Convert time from DOS to UNIX time format. 180*2720Sfrankho * Since FAT timestamps cannot be expressed in 32bit time_t, 181*2720Sfrankho * the calculation is performed using 64bit values. It's up to 182*2720Sfrankho * the caller to decide what to do for out-of-UNIX-range values. 1830Sstevel@tonic-gate */ 184*2720Sfrankho void 185*2720Sfrankho pc_pcttotv( 186*2720Sfrankho struct pctime *pctp, /* DOS time input */ 187*2720Sfrankho int64_t *unixtime) /* caller converts to time_t */ 1880Sstevel@tonic-gate { 189*2720Sfrankho uint_t year, month, day, hour, min, sec; 1900Sstevel@tonic-gate 191*2720Sfrankho sec = 2 * ((LE_16(pctp->pct_time) >> SECSHIFT) & SECMASK); 192*2720Sfrankho min = (LE_16(pctp->pct_time) >> MINSHIFT) & MINMASK; 193*2720Sfrankho hour = (LE_16(pctp->pct_time) >> HOURSHIFT) & HOURMASK; 194*2720Sfrankho day = (LE_16(pctp->pct_date) >> DAYSHIFT) & DAYMASK; 195*2720Sfrankho month = (LE_16(pctp->pct_date) >> MONSHIFT) & MONMASK; 196*2720Sfrankho year = (LE_16(pctp->pct_date) >> YEARSHIFT) & YEARMASK; 197*2720Sfrankho year += YEAR_ZERO; 1980Sstevel@tonic-gate 1990Sstevel@tonic-gate /* 200*2720Sfrankho * Basic sanity checks. The FAT timestamp bitfields allow for 201*2720Sfrankho * impossible dates/times - return the "FAT epoch" for these. 2020Sstevel@tonic-gate */ 203*2720Sfrankho if (pctp->pct_date == 0) { 204*2720Sfrankho year = YEAR_ZERO; 205*2720Sfrankho month = 1; 206*2720Sfrankho day = 1; 207*2720Sfrankho } 208*2720Sfrankho if (month > 12 || month < 1 || 209*2720Sfrankho day < 1 || day > days_in_month(month, year) || 210*2720Sfrankho hour > 23 || min > 59 || sec > 59) { 211*2720Sfrankho cmn_err(CE_NOTE, "impossible FAT timestamp, " 212*2720Sfrankho "d/m/y %d/%d/%d, h:m:s %d:%d:%d", 213*2720Sfrankho day, month, year, hour, min, sec); 214*2720Sfrankho *unixtime = YZ_SECS + pc_tz.secondswest; 215*2720Sfrankho return; 2160Sstevel@tonic-gate } 217*2720Sfrankho 218*2720Sfrankho PC_DPRINTF3(1, "pc2ux date: %d.%d.%d\n", day, month, year); 219*2720Sfrankho PC_DPRINTF3(1, "pc2ux time: %dh%dm%ds\n", hour, min, sec); 2200Sstevel@tonic-gate 221*2720Sfrankho *unixtime = (int64_t)sec; 222*2720Sfrankho *unixtime += 60 * (int64_t)min; 223*2720Sfrankho *unixtime += 3600 * (int64_t)hour; 224*2720Sfrankho *unixtime += 86400 * (int64_t)day; 225*2720Sfrankho while (month > 1) { 226*2720Sfrankho month--; 227*2720Sfrankho *unixtime += 86400 * (int64_t)days_in_month(month, year); 228*2720Sfrankho } 229*2720Sfrankho while (year > YEAR_ZERO) { 230*2720Sfrankho *unixtime += 86400 * (int64_t)days_in_year(year); 231*2720Sfrankho year--; 232*2720Sfrankho } 233*2720Sfrankho /* 234*2720Sfrankho * For FAT, the beginning of all time is 01/01/1980, 235*2720Sfrankho * and years are counted relative to that. 236*2720Sfrankho * We adjust this base value by the timezone offset 237*2720Sfrankho * that is passed in to pcfs at mount time. 238*2720Sfrankho */ 239*2720Sfrankho *unixtime += YZ_SECS; 240*2720Sfrankho *unixtime += pc_tz.secondswest; 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate /* 243*2720Sfrankho * FAT epoch is past UNIX epoch - negative UNIX times 244*2720Sfrankho * cannot result from the conversion. 2450Sstevel@tonic-gate */ 246*2720Sfrankho ASSERT(*unixtime > 0); 247*2720Sfrankho PC_DPRINTF1(1, "pc2ux unixtime: %lld\n", (long long)(*unixtime)); 2480Sstevel@tonic-gate } 2490Sstevel@tonic-gate 2500Sstevel@tonic-gate /* 2510Sstevel@tonic-gate * Determine whether a character is valid for a pc 8.3 file system file name. 2520Sstevel@tonic-gate * The Windows 95 Resource Kit claims that these are valid: 2530Sstevel@tonic-gate * uppercase letters and numbers 2540Sstevel@tonic-gate * blank 2550Sstevel@tonic-gate * ASCII characters greater than 127 2560Sstevel@tonic-gate * $%'-_@~`!()^#& 2570Sstevel@tonic-gate * Long file names can also have 2580Sstevel@tonic-gate * lowercase letters 2590Sstevel@tonic-gate * +,;=[]. 2600Sstevel@tonic-gate */ 2610Sstevel@tonic-gate int 2620Sstevel@tonic-gate pc_valid_lfn_char(char c) 2630Sstevel@tonic-gate { 2640Sstevel@tonic-gate char *cp; 2650Sstevel@tonic-gate int n; 2660Sstevel@tonic-gate static char valtab[] = { 2670Sstevel@tonic-gate "+,;=[].$#&@!%()-{}<>`_^~|' " 2680Sstevel@tonic-gate }; 2690Sstevel@tonic-gate 2700Sstevel@tonic-gate if (c >= 'a' && c <= 'z') 2710Sstevel@tonic-gate return (1); 2720Sstevel@tonic-gate if (c >= 'A' && c <= 'Z') 2730Sstevel@tonic-gate return (1); 2740Sstevel@tonic-gate if (c >= '0' && c <= '9') 2750Sstevel@tonic-gate return (1); 2760Sstevel@tonic-gate cp = valtab; 2770Sstevel@tonic-gate n = sizeof (valtab); 2780Sstevel@tonic-gate while (n--) { 2790Sstevel@tonic-gate if (c == *cp++) 2800Sstevel@tonic-gate return (1); 2810Sstevel@tonic-gate } 2820Sstevel@tonic-gate return (0); 2830Sstevel@tonic-gate } 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate int 2860Sstevel@tonic-gate pc_valid_long_fn(char *namep) 2870Sstevel@tonic-gate { 2880Sstevel@tonic-gate char *tmp; 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate for (tmp = namep; *tmp != '\0'; tmp++) 2910Sstevel@tonic-gate if (!pc_valid_lfn_char(*tmp)) 2920Sstevel@tonic-gate return (0); 2930Sstevel@tonic-gate if ((tmp - namep) >= PCMAXNAMLEN) 2940Sstevel@tonic-gate return (0); 2950Sstevel@tonic-gate return (1); 2960Sstevel@tonic-gate } 2970Sstevel@tonic-gate 2980Sstevel@tonic-gate int 2990Sstevel@tonic-gate pc_fname_ext_to_name(char *namep, char *fname, char *ext, int foldcase) 3000Sstevel@tonic-gate { 3010Sstevel@tonic-gate int i; 3020Sstevel@tonic-gate char *tp = namep; 3030Sstevel@tonic-gate char c; 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate i = PCFNAMESIZE; 3060Sstevel@tonic-gate while (i-- && ((c = *fname) != ' ')) { 3070Sstevel@tonic-gate if (!(c == '.' || pc_validchar(c))) { 3080Sstevel@tonic-gate return (-1); 3090Sstevel@tonic-gate } 3100Sstevel@tonic-gate if (foldcase) 3110Sstevel@tonic-gate *tp++ = tolower(c); 3120Sstevel@tonic-gate else 3130Sstevel@tonic-gate *tp++ = c; 3140Sstevel@tonic-gate fname++; 3150Sstevel@tonic-gate } 3160Sstevel@tonic-gate if (*ext != ' ') { 3170Sstevel@tonic-gate *tp++ = '.'; 3180Sstevel@tonic-gate i = PCFEXTSIZE; 3190Sstevel@tonic-gate while (i-- && ((c = *ext) != ' ')) { 3200Sstevel@tonic-gate if (!pc_validchar(c)) { 3210Sstevel@tonic-gate return (-1); 3220Sstevel@tonic-gate } 3230Sstevel@tonic-gate if (foldcase) 3240Sstevel@tonic-gate *tp++ = tolower(c); 3250Sstevel@tonic-gate else 3260Sstevel@tonic-gate *tp++ = c; 3270Sstevel@tonic-gate ext++; 3280Sstevel@tonic-gate } 3290Sstevel@tonic-gate } 3300Sstevel@tonic-gate *tp = '\0'; 3310Sstevel@tonic-gate return (0); 3320Sstevel@tonic-gate } 333