dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / kernel / fs / pcfs / pc_subr.c
blob2a1bf2f4792cdeef1edcfcc2350c8746b60d056d
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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.
32 #ifndef KERNEL
33 #define KERNEL
34 #endif
36 #include <sys/param.h>
37 #include <sys/time.h>
38 #include <sys/conf.h>
39 #include <sys/sysmacros.h>
40 #include <sys/vfs.h>
41 #include <sys/debug.h>
42 #include <sys/errno.h>
43 #include <sys/cmn_err.h>
44 #include <sys/ddi.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:
55 * - years since 1980
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
64 * timezone settings
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 */
94 static int
95 days_in_year(int y)
97 return (leap_year((y)) ? 366 : 365);
100 static int
101 days_in_month(int m, int y)
103 if (m == 2 && leap_year(y))
104 return (29);
105 else
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.
117 pc_tvtopct(
118 timestruc_t *tvp, /* UNIX time input */
119 struct pctime *pctp) /* pctime output */
121 uint_t year, month, day, hour, min, sec;
122 int64_t unixtime;
124 unixtime = (int64_t)tvp->tv_sec;
125 unixtime -= YZ_SECS;
126 unixtime -= pc_tz.secondswest;
127 if (unixtime <= 0) {
129 * "before beginning of all time" for DOS ...
131 return (EOVERFLOW);
133 for (year = YEAR_ZERO; unixtime >= days_in_year(year) * 86400;
134 year++)
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 ...
142 return (EOVERFLOW);
145 for (month = 1; unixtime >= 86400 * days_in_month(month, year);
146 month++)
147 unixtime -= 86400 * days_in_month(month, year);
149 year -= YEAR_ZERO;
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;
160 sec = (int)unixtime;
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));
169 ASSERT(hour < 24);
170 ASSERT(min < 60);
171 ASSERT(sec < 60);
173 pctp->pct_time =
174 LE_16(hour << HOURSHIFT | min << MINSHIFT | (sec / 2) << SECSHIFT);
175 pctp->pct_date =
176 LE_16(year << YEARSHIFT | month << MONSHIFT | day << DAYSHIFT);
178 return (0);
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.
187 void
188 pc_pcttotv(
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;
200 year += YEAR_ZERO;
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) {
207 year = YEAR_ZERO;
208 month = 1;
209 day = 1;
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;
218 return;
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);
228 while (month > 1) {
229 month--;
230 *unixtime += 86400 * (int64_t)days_in_month(month, year);
232 while (year > YEAR_ZERO) {
233 year--;
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.
257 * / \ : * ? < > | "
260 pc_valid_lfn_char(char c)
262 const char *cp;
263 int n;
265 static const char invaltab[] = {
266 "/\\:*?<>|\""
269 cp = invaltab;
270 n = sizeof (invaltab) - 1;
271 while (n--) {
272 if (c == *cp++)
273 return (0);
275 return (1);
279 pc_valid_long_fn(char *namep, int utf8)
281 char *tmp;
282 int len, error;
283 char *prohibited[13] = {
284 "/", "\\", ":", "*", "?", "<", ">", "|", "\"", YEN, LRO, RLO,
285 NULL
288 if (utf8) {
289 /* UTF-8 */
290 if ((len = u8_validate(namep, strlen(namep), prohibited,
291 (U8_VALIDATE_ENTIRE|U8_VALIDATE_CHECK_ADDITIONAL),
292 &error)) < 0)
293 return (0);
294 if (len > PCMAXNAMLEN)
295 return (0);
296 } else {
297 /* UTF-16 */
298 for (tmp = namep; (*tmp != '\0') || (*(tmp+1) != '\0');
299 tmp += 2) {
300 if ((*(tmp+1) == '\0') && !pc_valid_lfn_char(*tmp))
301 return (0);
303 /* Prohibit the Yen character */
304 if ((*(tmp+1) == '\0') && (*tmp == '\xa5'))
305 return (0);
307 /* Prohibit the left-to-right override control char */
308 if ((*(tmp+1) == '\x20') && (*tmp == '\x2d'))
309 return (0);
311 /* Prohibit the right-to-left override control char */
312 if ((*(tmp+1) == '\x20') && (*tmp == '\x2e'))
313 return (0);
315 if ((tmp - namep) > (PCMAXNAMLEN * sizeof (uint16_t)))
316 return (0);
318 return (1);
322 pc_fname_ext_to_name(char *namep, char *fname, char *ext, int foldcase)
324 int i;
325 char *tp = namep;
326 char c;
328 i = PCFNAMESIZE;
329 while (i-- && ((c = *fname) != ' ')) {
330 if (!(c == '.' || pc_validchar(c))) {
331 return (-1);
333 if (foldcase)
334 *tp++ = tolower(c);
335 else
336 *tp++ = c;
337 fname++;
339 if (*ext != ' ') {
340 *tp++ = '.';
341 i = PCFEXTSIZE;
342 while (i-- && ((c = *ext) != ' ')) {
343 if (!pc_validchar(c)) {
344 return (-1);
346 if (foldcase)
347 *tp++ = tolower(c);
348 else
349 *tp++ = c;
350 ext++;
353 *tp = '\0';
354 return (0);