4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Copyright (c) 1980 Regents of the University of California.
28 * All rights reserved. The Berkeley software License Agreement
29 * specifies the terms and conditions for redistribution.
36 #include <sys/param.h>
39 #include <sys/sysmacros.h>
41 #include <sys/debug.h>
42 #include <sys/errno.h>
43 #include <sys/cmn_err.h>
45 #include <sys/sunddi.h>
46 #include <sys/byteorder.h>
47 #include <sys/types.h>
48 #include <sys/fs/pc_fs.h>
49 #include <sys/fs/pc_label.h>
50 #include <sys/fs/pc_dir.h>
51 #include <sys/fs/pc_node.h>
54 * Convert time between DOS formats:
56 * - months/days/hours/minutes/seconds, local TZ
57 * and the UNIX format (seconds since 01/01/1970, 00:00:00 UT).
59 * Timezones are adjusted for via mount option arg (secondswest),
60 * but daylight savings time corrections are not made. Calculated
61 * time may therefore end up being wrong by an hour, but this:
62 * a) will happen as well if media is interchanged between
63 * two DOS/Windows-based systems that use different
65 * b) is the best option we have unless we decide to put
66 * a full ctime(3C) framework into the kernel, including
67 * all conversion tables - AND keeping them current ...
70 int pc_tvtopct(timestruc_t
*, struct pctime
*);
71 void pc_pcttotv(struct pctime
*, int64_t *);
74 * Macros/Definitons required to convert between DOS-style and
75 * UNIX-style time recording.
76 * DOS year zero is 1980.
78 static int daysinmonth
[] =
79 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
81 #define YEAR_ZERO 1980
82 #define YZ_SECS (((8 * 365) + (2 * 366)) * 86400)
83 #define FAT_ENDOFTIME \
84 LE_16(23 << HOURSHIFT | 59 << MINSHIFT | (59/2) << SECSHIFT)
85 #define FAT_ENDOFDATE \
86 LE_16(127 << YEARSHIFT | 12 << MONSHIFT | 31 << DAYSHIFT)
87 #define leap_year(y) \
88 (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
90 #define YEN "\xc2\xa5" /* Yen Sign UTF-8 character */
91 #define LRO "\xe2\x80\xad" /* Left-To-Right Override UTF-8 character */
92 #define RLO "\xe2\x80\xae" /* Right-To-Left Override UTF-8 character */
97 return (leap_year((y
)) ? 366 : 365);
101 days_in_month(int m
, int y
)
103 if (m
== 2 && leap_year(y
))
106 return (daysinmonth
[m
-1]);
109 struct pcfs_args pc_tz
; /* this is set by pcfs_mount */
112 * Convert time from UNIX to DOS format.
113 * Return EOVERFLOW in case no valid DOS time representation
114 * exists for the given UNIX time.
118 timestruc_t
*tvp
, /* UNIX time input */
119 struct pctime
*pctp
) /* pctime output */
121 uint_t year
, month
, day
, hour
, min
, sec
;
124 unixtime
= (int64_t)tvp
->tv_sec
;
126 unixtime
-= pc_tz
.secondswest
;
129 * "before beginning of all time" for DOS ...
133 for (year
= YEAR_ZERO
; unixtime
>= days_in_year(year
) * 86400;
135 unixtime
-= 86400 * days_in_year(year
);
137 if (year
> 127 + YEAR_ZERO
) {
139 * "past end of all time" for DOS - can happen
140 * on a 64bit kernel via utimes() syscall ...
145 for (month
= 1; unixtime
>= 86400 * days_in_month(month
, year
);
147 unixtime
-= 86400 * days_in_month(month
, year
);
151 day
= (int)(unixtime
/ 86400);
152 unixtime
-= 86400 * day
++; /* counting starts at 1 */
154 hour
= (int)(unixtime
/ 3600);
155 unixtime
-= 3600 * hour
;
157 min
= (int)(unixtime
/ 60);
158 unixtime
-= 60 * min
;
162 PC_DPRINTF3(1, "ux2pc date: %d.%d.%d\n", day
, month
, YEAR_ZERO
+ year
);
163 PC_DPRINTF3(1, "ux2pc time: %dh%dm%ds\n", hour
, min
, sec
);
164 PC_DPRINTF1(1, "ux2pc unixtime: %lld\n", (long long)(unixtime
));
166 ASSERT(year
>= 0 && year
< 128);
167 ASSERT(month
>= 1 && month
<= 12);
168 ASSERT(day
>= 1 && day
<= days_in_month(month
, year
));
174 LE_16(hour
<< HOURSHIFT
| min
<< MINSHIFT
| (sec
/ 2) << SECSHIFT
);
176 LE_16(year
<< YEARSHIFT
| month
<< MONSHIFT
| day
<< DAYSHIFT
);
182 * Convert time from DOS to UNIX time format.
183 * Since FAT timestamps cannot be expressed in 32bit time_t,
184 * the calculation is performed using 64bit values. It's up to
185 * the caller to decide what to do for out-of-UNIX-range values.
189 struct pctime
*pctp
, /* DOS time input */
190 int64_t *unixtime
) /* caller converts to time_t */
192 uint_t year
, month
, day
, hour
, min
, sec
;
194 sec
= 2 * ((LE_16(pctp
->pct_time
) >> SECSHIFT
) & SECMASK
);
195 min
= (LE_16(pctp
->pct_time
) >> MINSHIFT
) & MINMASK
;
196 hour
= (LE_16(pctp
->pct_time
) >> HOURSHIFT
) & HOURMASK
;
197 day
= (LE_16(pctp
->pct_date
) >> DAYSHIFT
) & DAYMASK
;
198 month
= (LE_16(pctp
->pct_date
) >> MONSHIFT
) & MONMASK
;
199 year
= (LE_16(pctp
->pct_date
) >> YEARSHIFT
) & YEARMASK
;
203 * Basic sanity checks. The FAT timestamp bitfields allow for
204 * impossible dates/times - return the "FAT epoch" for these.
206 if (pctp
->pct_date
== 0) {
211 if (month
> 12 || month
< 1 ||
212 day
< 1 || day
> days_in_month(month
, year
) ||
213 hour
> 23 || min
> 59 || sec
> 59) {
214 cmn_err(CE_NOTE
, "impossible FAT timestamp, "
215 "d/m/y %d/%d/%d, h:m:s %d:%d:%d",
216 day
, month
, year
, hour
, min
, sec
);
217 *unixtime
= YZ_SECS
+ pc_tz
.secondswest
;
221 PC_DPRINTF3(1, "pc2ux date: %d.%d.%d\n", day
, month
, year
);
222 PC_DPRINTF3(1, "pc2ux time: %dh%dm%ds\n", hour
, min
, sec
);
224 *unixtime
= (int64_t)sec
;
225 *unixtime
+= 60 * (int64_t)min
;
226 *unixtime
+= 3600 * (int64_t)hour
;
227 *unixtime
+= 86400 * (int64_t)(day
-1);
230 *unixtime
+= 86400 * (int64_t)days_in_month(month
, year
);
232 while (year
> YEAR_ZERO
) {
234 *unixtime
+= 86400 * (int64_t)days_in_year(year
);
237 * For FAT, the beginning of all time is 01/01/1980,
238 * and years are counted relative to that.
239 * We adjust this base value by the timezone offset
240 * that is passed in to pcfs at mount time.
242 *unixtime
+= YZ_SECS
;
243 *unixtime
+= pc_tz
.secondswest
;
246 * FAT epoch is past UNIX epoch - negative UNIX times
247 * cannot result from the conversion.
249 ASSERT(*unixtime
> 0);
250 PC_DPRINTF1(1, "pc2ux unixtime: %lld\n", (long long)(*unixtime
));
254 * Determine whether a character is valid for a long file name.
255 * It is easier to determine by filtering out invalid characters.
256 * Following are invalid characters in a long filename.
260 pc_valid_lfn_char(char c
)
265 static const char invaltab
[] = {
270 n
= sizeof (invaltab
) - 1;
279 pc_valid_long_fn(char *namep
, int utf8
)
283 char *prohibited
[13] = {
284 "/", "\\", ":", "*", "?", "<", ">", "|", "\"", YEN
, LRO
, RLO
,
290 if ((len
= u8_validate(namep
, strlen(namep
), prohibited
,
291 (U8_VALIDATE_ENTIRE
|U8_VALIDATE_CHECK_ADDITIONAL
),
294 if (len
> PCMAXNAMLEN
)
298 for (tmp
= namep
; (*tmp
!= '\0') || (*(tmp
+1) != '\0');
300 if ((*(tmp
+1) == '\0') && !pc_valid_lfn_char(*tmp
))
303 /* Prohibit the Yen character */
304 if ((*(tmp
+1) == '\0') && (*tmp
== '\xa5'))
307 /* Prohibit the left-to-right override control char */
308 if ((*(tmp
+1) == '\x20') && (*tmp
== '\x2d'))
311 /* Prohibit the right-to-left override control char */
312 if ((*(tmp
+1) == '\x20') && (*tmp
== '\x2e'))
315 if ((tmp
- namep
) > (PCMAXNAMLEN
* sizeof (uint16_t)))
322 pc_fname_ext_to_name(char *namep
, char *fname
, char *ext
, int foldcase
)
329 while (i
-- && ((c
= *fname
) != ' ')) {
330 if (!(c
== '.' || pc_validchar(c
))) {
342 while (i
-- && ((c
= *ext
) != ' ')) {
343 if (!pc_validchar(c
)) {