xref: /onnv-gate/usr/src/uts/common/fs/pcfs/pc_subr.c (revision 2720:a82b4631f707)
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