10Sstevel@tonic-gate /* 22720Sfrankho * CDDL HEADER START 32720Sfrankho * 42720Sfrankho * The contents of this file are subject to the terms of the 52720Sfrankho * Common Development and Distribution License (the "License"). 62720Sfrankho * You may not use this file except in compliance with the License. 72720Sfrankho * 82720Sfrankho * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92720Sfrankho * or http://www.opensolaris.org/os/licensing. 102720Sfrankho * See the License for the specific language governing permissions 112720Sfrankho * and limitations under the License. 122720Sfrankho * 132720Sfrankho * When distributing Covered Code, include this CDDL HEADER in each 142720Sfrankho * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152720Sfrankho * If applicable, add the following below this CDDL HEADER, with the 162720Sfrankho * fields enclosed by brackets "[]" replaced with your own identifying 172720Sfrankho * information: Portions Copyright [yyyy] [name of copyright owner] 182720Sfrankho * 192720Sfrankho * CDDL HEADER END 202720Sfrankho */ 212720Sfrankho /* 22*7023Sbatschul * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 232720Sfrankho * 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> 452720Sfrankho #include <sys/cmn_err.h> 462720Sfrankho #include <sys/ddi.h> 472720Sfrankho #include <sys/sunddi.h> 482720Sfrankho #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 /* 552720Sfrankho * Convert time between DOS formats: 562720Sfrankho * - years since 1980 572720Sfrankho * - months/days/hours/minutes/seconds, local TZ 582720Sfrankho * and the UNIX format (seconds since 01/01/1970, 00:00:00 UT). 592720Sfrankho * 602720Sfrankho * Timezones are adjusted for via mount option arg (secondswest), 612720Sfrankho * but daylight savings time corrections are not made. Calculated 622720Sfrankho * time may therefore end up being wrong by an hour, but this: 632720Sfrankho * a) will happen as well if media is interchanged between 642720Sfrankho * two DOS/Windows-based systems that use different 652720Sfrankho * timezone settings 662720Sfrankho * b) is the best option we have unless we decide to put 672720Sfrankho * a full ctime(3C) framework into the kernel, including 682720Sfrankho * all conversion tables - AND keeping them current ... 690Sstevel@tonic-gate */ 700Sstevel@tonic-gate 712720Sfrankho int pc_tvtopct(timestruc_t *, struct pctime *); 722720Sfrankho void pc_pcttotv(struct pctime *, int64_t *); 730Sstevel@tonic-gate 740Sstevel@tonic-gate /* 752720Sfrankho * Macros/Definitons required to convert between DOS-style and 762720Sfrankho * UNIX-style time recording. 772720Sfrankho * DOS year zero is 1980. 780Sstevel@tonic-gate */ 792720Sfrankho static int daysinmonth[] = 802720Sfrankho { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 810Sstevel@tonic-gate 822720Sfrankho #define YEAR_ZERO 1980 832720Sfrankho #define YZ_SECS (((8 * 365) + (2 * 366)) * 86400) 842720Sfrankho #define FAT_ENDOFTIME \ 852720Sfrankho LE_16(23 << HOURSHIFT | 59 << MINSHIFT | (59/2) << SECSHIFT) 862720Sfrankho #define FAT_ENDOFDATE \ 872720Sfrankho LE_16(127 << YEARSHIFT | 12 << MONSHIFT | 31 << DAYSHIFT) 882720Sfrankho #define leap_year(y) \ 892720Sfrankho (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) 900Sstevel@tonic-gate 912720Sfrankho static int 922720Sfrankho days_in_year(int y) 932720Sfrankho { 942720Sfrankho return (leap_year((y)) ? 366 : 365); 952720Sfrankho } 960Sstevel@tonic-gate 972720Sfrankho static int 982720Sfrankho days_in_month(int m, int y) 992720Sfrankho { 1002720Sfrankho if (m == 2 && leap_year(y)) 1012720Sfrankho return (29); 1022720Sfrankho else 1032720Sfrankho return (daysinmonth[m-1]); 1042720Sfrankho } 1050Sstevel@tonic-gate 1060Sstevel@tonic-gate struct pcfs_args pc_tz; /* this is set by pcfs_mount */ 1070Sstevel@tonic-gate 1082720Sfrankho /* 1092720Sfrankho * Convert time from UNIX to DOS format. 1102720Sfrankho * Return EOVERFLOW in case no valid DOS time representation 1112720Sfrankho * exists for the given UNIX time. 1122720Sfrankho */ 1132720Sfrankho int 1142720Sfrankho pc_tvtopct( 1152720Sfrankho timestruc_t *tvp, /* UNIX time input */ 1162720Sfrankho struct pctime *pctp) /* pctime output */ 1170Sstevel@tonic-gate { 1182720Sfrankho uint_t year, month, day, hour, min, sec; 1192720Sfrankho int64_t unixtime; 1202720Sfrankho 1212720Sfrankho unixtime = (int64_t)tvp->tv_sec; 1222720Sfrankho unixtime -= YZ_SECS; 1232720Sfrankho unixtime -= pc_tz.secondswest; 1242720Sfrankho if (unixtime <= 0) { 1252720Sfrankho /* 1262720Sfrankho * "before beginning of all time" for DOS ... 1272720Sfrankho */ 1282720Sfrankho return (EOVERFLOW); 1292720Sfrankho } 1302720Sfrankho for (year = YEAR_ZERO; unixtime >= days_in_year(year) * 86400; 1312720Sfrankho year++) 1322720Sfrankho unixtime -= 86400 * days_in_year(year); 1332720Sfrankho 1342720Sfrankho if (year > 127 + YEAR_ZERO) { 1352720Sfrankho /* 1362720Sfrankho * "past end of all time" for DOS - can happen 1372720Sfrankho * on a 64bit kernel via utimes() syscall ... 1382720Sfrankho */ 1392720Sfrankho return (EOVERFLOW); 1402720Sfrankho } 1410Sstevel@tonic-gate 1422720Sfrankho for (month = 1; unixtime >= 86400 * days_in_month(month, year); 1432720Sfrankho month++) 1442720Sfrankho unixtime -= 86400 * days_in_month(month, year); 1452720Sfrankho 1462720Sfrankho year -= YEAR_ZERO; 1472720Sfrankho 1482720Sfrankho day = (int)(unixtime / 86400); 1492720Sfrankho unixtime -= 86400 * day++; /* counting starts at 1 */ 1502720Sfrankho 1512720Sfrankho hour = (int)(unixtime / 3600); 1522720Sfrankho unixtime -= 3600 * hour; 1532720Sfrankho 1542720Sfrankho min = (int)(unixtime / 60); 1552720Sfrankho unixtime -= 60 * min; 1562720Sfrankho 1572720Sfrankho sec = (int)unixtime; 1582720Sfrankho 1592720Sfrankho PC_DPRINTF3(1, "ux2pc date: %d.%d.%d\n", day, month, YEAR_ZERO + year); 1602720Sfrankho PC_DPRINTF3(1, "ux2pc time: %dh%dm%ds\n", hour, min, sec); 1612720Sfrankho PC_DPRINTF1(1, "ux2pc unixtime: %lld\n", (long long)(unixtime)); 1622720Sfrankho 1632720Sfrankho ASSERT(year >= 0 && year < 128); 1642720Sfrankho ASSERT(month >= 1 && month <= 12); 1652720Sfrankho ASSERT(day >= 1 && day <= days_in_month(month, year)); 1662720Sfrankho ASSERT(hour < 24); 1672720Sfrankho ASSERT(min < 60); 1682720Sfrankho ASSERT(sec < 60); 1692720Sfrankho 1702720Sfrankho pctp->pct_time = 1712720Sfrankho LE_16(hour << HOURSHIFT | min << MINSHIFT | (sec / 2) << SECSHIFT); 1722720Sfrankho pctp->pct_date = 1732720Sfrankho LE_16(year << YEARSHIFT | month << MONSHIFT | day << DAYSHIFT); 1742720Sfrankho 1752720Sfrankho return (0); 1760Sstevel@tonic-gate } 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate /* 1792720Sfrankho * Convert time from DOS to UNIX time format. 1802720Sfrankho * Since FAT timestamps cannot be expressed in 32bit time_t, 1812720Sfrankho * the calculation is performed using 64bit values. It's up to 1822720Sfrankho * the caller to decide what to do for out-of-UNIX-range values. 1830Sstevel@tonic-gate */ 1842720Sfrankho void 1852720Sfrankho pc_pcttotv( 1862720Sfrankho struct pctime *pctp, /* DOS time input */ 1872720Sfrankho int64_t *unixtime) /* caller converts to time_t */ 1880Sstevel@tonic-gate { 1892720Sfrankho uint_t year, month, day, hour, min, sec; 1900Sstevel@tonic-gate 1912720Sfrankho sec = 2 * ((LE_16(pctp->pct_time) >> SECSHIFT) & SECMASK); 1922720Sfrankho min = (LE_16(pctp->pct_time) >> MINSHIFT) & MINMASK; 1932720Sfrankho hour = (LE_16(pctp->pct_time) >> HOURSHIFT) & HOURMASK; 1942720Sfrankho day = (LE_16(pctp->pct_date) >> DAYSHIFT) & DAYMASK; 1952720Sfrankho month = (LE_16(pctp->pct_date) >> MONSHIFT) & MONMASK; 1962720Sfrankho year = (LE_16(pctp->pct_date) >> YEARSHIFT) & YEARMASK; 1972720Sfrankho year += YEAR_ZERO; 1980Sstevel@tonic-gate 1990Sstevel@tonic-gate /* 2002720Sfrankho * Basic sanity checks. The FAT timestamp bitfields allow for 2012720Sfrankho * impossible dates/times - return the "FAT epoch" for these. 2020Sstevel@tonic-gate */ 2032720Sfrankho if (pctp->pct_date == 0) { 2042720Sfrankho year = YEAR_ZERO; 2052720Sfrankho month = 1; 2062720Sfrankho day = 1; 2072720Sfrankho } 2082720Sfrankho if (month > 12 || month < 1 || 2092720Sfrankho day < 1 || day > days_in_month(month, year) || 2102720Sfrankho hour > 23 || min > 59 || sec > 59) { 2112720Sfrankho cmn_err(CE_NOTE, "impossible FAT timestamp, " 2122720Sfrankho "d/m/y %d/%d/%d, h:m:s %d:%d:%d", 2132720Sfrankho day, month, year, hour, min, sec); 2142720Sfrankho *unixtime = YZ_SECS + pc_tz.secondswest; 2152720Sfrankho return; 2160Sstevel@tonic-gate } 2172720Sfrankho 2182720Sfrankho PC_DPRINTF3(1, "pc2ux date: %d.%d.%d\n", day, month, year); 2192720Sfrankho PC_DPRINTF3(1, "pc2ux time: %dh%dm%ds\n", hour, min, sec); 2200Sstevel@tonic-gate 2212720Sfrankho *unixtime = (int64_t)sec; 2222720Sfrankho *unixtime += 60 * (int64_t)min; 2232720Sfrankho *unixtime += 3600 * (int64_t)hour; 224*7023Sbatschul *unixtime += 86400 * (int64_t)(day -1); 2252720Sfrankho while (month > 1) { 2262720Sfrankho month--; 2272720Sfrankho *unixtime += 86400 * (int64_t)days_in_month(month, year); 2282720Sfrankho } 2292720Sfrankho while (year > YEAR_ZERO) { 230*7023Sbatschul year--; 2312720Sfrankho *unixtime += 86400 * (int64_t)days_in_year(year); 2322720Sfrankho } 2332720Sfrankho /* 2342720Sfrankho * For FAT, the beginning of all time is 01/01/1980, 2352720Sfrankho * and years are counted relative to that. 2362720Sfrankho * We adjust this base value by the timezone offset 2372720Sfrankho * that is passed in to pcfs at mount time. 2382720Sfrankho */ 2392720Sfrankho *unixtime += YZ_SECS; 2402720Sfrankho *unixtime += pc_tz.secondswest; 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate /* 2432720Sfrankho * FAT epoch is past UNIX epoch - negative UNIX times 2442720Sfrankho * cannot result from the conversion. 2450Sstevel@tonic-gate */ 2462720Sfrankho ASSERT(*unixtime > 0); 2472720Sfrankho PC_DPRINTF1(1, "pc2ux unixtime: %lld\n", (long long)(*unixtime)); 2480Sstevel@tonic-gate } 2490Sstevel@tonic-gate 2500Sstevel@tonic-gate /* 2514723Sksn * Determine whether a character is valid for a long file name. 2524723Sksn * It is easier to determine by filtering out invalid characters. 2534723Sksn * Following are invalid characters in a long filename. 2544723Sksn * / \ : * ? < > | " 2550Sstevel@tonic-gate */ 2560Sstevel@tonic-gate int 2570Sstevel@tonic-gate pc_valid_lfn_char(char c) 2580Sstevel@tonic-gate { 2594723Sksn const char *cp; 2600Sstevel@tonic-gate int n; 2614723Sksn 2624723Sksn static const char invaltab[] = { 2634723Sksn "/\\:*?<>|\"" 2640Sstevel@tonic-gate }; 2650Sstevel@tonic-gate 2664723Sksn cp = invaltab; 2674723Sksn n = sizeof (invaltab) - 1; 2680Sstevel@tonic-gate while (n--) { 2690Sstevel@tonic-gate if (c == *cp++) 2704723Sksn return (0); 2710Sstevel@tonic-gate } 2724723Sksn return (1); 2730Sstevel@tonic-gate } 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate int 2764723Sksn pc_valid_long_fn(char *namep, int utf8) 2770Sstevel@tonic-gate { 2780Sstevel@tonic-gate char *tmp; 2790Sstevel@tonic-gate 2804723Sksn if (utf8) { 2814723Sksn /* UTF-8 */ 2824723Sksn for (tmp = namep; *tmp != '\0'; tmp++) 2834723Sksn if (!pc_valid_lfn_char(*tmp)) 2844723Sksn return (0); 2854723Sksn if ((tmp - namep) > PCMAXNAMLEN) 2860Sstevel@tonic-gate return (0); 2874723Sksn } else { 2884723Sksn /* UTF-16 */ 2894723Sksn for (tmp = namep; (*tmp != '\0') && (*(tmp+1) != '\0'); 2904723Sksn tmp += 2) { 2914723Sksn if ((*(tmp+1) == '\0') && !pc_valid_lfn_char(*tmp)) 2924723Sksn return (0); 2934723Sksn } 2944723Sksn if ((tmp - namep) > (PCMAXNAMLEN * sizeof (uint16_t))) 2954723Sksn return (0); 2964723Sksn } 2970Sstevel@tonic-gate return (1); 2980Sstevel@tonic-gate } 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate int 3010Sstevel@tonic-gate pc_fname_ext_to_name(char *namep, char *fname, char *ext, int foldcase) 3020Sstevel@tonic-gate { 3030Sstevel@tonic-gate int i; 3040Sstevel@tonic-gate char *tp = namep; 3050Sstevel@tonic-gate char c; 3060Sstevel@tonic-gate 3070Sstevel@tonic-gate i = PCFNAMESIZE; 3080Sstevel@tonic-gate while (i-- && ((c = *fname) != ' ')) { 3090Sstevel@tonic-gate if (!(c == '.' || pc_validchar(c))) { 3100Sstevel@tonic-gate return (-1); 3110Sstevel@tonic-gate } 3120Sstevel@tonic-gate if (foldcase) 3130Sstevel@tonic-gate *tp++ = tolower(c); 3140Sstevel@tonic-gate else 3150Sstevel@tonic-gate *tp++ = c; 3160Sstevel@tonic-gate fname++; 3170Sstevel@tonic-gate } 3180Sstevel@tonic-gate if (*ext != ' ') { 3190Sstevel@tonic-gate *tp++ = '.'; 3200Sstevel@tonic-gate i = PCFEXTSIZE; 3210Sstevel@tonic-gate while (i-- && ((c = *ext) != ' ')) { 3220Sstevel@tonic-gate if (!pc_validchar(c)) { 3230Sstevel@tonic-gate return (-1); 3240Sstevel@tonic-gate } 3250Sstevel@tonic-gate if (foldcase) 3260Sstevel@tonic-gate *tp++ = tolower(c); 3270Sstevel@tonic-gate else 3280Sstevel@tonic-gate *tp++ = c; 3290Sstevel@tonic-gate ext++; 3300Sstevel@tonic-gate } 3310Sstevel@tonic-gate } 3320Sstevel@tonic-gate *tp = '\0'; 3330Sstevel@tonic-gate return (0); 3340Sstevel@tonic-gate } 335