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 /*
227023Sbatschul * 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 #ifndef KERNEL
330Sstevel@tonic-gate #define KERNEL
340Sstevel@tonic-gate #endif
350Sstevel@tonic-gate
360Sstevel@tonic-gate #include <sys/param.h>
370Sstevel@tonic-gate #include <sys/time.h>
380Sstevel@tonic-gate #include <sys/conf.h>
390Sstevel@tonic-gate #include <sys/sysmacros.h>
400Sstevel@tonic-gate #include <sys/vfs.h>
410Sstevel@tonic-gate #include <sys/debug.h>
420Sstevel@tonic-gate #include <sys/errno.h>
432720Sfrankho #include <sys/cmn_err.h>
442720Sfrankho #include <sys/ddi.h>
452720Sfrankho #include <sys/sunddi.h>
462720Sfrankho #include <sys/byteorder.h>
47*7735SOwen.Roberts@Sun.Com #include <sys/types.h>
480Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
490Sstevel@tonic-gate #include <sys/fs/pc_label.h>
500Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
510Sstevel@tonic-gate #include <sys/fs/pc_node.h>
520Sstevel@tonic-gate
530Sstevel@tonic-gate /*
542720Sfrankho * Convert time between DOS formats:
552720Sfrankho * - years since 1980
562720Sfrankho * - months/days/hours/minutes/seconds, local TZ
572720Sfrankho * and the UNIX format (seconds since 01/01/1970, 00:00:00 UT).
582720Sfrankho *
592720Sfrankho * Timezones are adjusted for via mount option arg (secondswest),
602720Sfrankho * but daylight savings time corrections are not made. Calculated
612720Sfrankho * time may therefore end up being wrong by an hour, but this:
622720Sfrankho * a) will happen as well if media is interchanged between
632720Sfrankho * two DOS/Windows-based systems that use different
642720Sfrankho * timezone settings
652720Sfrankho * b) is the best option we have unless we decide to put
662720Sfrankho * a full ctime(3C) framework into the kernel, including
672720Sfrankho * all conversion tables - AND keeping them current ...
680Sstevel@tonic-gate */
690Sstevel@tonic-gate
702720Sfrankho int pc_tvtopct(timestruc_t *, struct pctime *);
712720Sfrankho void pc_pcttotv(struct pctime *, int64_t *);
720Sstevel@tonic-gate
730Sstevel@tonic-gate /*
742720Sfrankho * Macros/Definitons required to convert between DOS-style and
752720Sfrankho * UNIX-style time recording.
762720Sfrankho * DOS year zero is 1980.
770Sstevel@tonic-gate */
782720Sfrankho static int daysinmonth[] =
792720Sfrankho { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
800Sstevel@tonic-gate
812720Sfrankho #define YEAR_ZERO 1980
822720Sfrankho #define YZ_SECS (((8 * 365) + (2 * 366)) * 86400)
832720Sfrankho #define FAT_ENDOFTIME \
842720Sfrankho LE_16(23 << HOURSHIFT | 59 << MINSHIFT | (59/2) << SECSHIFT)
852720Sfrankho #define FAT_ENDOFDATE \
862720Sfrankho LE_16(127 << YEARSHIFT | 12 << MONSHIFT | 31 << DAYSHIFT)
872720Sfrankho #define leap_year(y) \
882720Sfrankho (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
890Sstevel@tonic-gate
90*7735SOwen.Roberts@Sun.Com #define YEN "\xc2\xa5" /* Yen Sign UTF-8 character */
91*7735SOwen.Roberts@Sun.Com #define LRO "\xe2\x80\xad" /* Left-To-Right Override UTF-8 character */
92*7735SOwen.Roberts@Sun.Com #define RLO "\xe2\x80\xae" /* Right-To-Left Override UTF-8 character */
93*7735SOwen.Roberts@Sun.Com
942720Sfrankho static int
days_in_year(int y)952720Sfrankho days_in_year(int y)
962720Sfrankho {
972720Sfrankho return (leap_year((y)) ? 366 : 365);
982720Sfrankho }
990Sstevel@tonic-gate
1002720Sfrankho static int
days_in_month(int m,int y)1012720Sfrankho days_in_month(int m, int y)
1022720Sfrankho {
1032720Sfrankho if (m == 2 && leap_year(y))
1042720Sfrankho return (29);
1052720Sfrankho else
1062720Sfrankho return (daysinmonth[m-1]);
1072720Sfrankho }
1080Sstevel@tonic-gate
1090Sstevel@tonic-gate struct pcfs_args pc_tz; /* this is set by pcfs_mount */
1100Sstevel@tonic-gate
1112720Sfrankho /*
1122720Sfrankho * Convert time from UNIX to DOS format.
1132720Sfrankho * Return EOVERFLOW in case no valid DOS time representation
1142720Sfrankho * exists for the given UNIX time.
1152720Sfrankho */
1162720Sfrankho int
pc_tvtopct(timestruc_t * tvp,struct pctime * pctp)1172720Sfrankho pc_tvtopct(
1182720Sfrankho timestruc_t *tvp, /* UNIX time input */
1192720Sfrankho struct pctime *pctp) /* pctime output */
1200Sstevel@tonic-gate {
1212720Sfrankho uint_t year, month, day, hour, min, sec;
1222720Sfrankho int64_t unixtime;
1232720Sfrankho
1242720Sfrankho unixtime = (int64_t)tvp->tv_sec;
1252720Sfrankho unixtime -= YZ_SECS;
1262720Sfrankho unixtime -= pc_tz.secondswest;
1272720Sfrankho if (unixtime <= 0) {
1282720Sfrankho /*
1292720Sfrankho * "before beginning of all time" for DOS ...
1302720Sfrankho */
1312720Sfrankho return (EOVERFLOW);
1322720Sfrankho }
1332720Sfrankho for (year = YEAR_ZERO; unixtime >= days_in_year(year) * 86400;
1342720Sfrankho year++)
1352720Sfrankho unixtime -= 86400 * days_in_year(year);
1362720Sfrankho
1372720Sfrankho if (year > 127 + YEAR_ZERO) {
1382720Sfrankho /*
1392720Sfrankho * "past end of all time" for DOS - can happen
1402720Sfrankho * on a 64bit kernel via utimes() syscall ...
1412720Sfrankho */
1422720Sfrankho return (EOVERFLOW);
1432720Sfrankho }
1440Sstevel@tonic-gate
1452720Sfrankho for (month = 1; unixtime >= 86400 * days_in_month(month, year);
1462720Sfrankho month++)
1472720Sfrankho unixtime -= 86400 * days_in_month(month, year);
1482720Sfrankho
1492720Sfrankho year -= YEAR_ZERO;
1502720Sfrankho
1512720Sfrankho day = (int)(unixtime / 86400);
1522720Sfrankho unixtime -= 86400 * day++; /* counting starts at 1 */
1532720Sfrankho
1542720Sfrankho hour = (int)(unixtime / 3600);
1552720Sfrankho unixtime -= 3600 * hour;
1562720Sfrankho
1572720Sfrankho min = (int)(unixtime / 60);
1582720Sfrankho unixtime -= 60 * min;
1592720Sfrankho
1602720Sfrankho sec = (int)unixtime;
1612720Sfrankho
1622720Sfrankho PC_DPRINTF3(1, "ux2pc date: %d.%d.%d\n", day, month, YEAR_ZERO + year);
1632720Sfrankho PC_DPRINTF3(1, "ux2pc time: %dh%dm%ds\n", hour, min, sec);
1642720Sfrankho PC_DPRINTF1(1, "ux2pc unixtime: %lld\n", (long long)(unixtime));
1652720Sfrankho
1662720Sfrankho ASSERT(year >= 0 && year < 128);
1672720Sfrankho ASSERT(month >= 1 && month <= 12);
1682720Sfrankho ASSERT(day >= 1 && day <= days_in_month(month, year));
1692720Sfrankho ASSERT(hour < 24);
1702720Sfrankho ASSERT(min < 60);
1712720Sfrankho ASSERT(sec < 60);
1722720Sfrankho
1732720Sfrankho pctp->pct_time =
1742720Sfrankho LE_16(hour << HOURSHIFT | min << MINSHIFT | (sec / 2) << SECSHIFT);
1752720Sfrankho pctp->pct_date =
1762720Sfrankho LE_16(year << YEARSHIFT | month << MONSHIFT | day << DAYSHIFT);
1772720Sfrankho
1782720Sfrankho return (0);
1790Sstevel@tonic-gate }
1800Sstevel@tonic-gate
1810Sstevel@tonic-gate /*
1822720Sfrankho * Convert time from DOS to UNIX time format.
1832720Sfrankho * Since FAT timestamps cannot be expressed in 32bit time_t,
1842720Sfrankho * the calculation is performed using 64bit values. It's up to
1852720Sfrankho * the caller to decide what to do for out-of-UNIX-range values.
1860Sstevel@tonic-gate */
1872720Sfrankho void
pc_pcttotv(struct pctime * pctp,int64_t * unixtime)1882720Sfrankho pc_pcttotv(
1892720Sfrankho struct pctime *pctp, /* DOS time input */
1902720Sfrankho int64_t *unixtime) /* caller converts to time_t */
1910Sstevel@tonic-gate {
1922720Sfrankho uint_t year, month, day, hour, min, sec;
1930Sstevel@tonic-gate
1942720Sfrankho sec = 2 * ((LE_16(pctp->pct_time) >> SECSHIFT) & SECMASK);
1952720Sfrankho min = (LE_16(pctp->pct_time) >> MINSHIFT) & MINMASK;
1962720Sfrankho hour = (LE_16(pctp->pct_time) >> HOURSHIFT) & HOURMASK;
1972720Sfrankho day = (LE_16(pctp->pct_date) >> DAYSHIFT) & DAYMASK;
1982720Sfrankho month = (LE_16(pctp->pct_date) >> MONSHIFT) & MONMASK;
1992720Sfrankho year = (LE_16(pctp->pct_date) >> YEARSHIFT) & YEARMASK;
2002720Sfrankho year += YEAR_ZERO;
2010Sstevel@tonic-gate
2020Sstevel@tonic-gate /*
2032720Sfrankho * Basic sanity checks. The FAT timestamp bitfields allow for
2042720Sfrankho * impossible dates/times - return the "FAT epoch" for these.
2050Sstevel@tonic-gate */
2062720Sfrankho if (pctp->pct_date == 0) {
2072720Sfrankho year = YEAR_ZERO;
2082720Sfrankho month = 1;
2092720Sfrankho day = 1;
2102720Sfrankho }
2112720Sfrankho if (month > 12 || month < 1 ||
2122720Sfrankho day < 1 || day > days_in_month(month, year) ||
2132720Sfrankho hour > 23 || min > 59 || sec > 59) {
2142720Sfrankho cmn_err(CE_NOTE, "impossible FAT timestamp, "
2152720Sfrankho "d/m/y %d/%d/%d, h:m:s %d:%d:%d",
2162720Sfrankho day, month, year, hour, min, sec);
2172720Sfrankho *unixtime = YZ_SECS + pc_tz.secondswest;
2182720Sfrankho return;
2190Sstevel@tonic-gate }
2202720Sfrankho
2212720Sfrankho PC_DPRINTF3(1, "pc2ux date: %d.%d.%d\n", day, month, year);
2222720Sfrankho PC_DPRINTF3(1, "pc2ux time: %dh%dm%ds\n", hour, min, sec);
2230Sstevel@tonic-gate
2242720Sfrankho *unixtime = (int64_t)sec;
2252720Sfrankho *unixtime += 60 * (int64_t)min;
2262720Sfrankho *unixtime += 3600 * (int64_t)hour;
2277023Sbatschul *unixtime += 86400 * (int64_t)(day -1);
2282720Sfrankho while (month > 1) {
2292720Sfrankho month--;
2302720Sfrankho *unixtime += 86400 * (int64_t)days_in_month(month, year);
2312720Sfrankho }
2322720Sfrankho while (year > YEAR_ZERO) {
2337023Sbatschul year--;
2342720Sfrankho *unixtime += 86400 * (int64_t)days_in_year(year);
2352720Sfrankho }
2362720Sfrankho /*
2372720Sfrankho * For FAT, the beginning of all time is 01/01/1980,
2382720Sfrankho * and years are counted relative to that.
2392720Sfrankho * We adjust this base value by the timezone offset
2402720Sfrankho * that is passed in to pcfs at mount time.
2412720Sfrankho */
2422720Sfrankho *unixtime += YZ_SECS;
2432720Sfrankho *unixtime += pc_tz.secondswest;
2440Sstevel@tonic-gate
2450Sstevel@tonic-gate /*
2462720Sfrankho * FAT epoch is past UNIX epoch - negative UNIX times
2472720Sfrankho * cannot result from the conversion.
2480Sstevel@tonic-gate */
2492720Sfrankho ASSERT(*unixtime > 0);
2502720Sfrankho PC_DPRINTF1(1, "pc2ux unixtime: %lld\n", (long long)(*unixtime));
2510Sstevel@tonic-gate }
2520Sstevel@tonic-gate
2530Sstevel@tonic-gate /*
2544723Sksn * Determine whether a character is valid for a long file name.
2554723Sksn * It is easier to determine by filtering out invalid characters.
2564723Sksn * Following are invalid characters in a long filename.
2574723Sksn * / \ : * ? < > | "
2580Sstevel@tonic-gate */
2590Sstevel@tonic-gate int
pc_valid_lfn_char(char c)2600Sstevel@tonic-gate pc_valid_lfn_char(char c)
2610Sstevel@tonic-gate {
2624723Sksn const char *cp;
2630Sstevel@tonic-gate int n;
2644723Sksn
2654723Sksn static const char invaltab[] = {
2664723Sksn "/\\:*?<>|\""
2670Sstevel@tonic-gate };
2680Sstevel@tonic-gate
2694723Sksn cp = invaltab;
2704723Sksn n = sizeof (invaltab) - 1;
2710Sstevel@tonic-gate while (n--) {
2720Sstevel@tonic-gate if (c == *cp++)
2734723Sksn return (0);
2740Sstevel@tonic-gate }
2754723Sksn return (1);
2760Sstevel@tonic-gate }
2770Sstevel@tonic-gate
2780Sstevel@tonic-gate int
pc_valid_long_fn(char * namep,int utf8)2794723Sksn pc_valid_long_fn(char *namep, int utf8)
2800Sstevel@tonic-gate {
2810Sstevel@tonic-gate char *tmp;
282*7735SOwen.Roberts@Sun.Com int len, error;
283*7735SOwen.Roberts@Sun.Com char *prohibited[13] = {
284*7735SOwen.Roberts@Sun.Com "/", "\\", ":", "*", "?", "<", ">", "|", "\"", YEN, LRO, RLO,
285*7735SOwen.Roberts@Sun.Com NULL
286*7735SOwen.Roberts@Sun.Com };
2870Sstevel@tonic-gate
2884723Sksn if (utf8) {
2894723Sksn /* UTF-8 */
290*7735SOwen.Roberts@Sun.Com if ((len = u8_validate(namep, strlen(namep), prohibited,
291*7735SOwen.Roberts@Sun.Com (U8_VALIDATE_ENTIRE|U8_VALIDATE_CHECK_ADDITIONAL),
292*7735SOwen.Roberts@Sun.Com &error)) < 0)
293*7735SOwen.Roberts@Sun.Com return (0);
294*7735SOwen.Roberts@Sun.Com if (len > PCMAXNAMLEN)
2950Sstevel@tonic-gate return (0);
2964723Sksn } else {
2974723Sksn /* UTF-16 */
298*7735SOwen.Roberts@Sun.Com for (tmp = namep; (*tmp != '\0') || (*(tmp+1) != '\0');
2994723Sksn tmp += 2) {
3004723Sksn if ((*(tmp+1) == '\0') && !pc_valid_lfn_char(*tmp))
3014723Sksn return (0);
302*7735SOwen.Roberts@Sun.Com
303*7735SOwen.Roberts@Sun.Com /* Prohibit the Yen character */
304*7735SOwen.Roberts@Sun.Com if ((*(tmp+1) == '\0') && (*tmp == '\xa5'))
305*7735SOwen.Roberts@Sun.Com return (0);
306*7735SOwen.Roberts@Sun.Com
307*7735SOwen.Roberts@Sun.Com /* Prohibit the left-to-right override control char */
308*7735SOwen.Roberts@Sun.Com if ((*(tmp+1) == '\x20') && (*tmp == '\x2d'))
309*7735SOwen.Roberts@Sun.Com return (0);
310*7735SOwen.Roberts@Sun.Com
311*7735SOwen.Roberts@Sun.Com /* Prohibit the right-to-left override control char */
312*7735SOwen.Roberts@Sun.Com if ((*(tmp+1) == '\x20') && (*tmp == '\x2e'))
313*7735SOwen.Roberts@Sun.Com return (0);
3144723Sksn }
3154723Sksn if ((tmp - namep) > (PCMAXNAMLEN * sizeof (uint16_t)))
3164723Sksn return (0);
3174723Sksn }
3180Sstevel@tonic-gate return (1);
3190Sstevel@tonic-gate }
3200Sstevel@tonic-gate
3210Sstevel@tonic-gate int
pc_fname_ext_to_name(char * namep,char * fname,char * ext,int foldcase)3220Sstevel@tonic-gate pc_fname_ext_to_name(char *namep, char *fname, char *ext, int foldcase)
3230Sstevel@tonic-gate {
3240Sstevel@tonic-gate int i;
3250Sstevel@tonic-gate char *tp = namep;
3260Sstevel@tonic-gate char c;
3270Sstevel@tonic-gate
3280Sstevel@tonic-gate i = PCFNAMESIZE;
3290Sstevel@tonic-gate while (i-- && ((c = *fname) != ' ')) {
3300Sstevel@tonic-gate if (!(c == '.' || pc_validchar(c))) {
3310Sstevel@tonic-gate return (-1);
3320Sstevel@tonic-gate }
3330Sstevel@tonic-gate if (foldcase)
3340Sstevel@tonic-gate *tp++ = tolower(c);
3350Sstevel@tonic-gate else
3360Sstevel@tonic-gate *tp++ = c;
3370Sstevel@tonic-gate fname++;
3380Sstevel@tonic-gate }
3390Sstevel@tonic-gate if (*ext != ' ') {
3400Sstevel@tonic-gate *tp++ = '.';
3410Sstevel@tonic-gate i = PCFEXTSIZE;
3420Sstevel@tonic-gate while (i-- && ((c = *ext) != ' ')) {
3430Sstevel@tonic-gate if (!pc_validchar(c)) {
3440Sstevel@tonic-gate return (-1);
3450Sstevel@tonic-gate }
3460Sstevel@tonic-gate if (foldcase)
3470Sstevel@tonic-gate *tp++ = tolower(c);
3480Sstevel@tonic-gate else
3490Sstevel@tonic-gate *tp++ = c;
3500Sstevel@tonic-gate ext++;
3510Sstevel@tonic-gate }
3520Sstevel@tonic-gate }
3530Sstevel@tonic-gate *tp = '\0';
3540Sstevel@tonic-gate return (0);
3550Sstevel@tonic-gate }
356