xref: /illumos-gate/usr/src/uts/common/fs/pcfs/pc_subr.c (revision 4d28e670)
17c478bd9Sstevel@tonic-gate /*
2264a6e74Sfrankho  * CDDL HEADER START
3264a6e74Sfrankho  *
4264a6e74Sfrankho  * The contents of this file are subject to the terms of the
5264a6e74Sfrankho  * Common Development and Distribution License (the "License").
6264a6e74Sfrankho  * You may not use this file except in compliance with the License.
7264a6e74Sfrankho  *
8264a6e74Sfrankho  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9264a6e74Sfrankho  * or http://www.opensolaris.org/os/licensing.
10264a6e74Sfrankho  * See the License for the specific language governing permissions
11264a6e74Sfrankho  * and limitations under the License.
12264a6e74Sfrankho  *
13264a6e74Sfrankho  * When distributing Covered Code, include this CDDL HEADER in each
14264a6e74Sfrankho  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15264a6e74Sfrankho  * If applicable, add the following below this CDDL HEADER, with the
16264a6e74Sfrankho  * fields enclosed by brackets "[]" replaced with your own identifying
17264a6e74Sfrankho  * information: Portions Copyright [yyyy] [name of copyright owner]
18264a6e74Sfrankho  *
19264a6e74Sfrankho  * CDDL HEADER END
20264a6e74Sfrankho  */
21264a6e74Sfrankho /*
22*4d28e670Sbatschul  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23264a6e74Sfrankho  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * Copyright (c) 1980 Regents of the University of California.
287c478bd9Sstevel@tonic-gate  * All rights reserved. The Berkeley software License Agreement
297c478bd9Sstevel@tonic-gate  * specifies the terms and conditions for redistribution.
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #ifndef KERNEL
357c478bd9Sstevel@tonic-gate #define	KERNEL
367c478bd9Sstevel@tonic-gate #endif
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate #include <sys/param.h>
397c478bd9Sstevel@tonic-gate #include <sys/time.h>
407c478bd9Sstevel@tonic-gate #include <sys/conf.h>
417c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
427c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
437c478bd9Sstevel@tonic-gate #include <sys/debug.h>
447c478bd9Sstevel@tonic-gate #include <sys/errno.h>
45264a6e74Sfrankho #include <sys/cmn_err.h>
46264a6e74Sfrankho #include <sys/ddi.h>
47264a6e74Sfrankho #include <sys/sunddi.h>
48264a6e74Sfrankho #include <sys/byteorder.h>
497c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
507c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
517c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
527c478bd9Sstevel@tonic-gate #include <sys/fs/pc_node.h>
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate /*
55264a6e74Sfrankho  * Convert time between DOS formats:
56264a6e74Sfrankho  *	- years since 1980
57264a6e74Sfrankho  *	- months/days/hours/minutes/seconds, local TZ
58264a6e74Sfrankho  * and the UNIX format (seconds since 01/01/1970, 00:00:00 UT).
59264a6e74Sfrankho  *
60264a6e74Sfrankho  * Timezones are adjusted for via mount option arg (secondswest),
61264a6e74Sfrankho  * but daylight savings time corrections are not made. Calculated
62264a6e74Sfrankho  * time may therefore end up being wrong by an hour, but this:
63264a6e74Sfrankho  *	a) will happen as well if media is interchanged between
64264a6e74Sfrankho  *	   two DOS/Windows-based systems that use different
65264a6e74Sfrankho  *	   timezone settings
66264a6e74Sfrankho  *	b) is the best option we have unless we decide to put
67264a6e74Sfrankho  *	   a full ctime(3C) framework into the kernel, including
68264a6e74Sfrankho  *	   all conversion tables - AND keeping them current ...
697c478bd9Sstevel@tonic-gate  */
707c478bd9Sstevel@tonic-gate 
71264a6e74Sfrankho int pc_tvtopct(timestruc_t *, struct pctime *);
72264a6e74Sfrankho void pc_pcttotv(struct pctime *, int64_t *);
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate /*
75264a6e74Sfrankho  * Macros/Definitons required to convert between DOS-style and
76264a6e74Sfrankho  * UNIX-style time recording.
77264a6e74Sfrankho  * DOS year zero is 1980.
787c478bd9Sstevel@tonic-gate  */
79264a6e74Sfrankho static int daysinmonth[] =
80264a6e74Sfrankho 	    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
81264a6e74Sfrankho 
82264a6e74Sfrankho #define	YEAR_ZERO	1980
83264a6e74Sfrankho #define	YZ_SECS	(((8 * 365) + (2 * 366)) * 86400)
84264a6e74Sfrankho #define	FAT_ENDOFTIME	\
85264a6e74Sfrankho 	LE_16(23 << HOURSHIFT | 59 << MINSHIFT | (59/2) << SECSHIFT)
86264a6e74Sfrankho #define	FAT_ENDOFDATE	\
87264a6e74Sfrankho 	LE_16(127 << YEARSHIFT | 12 << MONSHIFT | 31 << DAYSHIFT)
88264a6e74Sfrankho #define	leap_year(y) \
89264a6e74Sfrankho 	(((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
907c478bd9Sstevel@tonic-gate 
91264a6e74Sfrankho static int
92264a6e74Sfrankho days_in_year(int y)
93264a6e74Sfrankho {
94264a6e74Sfrankho 	return (leap_year((y)) ? 366 : 365);
957c478bd9Sstevel@tonic-gate }
967c478bd9Sstevel@tonic-gate 
97264a6e74Sfrankho static int
98264a6e74Sfrankho days_in_month(int m, int y)
997c478bd9Sstevel@tonic-gate {
100264a6e74Sfrankho 	if (m == 2 && leap_year(y))
101264a6e74Sfrankho 		return (29);
102264a6e74Sfrankho 	else
103264a6e74Sfrankho 		return (daysinmonth[m-1]);
1047c478bd9Sstevel@tonic-gate }
1057c478bd9Sstevel@tonic-gate 
106264a6e74Sfrankho struct pcfs_args pc_tz; /* this is set by pcfs_mount */
107264a6e74Sfrankho 
1087c478bd9Sstevel@tonic-gate /*
109264a6e74Sfrankho  * Convert time from UNIX to DOS format.
110264a6e74Sfrankho  * Return EOVERFLOW in case no valid DOS time representation
111264a6e74Sfrankho  * exists for the given UNIX time.
1127c478bd9Sstevel@tonic-gate  */
113264a6e74Sfrankho int
114264a6e74Sfrankho pc_tvtopct(
115264a6e74Sfrankho 	timestruc_t	*tvp,		/* UNIX time input */
116264a6e74Sfrankho 	struct pctime *pctp)		/* pctime output */
1177c478bd9Sstevel@tonic-gate {
118264a6e74Sfrankho 	uint_t year, month, day, hour, min, sec;
119264a6e74Sfrankho 	int64_t unixtime;
120264a6e74Sfrankho 
121264a6e74Sfrankho 	unixtime = (int64_t)tvp->tv_sec;
122264a6e74Sfrankho 	unixtime -= YZ_SECS;
123264a6e74Sfrankho 	unixtime -= pc_tz.secondswest;
124264a6e74Sfrankho 	if (unixtime <= 0) {
125264a6e74Sfrankho 		/*
126264a6e74Sfrankho 		 * "before beginning of all time" for DOS ...
127264a6e74Sfrankho 		 */
128264a6e74Sfrankho 		return (EOVERFLOW);
129264a6e74Sfrankho 	}
130264a6e74Sfrankho 	for (year = YEAR_ZERO; unixtime >= days_in_year(year) * 86400;
131264a6e74Sfrankho 	    year++)
132264a6e74Sfrankho 		unixtime -= 86400 * days_in_year(year);
133264a6e74Sfrankho 
134264a6e74Sfrankho 	if (year > 127 + YEAR_ZERO) {
135264a6e74Sfrankho 		/*
136264a6e74Sfrankho 		 * "past end of all time" for DOS - can happen
137264a6e74Sfrankho 		 * on a 64bit kernel via utimes() syscall ...
138264a6e74Sfrankho 		 */
139264a6e74Sfrankho 		return (EOVERFLOW);
140264a6e74Sfrankho 	}
1417c478bd9Sstevel@tonic-gate 
142264a6e74Sfrankho 	for (month = 1; unixtime >= 86400 * days_in_month(month, year);
143264a6e74Sfrankho 	    month++)
144264a6e74Sfrankho 		unixtime -= 86400 * days_in_month(month, year);
1457c478bd9Sstevel@tonic-gate 
146264a6e74Sfrankho 	year -= YEAR_ZERO;
1477c478bd9Sstevel@tonic-gate 
148264a6e74Sfrankho 	day = (int)(unixtime / 86400);
149264a6e74Sfrankho 	unixtime -= 86400 * day++;	/* counting starts at 1 */
1507c478bd9Sstevel@tonic-gate 
151264a6e74Sfrankho 	hour = (int)(unixtime / 3600);
152264a6e74Sfrankho 	unixtime -= 3600 * hour;
1537c478bd9Sstevel@tonic-gate 
154264a6e74Sfrankho 	min = (int)(unixtime / 60);
155264a6e74Sfrankho 	unixtime -= 60 * min;
1567c478bd9Sstevel@tonic-gate 
157264a6e74Sfrankho 	sec = (int)unixtime;
1587c478bd9Sstevel@tonic-gate 
159264a6e74Sfrankho 	PC_DPRINTF3(1, "ux2pc date: %d.%d.%d\n", day, month, YEAR_ZERO + year);
160264a6e74Sfrankho 	PC_DPRINTF3(1, "ux2pc time: %dh%dm%ds\n", hour, min, sec);
161264a6e74Sfrankho 	PC_DPRINTF1(1, "ux2pc unixtime: %lld\n", (long long)(unixtime));
1627c478bd9Sstevel@tonic-gate 
163264a6e74Sfrankho 	ASSERT(year >= 0 && year < 128);
164264a6e74Sfrankho 	ASSERT(month >= 1 && month <= 12);
165264a6e74Sfrankho 	ASSERT(day >= 1 && day <= days_in_month(month, year));
166264a6e74Sfrankho 	ASSERT(hour < 24);
167264a6e74Sfrankho 	ASSERT(min < 60);
168264a6e74Sfrankho 	ASSERT(sec < 60);
169264a6e74Sfrankho 
170264a6e74Sfrankho 	pctp->pct_time =
171264a6e74Sfrankho 	    LE_16(hour << HOURSHIFT | min << MINSHIFT | (sec / 2) << SECSHIFT);
172264a6e74Sfrankho 	pctp->pct_date =
173264a6e74Sfrankho 	    LE_16(year << YEARSHIFT | month << MONSHIFT | day << DAYSHIFT);
174264a6e74Sfrankho 
175264a6e74Sfrankho 	return (0);
1767c478bd9Sstevel@tonic-gate }
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate /*
179264a6e74Sfrankho  * Convert time from DOS to UNIX time format.
180264a6e74Sfrankho  * Since FAT timestamps cannot be expressed in 32bit time_t,
181264a6e74Sfrankho  * the calculation is performed using 64bit values. It's up to
182264a6e74Sfrankho  * the caller to decide what to do for out-of-UNIX-range values.
1837c478bd9Sstevel@tonic-gate  */
184264a6e74Sfrankho void
185264a6e74Sfrankho pc_pcttotv(
186264a6e74Sfrankho 	struct pctime *pctp,		/* DOS time input */
187264a6e74Sfrankho 	int64_t *unixtime)		/* caller converts to time_t */
1887c478bd9Sstevel@tonic-gate {
189264a6e74Sfrankho 	uint_t year, month, day, hour, min, sec;
1907c478bd9Sstevel@tonic-gate 
191264a6e74Sfrankho 	sec = 2 * ((LE_16(pctp->pct_time) >> SECSHIFT) & SECMASK);
192264a6e74Sfrankho 	min = (LE_16(pctp->pct_time) >> MINSHIFT) & MINMASK;
193264a6e74Sfrankho 	hour = (LE_16(pctp->pct_time) >> HOURSHIFT) & HOURMASK;
194264a6e74Sfrankho 	day = (LE_16(pctp->pct_date) >> DAYSHIFT) & DAYMASK;
195264a6e74Sfrankho 	month = (LE_16(pctp->pct_date) >> MONSHIFT) & MONMASK;
196264a6e74Sfrankho 	year = (LE_16(pctp->pct_date) >> YEARSHIFT) & YEARMASK;
197264a6e74Sfrankho 	year += YEAR_ZERO;
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	/*
200264a6e74Sfrankho 	 * Basic sanity checks. The FAT timestamp bitfields allow for
201264a6e74Sfrankho 	 * impossible dates/times - return the "FAT epoch" for these.
2027c478bd9Sstevel@tonic-gate 	 */
203264a6e74Sfrankho 	if (pctp->pct_date == 0) {
204264a6e74Sfrankho 		year = YEAR_ZERO;
205264a6e74Sfrankho 		month = 1;
206264a6e74Sfrankho 		day = 1;
207264a6e74Sfrankho 	}
208264a6e74Sfrankho 	if (month > 12 || month < 1 ||
209264a6e74Sfrankho 	    day < 1 || day > days_in_month(month, year) ||
210264a6e74Sfrankho 	    hour > 23 || min > 59 || sec > 59) {
211264a6e74Sfrankho 		cmn_err(CE_NOTE, "impossible FAT timestamp, "
212264a6e74Sfrankho 		    "d/m/y %d/%d/%d, h:m:s %d:%d:%d",
213264a6e74Sfrankho 		    day, month, year, hour, min, sec);
214264a6e74Sfrankho 		*unixtime = YZ_SECS + pc_tz.secondswest;
215264a6e74Sfrankho 		return;
2167c478bd9Sstevel@tonic-gate 	}
2177c478bd9Sstevel@tonic-gate 
218264a6e74Sfrankho 	PC_DPRINTF3(1, "pc2ux date: %d.%d.%d\n", day, month, year);
219264a6e74Sfrankho 	PC_DPRINTF3(1, "pc2ux time: %dh%dm%ds\n", hour, min, sec);
2207c478bd9Sstevel@tonic-gate 
221264a6e74Sfrankho 	*unixtime = (int64_t)sec;
222264a6e74Sfrankho 	*unixtime += 60 * (int64_t)min;
223264a6e74Sfrankho 	*unixtime += 3600 * (int64_t)hour;
224*4d28e670Sbatschul 	*unixtime += 86400 * (int64_t)(day -1);
225264a6e74Sfrankho 	while (month > 1) {
226264a6e74Sfrankho 		month--;
227264a6e74Sfrankho 		*unixtime += 86400 * (int64_t)days_in_month(month, year);
228264a6e74Sfrankho 	}
229264a6e74Sfrankho 	while (year > YEAR_ZERO) {
230264a6e74Sfrankho 		year--;
231*4d28e670Sbatschul 		*unixtime += 86400 * (int64_t)days_in_year(year);
232264a6e74Sfrankho 	}
2337c478bd9Sstevel@tonic-gate 	/*
234264a6e74Sfrankho 	 * For FAT, the beginning of all time is 01/01/1980,
235264a6e74Sfrankho 	 * and years are counted relative to that.
236264a6e74Sfrankho 	 * We adjust this base value by the timezone offset
237264a6e74Sfrankho 	 * that is passed in to pcfs at mount time.
2387c478bd9Sstevel@tonic-gate 	 */
239264a6e74Sfrankho 	*unixtime += YZ_SECS;
240264a6e74Sfrankho 	*unixtime += pc_tz.secondswest;
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	/*
243264a6e74Sfrankho 	 * FAT epoch is past UNIX epoch - negative UNIX times
244264a6e74Sfrankho 	 * cannot result from the conversion.
2457c478bd9Sstevel@tonic-gate 	 */
246264a6e74Sfrankho 	ASSERT(*unixtime > 0);
247264a6e74Sfrankho 	PC_DPRINTF1(1, "pc2ux unixtime: %lld\n", (long long)(*unixtime));
2487c478bd9Sstevel@tonic-gate }
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate /*
2514a37d755Sksn  * Determine whether a character is valid for a long file name.
2524a37d755Sksn  * It is easier to determine by filtering out invalid characters.
2534a37d755Sksn  * Following are invalid characters in a long filename.
2544a37d755Sksn  *	/ \ : * ? < > | "
2557c478bd9Sstevel@tonic-gate  */
2567c478bd9Sstevel@tonic-gate int
2577c478bd9Sstevel@tonic-gate pc_valid_lfn_char(char c)
2587c478bd9Sstevel@tonic-gate {
2594a37d755Sksn 	const char *cp;
2607c478bd9Sstevel@tonic-gate 	int n;
2614a37d755Sksn 
2624a37d755Sksn 	static const char invaltab[] = {
2634a37d755Sksn 		"/\\:*?<>|\""
2647c478bd9Sstevel@tonic-gate 	};
2657c478bd9Sstevel@tonic-gate 
2664a37d755Sksn 	cp = invaltab;
2674a37d755Sksn 	n = sizeof (invaltab) - 1;
2687c478bd9Sstevel@tonic-gate 	while (n--) {
2697c478bd9Sstevel@tonic-gate 		if (c == *cp++)
2704a37d755Sksn 			return (0);
2717c478bd9Sstevel@tonic-gate 	}
2724a37d755Sksn 	return (1);
2737c478bd9Sstevel@tonic-gate }
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate int
2764a37d755Sksn pc_valid_long_fn(char *namep, int utf8)
2777c478bd9Sstevel@tonic-gate {
2787c478bd9Sstevel@tonic-gate 	char *tmp;
2797c478bd9Sstevel@tonic-gate 
2804a37d755Sksn 	if (utf8) {
2814a37d755Sksn 		/* UTF-8 */
2824a37d755Sksn 		for (tmp = namep; *tmp != '\0'; tmp++)
2834a37d755Sksn 			if (!pc_valid_lfn_char(*tmp))
2844a37d755Sksn 				return (0);
2854a37d755Sksn 		if ((tmp - namep) > PCMAXNAMLEN)
2867c478bd9Sstevel@tonic-gate 			return (0);
2874a37d755Sksn 	} else {
2884a37d755Sksn 		/* UTF-16 */
2894a37d755Sksn 		for (tmp = namep; (*tmp != '\0') && (*(tmp+1) != '\0');
2904a37d755Sksn 		    tmp += 2) {
2914a37d755Sksn 			if ((*(tmp+1) == '\0') && !pc_valid_lfn_char(*tmp))
2924a37d755Sksn 				return (0);
2934a37d755Sksn 		}
2944a37d755Sksn 		if ((tmp - namep) > (PCMAXNAMLEN * sizeof (uint16_t)))
2954a37d755Sksn 			return (0);
2964a37d755Sksn 	}
2977c478bd9Sstevel@tonic-gate 	return (1);
2987c478bd9Sstevel@tonic-gate }
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate int
3017c478bd9Sstevel@tonic-gate pc_fname_ext_to_name(char *namep, char *fname, char *ext, int foldcase)
3027c478bd9Sstevel@tonic-gate {
3037c478bd9Sstevel@tonic-gate 	int	i;
3047c478bd9Sstevel@tonic-gate 	char	*tp = namep;
3057c478bd9Sstevel@tonic-gate 	char	c;
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 	i = PCFNAMESIZE;
3087c478bd9Sstevel@tonic-gate 	while (i-- && ((c = *fname) != ' ')) {
3097c478bd9Sstevel@tonic-gate 		if (!(c == '.' || pc_validchar(c))) {
3107c478bd9Sstevel@tonic-gate 			return (-1);
3117c478bd9Sstevel@tonic-gate 		}
3127c478bd9Sstevel@tonic-gate 		if (foldcase)
3137c478bd9Sstevel@tonic-gate 			*tp++ = tolower(c);
3147c478bd9Sstevel@tonic-gate 		else
3157c478bd9Sstevel@tonic-gate 			*tp++ = c;
3167c478bd9Sstevel@tonic-gate 		fname++;
3177c478bd9Sstevel@tonic-gate 	}
3187c478bd9Sstevel@tonic-gate 	if (*ext != ' ') {
3197c478bd9Sstevel@tonic-gate 		*tp++ = '.';
3207c478bd9Sstevel@tonic-gate 		i = PCFEXTSIZE;
3217c478bd9Sstevel@tonic-gate 		while (i-- && ((c = *ext) != ' ')) {
3227c478bd9Sstevel@tonic-gate 			if (!pc_validchar(c)) {
3237c478bd9Sstevel@tonic-gate 				return (-1);
3247c478bd9Sstevel@tonic-gate 			}
3257c478bd9Sstevel@tonic-gate 			if (foldcase)
3267c478bd9Sstevel@tonic-gate 				*tp++ = tolower(c);
3277c478bd9Sstevel@tonic-gate 			else
3287c478bd9Sstevel@tonic-gate 				*tp++ = c;
3297c478bd9Sstevel@tonic-gate 			ext++;
3307c478bd9Sstevel@tonic-gate 		}
3317c478bd9Sstevel@tonic-gate 	}
3327c478bd9Sstevel@tonic-gate 	*tp = '\0';
3337c478bd9Sstevel@tonic-gate 	return (0);
3347c478bd9Sstevel@tonic-gate }
335