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 /*
224d28e670Sbatschul * 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 #ifndef KERNEL
337c478bd9Sstevel@tonic-gate #define KERNEL
347c478bd9Sstevel@tonic-gate #endif
357c478bd9Sstevel@tonic-gate
367c478bd9Sstevel@tonic-gate #include <sys/param.h>
377c478bd9Sstevel@tonic-gate #include <sys/time.h>
387c478bd9Sstevel@tonic-gate #include <sys/conf.h>
397c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
407c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
417c478bd9Sstevel@tonic-gate #include <sys/debug.h>
427c478bd9Sstevel@tonic-gate #include <sys/errno.h>
43264a6e74Sfrankho #include <sys/cmn_err.h>
44264a6e74Sfrankho #include <sys/ddi.h>
45264a6e74Sfrankho #include <sys/sunddi.h>
46264a6e74Sfrankho #include <sys/byteorder.h>
47*5f079001SOwen Roberts #include <sys/types.h>
487c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
497c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
507c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
517c478bd9Sstevel@tonic-gate #include <sys/fs/pc_node.h>
527c478bd9Sstevel@tonic-gate
537c478bd9Sstevel@tonic-gate /*
54264a6e74Sfrankho * Convert time between DOS formats:
55264a6e74Sfrankho * - years since 1980
56264a6e74Sfrankho * - months/days/hours/minutes/seconds, local TZ
57264a6e74Sfrankho * and the UNIX format (seconds since 01/01/1970, 00:00:00 UT).
58264a6e74Sfrankho *
59264a6e74Sfrankho * Timezones are adjusted for via mount option arg (secondswest),
60264a6e74Sfrankho * but daylight savings time corrections are not made. Calculated
61264a6e74Sfrankho * time may therefore end up being wrong by an hour, but this:
62264a6e74Sfrankho * a) will happen as well if media is interchanged between
63264a6e74Sfrankho * two DOS/Windows-based systems that use different
64264a6e74Sfrankho * timezone settings
65264a6e74Sfrankho * b) is the best option we have unless we decide to put
66264a6e74Sfrankho * a full ctime(3C) framework into the kernel, including
67264a6e74Sfrankho * all conversion tables - AND keeping them current ...
687c478bd9Sstevel@tonic-gate */
697c478bd9Sstevel@tonic-gate
70264a6e74Sfrankho int pc_tvtopct(timestruc_t *, struct pctime *);
71264a6e74Sfrankho void pc_pcttotv(struct pctime *, int64_t *);
727c478bd9Sstevel@tonic-gate
737c478bd9Sstevel@tonic-gate /*
74264a6e74Sfrankho * Macros/Definitons required to convert between DOS-style and
75264a6e74Sfrankho * UNIX-style time recording.
76264a6e74Sfrankho * DOS year zero is 1980.
777c478bd9Sstevel@tonic-gate */
78264a6e74Sfrankho static int daysinmonth[] =
79264a6e74Sfrankho { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
80264a6e74Sfrankho
81264a6e74Sfrankho #define YEAR_ZERO 1980
82264a6e74Sfrankho #define YZ_SECS (((8 * 365) + (2 * 366)) * 86400)
83264a6e74Sfrankho #define FAT_ENDOFTIME \
84264a6e74Sfrankho LE_16(23 << HOURSHIFT | 59 << MINSHIFT | (59/2) << SECSHIFT)
85264a6e74Sfrankho #define FAT_ENDOFDATE \
86264a6e74Sfrankho LE_16(127 << YEARSHIFT | 12 << MONSHIFT | 31 << DAYSHIFT)
87264a6e74Sfrankho #define leap_year(y) \
88264a6e74Sfrankho (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
89264a6e74Sfrankho
90*5f079001SOwen Roberts #define YEN "\xc2\xa5" /* Yen Sign UTF-8 character */
91*5f079001SOwen Roberts #define LRO "\xe2\x80\xad" /* Left-To-Right Override UTF-8 character */
92*5f079001SOwen Roberts #define RLO "\xe2\x80\xae" /* Right-To-Left Override UTF-8 character */
93*5f079001SOwen Roberts
94264a6e74Sfrankho static int
days_in_year(int y)95264a6e74Sfrankho days_in_year(int y)
967c478bd9Sstevel@tonic-gate {
97264a6e74Sfrankho return (leap_year((y)) ? 366 : 365);
987c478bd9Sstevel@tonic-gate }
997c478bd9Sstevel@tonic-gate
100264a6e74Sfrankho static int
days_in_month(int m,int y)101264a6e74Sfrankho days_in_month(int m, int y)
1027c478bd9Sstevel@tonic-gate {
103264a6e74Sfrankho if (m == 2 && leap_year(y))
104264a6e74Sfrankho return (29);
105264a6e74Sfrankho else
106264a6e74Sfrankho return (daysinmonth[m-1]);
1077c478bd9Sstevel@tonic-gate }
1087c478bd9Sstevel@tonic-gate
1097c478bd9Sstevel@tonic-gate struct pcfs_args pc_tz; /* this is set by pcfs_mount */
1107c478bd9Sstevel@tonic-gate
1117c478bd9Sstevel@tonic-gate /*
112264a6e74Sfrankho * Convert time from UNIX to DOS format.
113264a6e74Sfrankho * Return EOVERFLOW in case no valid DOS time representation
114264a6e74Sfrankho * exists for the given UNIX time.
1157c478bd9Sstevel@tonic-gate */
1167c478bd9Sstevel@tonic-gate int
pc_tvtopct(timestruc_t * tvp,struct pctime * pctp)117264a6e74Sfrankho pc_tvtopct(
118264a6e74Sfrankho timestruc_t *tvp, /* UNIX time input */
119264a6e74Sfrankho struct pctime *pctp) /* pctime output */
1207c478bd9Sstevel@tonic-gate {
121264a6e74Sfrankho uint_t year, month, day, hour, min, sec;
122264a6e74Sfrankho int64_t unixtime;
123264a6e74Sfrankho
124264a6e74Sfrankho unixtime = (int64_t)tvp->tv_sec;
125264a6e74Sfrankho unixtime -= YZ_SECS;
126264a6e74Sfrankho unixtime -= pc_tz.secondswest;
127264a6e74Sfrankho if (unixtime <= 0) {
128264a6e74Sfrankho /*
129264a6e74Sfrankho * "before beginning of all time" for DOS ...
130264a6e74Sfrankho */
131264a6e74Sfrankho return (EOVERFLOW);
132264a6e74Sfrankho }
133264a6e74Sfrankho for (year = YEAR_ZERO; unixtime >= days_in_year(year) * 86400;
134264a6e74Sfrankho year++)
135264a6e74Sfrankho unixtime -= 86400 * days_in_year(year);
136264a6e74Sfrankho
137264a6e74Sfrankho if (year > 127 + YEAR_ZERO) {
138264a6e74Sfrankho /*
139264a6e74Sfrankho * "past end of all time" for DOS - can happen
140264a6e74Sfrankho * on a 64bit kernel via utimes() syscall ...
141264a6e74Sfrankho */
142264a6e74Sfrankho return (EOVERFLOW);
143264a6e74Sfrankho }
144264a6e74Sfrankho
145264a6e74Sfrankho for (month = 1; unixtime >= 86400 * days_in_month(month, year);
146264a6e74Sfrankho month++)
147264a6e74Sfrankho unixtime -= 86400 * days_in_month(month, year);
148264a6e74Sfrankho
149264a6e74Sfrankho year -= YEAR_ZERO;
150264a6e74Sfrankho
151264a6e74Sfrankho day = (int)(unixtime / 86400);
152264a6e74Sfrankho unixtime -= 86400 * day++; /* counting starts at 1 */
153264a6e74Sfrankho
154264a6e74Sfrankho hour = (int)(unixtime / 3600);
155264a6e74Sfrankho unixtime -= 3600 * hour;
156264a6e74Sfrankho
157264a6e74Sfrankho min = (int)(unixtime / 60);
158264a6e74Sfrankho unixtime -= 60 * min;
159264a6e74Sfrankho
160264a6e74Sfrankho sec = (int)unixtime;
161264a6e74Sfrankho
162264a6e74Sfrankho PC_DPRINTF3(1, "ux2pc date: %d.%d.%d\n", day, month, YEAR_ZERO + year);
163264a6e74Sfrankho PC_DPRINTF3(1, "ux2pc time: %dh%dm%ds\n", hour, min, sec);
164264a6e74Sfrankho PC_DPRINTF1(1, "ux2pc unixtime: %lld\n", (long long)(unixtime));
165264a6e74Sfrankho
166264a6e74Sfrankho ASSERT(year >= 0 && year < 128);
167264a6e74Sfrankho ASSERT(month >= 1 && month <= 12);
168264a6e74Sfrankho ASSERT(day >= 1 && day <= days_in_month(month, year));
169264a6e74Sfrankho ASSERT(hour < 24);
170264a6e74Sfrankho ASSERT(min < 60);
171264a6e74Sfrankho ASSERT(sec < 60);
172264a6e74Sfrankho
173264a6e74Sfrankho pctp->pct_time =
174264a6e74Sfrankho LE_16(hour << HOURSHIFT | min << MINSHIFT | (sec / 2) << SECSHIFT);
175264a6e74Sfrankho pctp->pct_date =
176264a6e74Sfrankho LE_16(year << YEARSHIFT | month << MONSHIFT | day << DAYSHIFT);
177264a6e74Sfrankho
178264a6e74Sfrankho return (0);
179264a6e74Sfrankho }
1807c478bd9Sstevel@tonic-gate
1817c478bd9Sstevel@tonic-gate /*
182264a6e74Sfrankho * Convert time from DOS to UNIX time format.
183264a6e74Sfrankho * Since FAT timestamps cannot be expressed in 32bit time_t,
184264a6e74Sfrankho * the calculation is performed using 64bit values. It's up to
185264a6e74Sfrankho * the caller to decide what to do for out-of-UNIX-range values.
1867c478bd9Sstevel@tonic-gate */
187264a6e74Sfrankho void
pc_pcttotv(struct pctime * pctp,int64_t * unixtime)188264a6e74Sfrankho pc_pcttotv(
189264a6e74Sfrankho struct pctime *pctp, /* DOS time input */
190264a6e74Sfrankho int64_t *unixtime) /* caller converts to time_t */
191264a6e74Sfrankho {
192264a6e74Sfrankho uint_t year, month, day, hour, min, sec;
1937c478bd9Sstevel@tonic-gate
194264a6e74Sfrankho sec = 2 * ((LE_16(pctp->pct_time) >> SECSHIFT) & SECMASK);
195264a6e74Sfrankho min = (LE_16(pctp->pct_time) >> MINSHIFT) & MINMASK;
196264a6e74Sfrankho hour = (LE_16(pctp->pct_time) >> HOURSHIFT) & HOURMASK;
197264a6e74Sfrankho day = (LE_16(pctp->pct_date) >> DAYSHIFT) & DAYMASK;
198264a6e74Sfrankho month = (LE_16(pctp->pct_date) >> MONSHIFT) & MONMASK;
199264a6e74Sfrankho year = (LE_16(pctp->pct_date) >> YEARSHIFT) & YEARMASK;
200264a6e74Sfrankho year += YEAR_ZERO;
201264a6e74Sfrankho
202264a6e74Sfrankho /*
203264a6e74Sfrankho * Basic sanity checks. The FAT timestamp bitfields allow for
204264a6e74Sfrankho * impossible dates/times - return the "FAT epoch" for these.
205264a6e74Sfrankho */
206264a6e74Sfrankho if (pctp->pct_date == 0) {
207264a6e74Sfrankho year = YEAR_ZERO;
208264a6e74Sfrankho month = 1;
209264a6e74Sfrankho day = 1;
2107c478bd9Sstevel@tonic-gate }
211264a6e74Sfrankho if (month > 12 || month < 1 ||
212264a6e74Sfrankho day < 1 || day > days_in_month(month, year) ||
213264a6e74Sfrankho hour > 23 || min > 59 || sec > 59) {
214264a6e74Sfrankho cmn_err(CE_NOTE, "impossible FAT timestamp, "
215264a6e74Sfrankho "d/m/y %d/%d/%d, h:m:s %d:%d:%d",
216264a6e74Sfrankho day, month, year, hour, min, sec);
217264a6e74Sfrankho *unixtime = YZ_SECS + pc_tz.secondswest;
218264a6e74Sfrankho return;
219264a6e74Sfrankho }
220264a6e74Sfrankho
221264a6e74Sfrankho PC_DPRINTF3(1, "pc2ux date: %d.%d.%d\n", day, month, year);
222264a6e74Sfrankho PC_DPRINTF3(1, "pc2ux time: %dh%dm%ds\n", hour, min, sec);
223264a6e74Sfrankho
224264a6e74Sfrankho *unixtime = (int64_t)sec;
225264a6e74Sfrankho *unixtime += 60 * (int64_t)min;
226264a6e74Sfrankho *unixtime += 3600 * (int64_t)hour;
2274d28e670Sbatschul *unixtime += 86400 * (int64_t)(day -1);
228264a6e74Sfrankho while (month > 1) {
229264a6e74Sfrankho month--;
230264a6e74Sfrankho *unixtime += 86400 * (int64_t)days_in_month(month, year);
231264a6e74Sfrankho }
232264a6e74Sfrankho while (year > YEAR_ZERO) {
233264a6e74Sfrankho year--;
2344d28e670Sbatschul *unixtime += 86400 * (int64_t)days_in_year(year);
235264a6e74Sfrankho }
236264a6e74Sfrankho /*
237264a6e74Sfrankho * For FAT, the beginning of all time is 01/01/1980,
238264a6e74Sfrankho * and years are counted relative to that.
239264a6e74Sfrankho * We adjust this base value by the timezone offset
240264a6e74Sfrankho * that is passed in to pcfs at mount time.
241264a6e74Sfrankho */
242264a6e74Sfrankho *unixtime += YZ_SECS;
243264a6e74Sfrankho *unixtime += pc_tz.secondswest;
244264a6e74Sfrankho
245264a6e74Sfrankho /*
246264a6e74Sfrankho * FAT epoch is past UNIX epoch - negative UNIX times
247264a6e74Sfrankho * cannot result from the conversion.
248264a6e74Sfrankho */
249264a6e74Sfrankho ASSERT(*unixtime > 0);
250264a6e74Sfrankho PC_DPRINTF1(1, "pc2ux unixtime: %lld\n", (long long)(*unixtime));
2517c478bd9Sstevel@tonic-gate }
2527c478bd9Sstevel@tonic-gate
2537c478bd9Sstevel@tonic-gate /*
2544a37d755Sksn * Determine whether a character is valid for a long file name.
2554a37d755Sksn * It is easier to determine by filtering out invalid characters.
2564a37d755Sksn * Following are invalid characters in a long filename.
2574a37d755Sksn * / \ : * ? < > | "
2587c478bd9Sstevel@tonic-gate */
2597c478bd9Sstevel@tonic-gate int
pc_valid_lfn_char(char c)2607c478bd9Sstevel@tonic-gate pc_valid_lfn_char(char c)
2617c478bd9Sstevel@tonic-gate {
2624a37d755Sksn const char *cp;
2637c478bd9Sstevel@tonic-gate int n;
2644a37d755Sksn
2654a37d755Sksn static const char invaltab[] = {
2664a37d755Sksn "/\\:*?<>|\""
2677c478bd9Sstevel@tonic-gate };
2687c478bd9Sstevel@tonic-gate
2694a37d755Sksn cp = invaltab;
2704a37d755Sksn n = sizeof (invaltab) - 1;
2717c478bd9Sstevel@tonic-gate while (n--) {
2727c478bd9Sstevel@tonic-gate if (c == *cp++)
2737c478bd9Sstevel@tonic-gate return (0);
2747c478bd9Sstevel@tonic-gate }
2754a37d755Sksn return (1);
2764a37d755Sksn }
2777c478bd9Sstevel@tonic-gate
2787c478bd9Sstevel@tonic-gate int
pc_valid_long_fn(char * namep,int utf8)2794a37d755Sksn pc_valid_long_fn(char *namep, int utf8)
2807c478bd9Sstevel@tonic-gate {
2817c478bd9Sstevel@tonic-gate char *tmp;
282*5f079001SOwen Roberts int len, error;
283*5f079001SOwen Roberts char *prohibited[13] = {
284*5f079001SOwen Roberts "/", "\\", ":", "*", "?", "<", ">", "|", "\"", YEN, LRO, RLO,
285*5f079001SOwen Roberts NULL
286*5f079001SOwen Roberts };
2877c478bd9Sstevel@tonic-gate
2884a37d755Sksn if (utf8) {
2894a37d755Sksn /* UTF-8 */
290*5f079001SOwen Roberts if ((len = u8_validate(namep, strlen(namep), prohibited,
291*5f079001SOwen Roberts (U8_VALIDATE_ENTIRE|U8_VALIDATE_CHECK_ADDITIONAL),
292*5f079001SOwen Roberts &error)) < 0)
2937c478bd9Sstevel@tonic-gate return (0);
294*5f079001SOwen Roberts if (len > PCMAXNAMLEN)
2957c478bd9Sstevel@tonic-gate return (0);
2964a37d755Sksn } else {
2974a37d755Sksn /* UTF-16 */
298*5f079001SOwen Roberts for (tmp = namep; (*tmp != '\0') || (*(tmp+1) != '\0');
2994a37d755Sksn tmp += 2) {
3004a37d755Sksn if ((*(tmp+1) == '\0') && !pc_valid_lfn_char(*tmp))
3014a37d755Sksn return (0);
302*5f079001SOwen Roberts
303*5f079001SOwen Roberts /* Prohibit the Yen character */
304*5f079001SOwen Roberts if ((*(tmp+1) == '\0') && (*tmp == '\xa5'))
305*5f079001SOwen Roberts return (0);
306*5f079001SOwen Roberts
307*5f079001SOwen Roberts /* Prohibit the left-to-right override control char */
308*5f079001SOwen Roberts if ((*(tmp+1) == '\x20') && (*tmp == '\x2d'))
309*5f079001SOwen Roberts return (0);
310*5f079001SOwen Roberts
311*5f079001SOwen Roberts /* Prohibit the right-to-left override control char */
312*5f079001SOwen Roberts if ((*(tmp+1) == '\x20') && (*tmp == '\x2e'))
313*5f079001SOwen Roberts return (0);
3144a37d755Sksn }
3154a37d755Sksn if ((tmp - namep) > (PCMAXNAMLEN * sizeof (uint16_t)))
3164a37d755Sksn return (0);
3174a37d755Sksn }
3187c478bd9Sstevel@tonic-gate return (1);
3197c478bd9Sstevel@tonic-gate }
3207c478bd9Sstevel@tonic-gate
3217c478bd9Sstevel@tonic-gate int
pc_fname_ext_to_name(char * namep,char * fname,char * ext,int foldcase)3227c478bd9Sstevel@tonic-gate pc_fname_ext_to_name(char *namep, char *fname, char *ext, int foldcase)
3237c478bd9Sstevel@tonic-gate {
3247c478bd9Sstevel@tonic-gate int i;
3257c478bd9Sstevel@tonic-gate char *tp = namep;
3267c478bd9Sstevel@tonic-gate char c;
3277c478bd9Sstevel@tonic-gate
3287c478bd9Sstevel@tonic-gate i = PCFNAMESIZE;
3297c478bd9Sstevel@tonic-gate while (i-- && ((c = *fname) != ' ')) {
3307c478bd9Sstevel@tonic-gate if (!(c == '.' || pc_validchar(c))) {
3317c478bd9Sstevel@tonic-gate return (-1);
3327c478bd9Sstevel@tonic-gate }
3337c478bd9Sstevel@tonic-gate if (foldcase)
3347c478bd9Sstevel@tonic-gate *tp++ = tolower(c);
3357c478bd9Sstevel@tonic-gate else
3367c478bd9Sstevel@tonic-gate *tp++ = c;
3377c478bd9Sstevel@tonic-gate fname++;
3387c478bd9Sstevel@tonic-gate }
3397c478bd9Sstevel@tonic-gate if (*ext != ' ') {
3407c478bd9Sstevel@tonic-gate *tp++ = '.';
3417c478bd9Sstevel@tonic-gate i = PCFEXTSIZE;
3427c478bd9Sstevel@tonic-gate while (i-- && ((c = *ext) != ' ')) {
3437c478bd9Sstevel@tonic-gate if (!pc_validchar(c)) {
3447c478bd9Sstevel@tonic-gate return (-1);
3457c478bd9Sstevel@tonic-gate }
3467c478bd9Sstevel@tonic-gate if (foldcase)
3477c478bd9Sstevel@tonic-gate *tp++ = tolower(c);
3487c478bd9Sstevel@tonic-gate else
3497c478bd9Sstevel@tonic-gate *tp++ = c;
3507c478bd9Sstevel@tonic-gate ext++;
3517c478bd9Sstevel@tonic-gate }
3527c478bd9Sstevel@tonic-gate }
3537c478bd9Sstevel@tonic-gate *tp = '\0';
3547c478bd9Sstevel@tonic-gate return (0);
3557c478bd9Sstevel@tonic-gate }
356