xref: /illumos-gate/usr/src/uts/common/fs/pcfs/pc_subr.c (revision 7c478bd9)
1 /*
2  * Copyright (c) 1989, 1998 by Sun Microsystems, Inc.
3  * All rights reserved.
4  */
5 
6 /*
7  * Copyright (c) 1980 Regents of the University of California.
8  * All rights reserved. The Berkeley software License Agreement
9  * specifies the terms and conditions for redistribution.
10  */
11 
12 #pragma ident	"%Z%%M%	%I%	%E% SMI"
13 
14 #ifndef KERNEL
15 #define	KERNEL
16 #endif
17 
18 #include <sys/param.h>
19 #include <sys/time.h>
20 #include <sys/buf.h>
21 #include <sys/conf.h>
22 #include <sys/sysmacros.h>
23 #include <sys/kmem.h>
24 #include <sys/vfs.h>
25 #include <sys/debug.h>
26 #include <sys/errno.h>
27 #include <sys/fs/pc_fs.h>
28 #include <sys/fs/pc_label.h>
29 #include <sys/fs/pc_dir.h>
30 #include <sys/fs/pc_node.h>
31 
32 /*
33  * Structure returned by gmtime and localtime calls (see ctime(3)).
34  */
35 struct tm {
36 	short	tm_sec;
37 	short	tm_min;
38 	short	tm_hour;
39 	short	tm_mday;
40 	short	tm_mon;
41 	short	tm_year;
42 	short	tm_wday;
43 	short	tm_yday;
44 	short	tm_isdst;
45 };
46 
47 void pc_tvtopct(timestruc_t *, struct pctime *);
48 void pc_pcttotv(struct pctime *, timestruc_t *);
49 int pc_validchar(char);
50 
51 static struct tm *localtime(time_t *tim);
52 static int sunday(struct tm *, int);
53 static int dysize(int);
54 static struct tm *gmtime(int);
55 static time_t ctime(struct tm *);
56 
57 /* The cm struct defines tm_year relative to 1900 */
58 #define	YEAR_ZERO	1900
59 
60 /*
61  * convert timestruct to pctime
62  */
63 void
64 pc_tvtopct(
65 	timestruc_t	*tvp,			/* time input */
66 	struct pctime *pctp)		/* pctime output */
67 {
68 	struct tm *ctp;
69 
70 	ctp = localtime(&tvp->tv_sec);
71 #define	setfield(S, FIELD, SFT, MSK)	\
72 	S = (ltohs(S) & ~(MSK << SFT)) | (((FIELD) & MSK) << SFT); S = htols(S);
73 
74 	setfield(pctp->pct_time, ctp->tm_sec / 2, SECSHIFT, SECMASK);
75 	setfield(pctp->pct_time, ctp->tm_min, MINSHIFT, MINMASK);
76 	setfield(pctp->pct_time, ctp->tm_hour, HOURSHIFT, HOURMASK);
77 	setfield(pctp->pct_date, ctp->tm_mday, DAYSHIFT, DAYMASK);
78 	setfield(pctp->pct_date, ctp->tm_mon + 1, MONSHIFT, MONMASK);
79 	setfield(pctp->pct_date, ctp->tm_year - 80, YEARSHIFT, YEARMASK);
80 #undef setfield
81 }
82 
83 /*
84  * convert pctime to timeval
85  */
86 void
87 pc_pcttotv(
88 	struct pctime *pctp,		/* ptime input */
89 	timestruc_t *tvp)		/* tinmeval output */
90 {
91 	struct tm tm;
92 
93 #define	getfield(S, SFT, M)	(((int)(ltohs(S)) >> SFT) & M)
94 	tm.tm_sec = getfield(pctp->pct_time, SECSHIFT, SECMASK) * 2;
95 	tm.tm_min = getfield(pctp->pct_time, MINSHIFT, MINMASK);
96 	tm.tm_hour = getfield(pctp->pct_time, HOURSHIFT, HOURMASK);
97 	tm.tm_mday =  getfield(pctp->pct_date, DAYSHIFT, DAYMASK);
98 	tm.tm_mon = getfield(pctp->pct_date, MONSHIFT, MONMASK) - 1;
99 	tm.tm_year = 80 + getfield(pctp->pct_date, YEARSHIFT, YEARMASK);
100 #undef getfield
101 	tvp->tv_nsec = 0;
102 	tvp->tv_sec = ctime(&tm);
103 }
104 
105 /*
106  * This routine converts time as follows.
107  * The epoch is 0000 Jan 1 1970 GMT.
108  * The argument time is in seconds since then.
109  * The localtime(t) entry returns a pointer to an array
110  * containing
111  *  seconds (0-59)
112  *  minutes (0-59)
113  *  hours (0-23)
114  *  day of month (1-31)
115  *  month (0-11)
116  *  year-1900
117  *  weekday (0-6, Sun is 0)
118  *  day of the year
119  *  daylight savings flag
120  *
121  * The routine calls the system to determine the local
122  * timezone and whether Daylight Saving Time is permitted locally.
123  * (DST is then determined by the current local rules)
124  *
125  * The routine does not work
126  * in Saudi Arabia which runs on Solar time.
127  *
128  */
129 
130 static	int	dmsize[12] =
131 {
132 	31,
133 	28,
134 	31,
135 	30,
136 	31,
137 	30,
138 	31,
139 	31,
140 	30,
141 	31,
142 	30,
143 	31
144 };
145 
146 /*
147  * The following table is used for 1974 and 1975 and
148  * gives the day number of the first day after the Sunday of the
149  * change.
150  */
151 struct dstab {
152 	int	dayyr;
153 	int	daylb;
154 	int	dayle;
155 };
156 
157 static struct dstab usdaytab[] = {
158 	1974,	5,	333,	/* 1974: Jan 6 - last Sun. in Nov */
159 	1975,	58,	303,	/* 1975: Last Sun. in Feb - last Sun in Oct */
160 	0,	119,	303,	/* all other years: end Apr - end Oct */
161 };
162 static struct dstab ausdaytab[] = {
163 	1970,	400,	0,	/* 1970: no daylight saving at all */
164 	1971,	303,	0,	/* 1971: daylight saving from Oct 31 */
165 	1972,	303,	58,	/* 1972: Jan 1 -> Feb 27 & Oct 31 -> dec 31 */
166 	0,	303,	65,	/* others: -> Mar 7, Oct 31 -> */
167 };
168 
169 /*
170  * The European tables ... based on hearsay
171  * Believed correct for:
172  *	WE:	Great Britain, Ireland, Portugal
173  *	ME:	Belgium, Luxembourg, Netherlands, Denmark, Norway,
174  *		Austria, Poland, Czechoslovakia, Sweden, Switzerland,
175  *		DDR, DBR, France, Spain, Hungary, Italy, Jugoslavia
176  * Eastern European dst is unknown, we'll make it ME until someone speaks up.
177  *	EE:	Bulgaria, Finland, Greece, Rumania, Turkey, Western Russia
178  */
179 static struct dstab wedaytab[] = {
180 	1983,	86,	303,	/* 1983: end March - end Oct */
181 	1984,	86,	303,	/* 1984: end March - end Oct */
182 	1985,	86,	303,	/* 1985: end March - end Oct */
183 	0,	400,	0,	/* others: no daylight saving at all ??? */
184 };
185 
186 static struct dstab medaytab[] = {
187 	1983,	86,	272,	/* 1983: end March - end Sep */
188 	1984,	86,	272,	/* 1984: end March - end Sep */
189 	1985,	86,	272,	/* 1985: end March - end Sep */
190 	0,	400,	0,	/* others: no daylight saving at all ??? */
191 };
192 
193 static struct dayrules {
194 	int		dst_type;	/* number obtained from system */
195 	int		dst_hrs;	/* hours to add when dst on */
196 	struct	dstab	*dst_rules;	/* one of the above */
197 	enum {STH, NTH}	dst_hemi;	/* southern, northern hemisphere */
198 } dayrules [] = {
199 	DST_USA,	1,	usdaytab,	NTH,
200 	DST_AUST,	1,	ausdaytab,	STH,
201 	DST_WET,	1,	wedaytab,	NTH,
202 	DST_MET,	1,	medaytab,	NTH,
203 	DST_EET,	1,	medaytab,	NTH,	/* XXX */
204 	-1,
205 };
206 
207 struct pcfs_args pc_tz; /* this is set by pcfs_mount */
208 
209 static struct tm *
210 localtime(time_t *tim)
211 {
212 	int dayno;
213 	struct tm *ct;
214 	int	dalybeg, daylend;
215 	struct dayrules *dr;
216 	struct dstab *ds;
217 	int year;
218 	int copyt;
219 
220 	copyt = *tim - (int)pc_tz.secondswest;
221 	ct = gmtime(copyt);
222 	dayno = ct->tm_yday;
223 	for (dr = dayrules; dr->dst_type >= 0; dr++)
224 		if (dr->dst_type == pc_tz.dsttime)
225 			break;
226 	if (dr->dst_type >= 0) {
227 		year = ct->tm_year + 1900;
228 		for (ds = dr->dst_rules; ds->dayyr; ds++) {
229 			if (ds->dayyr == year) {
230 				break;
231 			}
232 		}
233 		dalybeg = ds->daylb;	/* first Sun after dst starts */
234 		daylend = ds->dayle;	/* first Sun after dst ends */
235 		dalybeg = sunday(ct, dalybeg);
236 		daylend = sunday(ct, daylend);
237 		switch (dr->dst_hemi) {
238 		case NTH:
239 			if (!(
240 			    (dayno > dalybeg ||
241 			    (dayno == dalybeg && ct->tm_hour >= 2)) &&
242 			    (dayno < daylend ||
243 			    (dayno == daylend && ct->tm_hour < 1)))) {
244 				return (ct);
245 			}
246 			break;
247 		case STH:
248 			if (!(
249 			    (dayno > dalybeg ||
250 			    (dayno == dalybeg && ct->tm_hour >= 2)) ||
251 			    (dayno < daylend ||
252 			    (dayno == daylend && ct->tm_hour < 2)))) {
253 				return (ct);
254 			}
255 			break;
256 		default:
257 		    return (ct);
258 		}
259 		copyt += dr->dst_hrs*60*60;
260 		ct = gmtime(copyt);
261 		ct->tm_isdst++;
262 	}
263 	return (ct);
264 }
265 
266 /*
267  * The argument is a 0-origin day number.
268  * The value is the day number of the first
269  * Sunday on or after the day.
270  */
271 static int
272 sunday(struct tm *t, int d)
273 {
274 	if (d >= 58)
275 		d += dysize(YEAR_ZERO + t->tm_year) - 365;
276 	return (d - (d - t->tm_yday + t->tm_wday + 700) % 7);
277 }
278 
279 static int
280 dysize(int y)
281 {
282 	if (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
283 		return (366);
284 	return (365);
285 }
286 
287 static struct tm *
288 gmtime(int tim)
289 {
290 	int d0, d1;
291 	int hms, day;
292 	short *tp;
293 	static struct tm xtime;
294 
295 	/*
296 	 * break initial number into days
297 	 */
298 	hms = tim % 86400;
299 	day = tim / 86400;
300 	if (hms < 0) {
301 		hms += 86400;
302 		day -= 1;
303 	}
304 	tp = (short *)&xtime;
305 
306 	/*
307 	 * generate hours:minutes:seconds
308 	 */
309 	*tp++ = hms%60;
310 	d1 = hms/60;
311 	*tp++ = d1%60;
312 	d1 /= 60;
313 	*tp++ = (short)d1;
314 
315 	/*
316 	 * day is the day number.
317 	 * generate day of the week.
318 	 * The addend is 4 mod 7 (1/1/1970 was Thursday)
319 	 */
320 
321 	xtime.tm_wday = (day+7340036)%7;
322 
323 	/*
324 	 * year number
325 	 */
326 	if (day >= 0)
327 		for (d1 = 70; day >= dysize(YEAR_ZERO + d1); d1++)
328 			day -= dysize(YEAR_ZERO + d1);
329 	else
330 		for (d1 = 70; day < 0; d1--)
331 			day += dysize(YEAR_ZERO + d1 - 1);
332 	xtime.tm_year = (short)d1;
333 	xtime.tm_yday = d0 = day;
334 
335 	/*
336 	 * generate month
337 	 */
338 
339 	if (dysize(YEAR_ZERO + d1) == 366)
340 		dmsize[1] = 29;
341 	for (d1 = 0; d0 >= dmsize[d1]; d1++)
342 		d0 -= dmsize[d1];
343 	dmsize[1] = 28;
344 	*tp++ = d0+1;
345 	*tp++ = (short)d1;
346 	xtime.tm_isdst = 0;
347 	return (&xtime);
348 }
349 
350 /*
351  * convert year, month, day, hour, minute, sec to (int)time.
352  */
353 static time_t
354 ctime(struct tm *tp)
355 {
356 	int i;
357 	time_t ct;
358 
359 	if (tp->tm_mon < 0 || tp->tm_mon > 11 ||
360 	    tp->tm_mday < 1 || tp->tm_mday > 31 ||
361 	    tp->tm_hour < 0 || tp->tm_hour > 23 ||
362 	    tp->tm_min < 0 || tp->tm_min > 59 ||
363 	    tp->tm_sec < 0 || tp->tm_sec > 59) {
364 		return (0);
365 	}
366 	ct = 0;
367 	for (i = /* 19 */ 70; i < tp->tm_year; i++)
368 		ct += dysize(YEAR_ZERO + i);
369 	/* Leap year */
370 	if (dysize(YEAR_ZERO + tp->tm_year) == 366 && tp->tm_mon >= 2)
371 		ct++;
372 	i = tp->tm_mon + 1;
373 	while (--i)
374 		ct += dmsize[i-1];
375 	ct += tp->tm_mday-1;
376 	ct = 24*ct + tp->tm_hour;
377 	ct = 60*ct + tp->tm_min;
378 	ct = 60*ct + tp->tm_sec;
379 	/* convert to GMT assuming local time */
380 	ct += (int)pc_tz.secondswest;
381 	/* now fix up local daylight time */
382 	if (localtime(&ct)->tm_isdst)
383 		ct -= 60*60;
384 	return (ct);
385 }
386 
387 /*
388  * Determine whether a character is valid for a pc 8.3 file system file name.
389  * The Windows 95 Resource Kit claims that these are valid:
390  *	uppercase letters and numbers
391  *	blank
392  *	ASCII characters greater than 127
393  *	$%'-_@~`!()^#&
394  * Long file names can also have
395  *	lowercase letters
396  *	+,;=[]
397  */
398 int
399 pc_validchar(char c)
400 {
401 	char *cp;
402 	int n;
403 	static char valtab[] = {
404 		"$#&@!%()-{}<>`_^~|' "
405 	};
406 
407 	/*
408 	 * Should be "$#&@!%()-{}`_^~' " ??
409 	 * From experiment in DOSWindows, *+=|\[];:",<>.?/ are illegal.
410 	 * See IBM DOS4.0 Tech Ref. B-57.
411 	 */
412 
413 	if (c >= 'A' && c <= 'Z')
414 		return (1);
415 	if (c >= '0' && c <= '9')
416 		return (1);
417 	cp = valtab;
418 	n = sizeof (valtab);
419 	while (n--) {
420 		if (c == *cp++)
421 			return (1);
422 	}
423 	return (0);
424 }
425 
426 /*
427  * Determine whether a character is valid for a pc 8.3 file system file name.
428  * The Windows 95 Resource Kit claims that these are valid:
429  *	uppercase letters and numbers
430  *	blank
431  *	ASCII characters greater than 127
432  *	$%'-_@~`!()^#&
433  * Long file names can also have
434  *	lowercase letters
435  *	+,;=[].
436  */
437 int
438 pc_valid_lfn_char(char c)
439 {
440 	char *cp;
441 	int n;
442 	static char valtab[] = {
443 		"+,;=[].$#&@!%()-{}<>`_^~|' "
444 	};
445 
446 	if (c >= 'a' && c <= 'z')
447 		return (1);
448 	if (c >= 'A' && c <= 'Z')
449 		return (1);
450 	if (c >= '0' && c <= '9')
451 		return (1);
452 	cp = valtab;
453 	n = sizeof (valtab);
454 	while (n--) {
455 		if (c == *cp++)
456 			return (1);
457 	}
458 	return (0);
459 }
460 
461 int
462 pc_valid_long_fn(char *namep)
463 {
464 	char *tmp;
465 
466 	for (tmp = namep; *tmp != '\0'; tmp++)
467 		if (!pc_valid_lfn_char(*tmp))
468 			return (0);
469 	if ((tmp - namep) >= PCMAXNAMLEN)
470 		return (0);
471 	return (1);
472 }
473 
474 int
475 pc_fname_ext_to_name(char *namep, char *fname, char *ext, int foldcase)
476 {
477 	int	i;
478 	char	*tp = namep;
479 	char	c;
480 
481 	i = PCFNAMESIZE;
482 	while (i-- && ((c = *fname) != ' ')) {
483 		if (!(c == '.' || pc_validchar(c))) {
484 			return (-1);
485 		}
486 		if (foldcase)
487 			*tp++ = tolower(c);
488 		else
489 			*tp++ = c;
490 		fname++;
491 	}
492 	if (*ext != ' ') {
493 		*tp++ = '.';
494 		i = PCFEXTSIZE;
495 		while (i-- && ((c = *ext) != ' ')) {
496 			if (!pc_validchar(c)) {
497 				return (-1);
498 			}
499 			if (foldcase)
500 				*tp++ = tolower(c);
501 			else
502 				*tp++ = c;
503 			ext++;
504 		}
505 	}
506 	*tp = '\0';
507 	return (0);
508 }
509