1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 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. 30 */ 31 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 #ifndef KERNEL 35 #define KERNEL 36 #endif 37 38 #include <sys/param.h> 39 #include <sys/time.h> 40 #include <sys/conf.h> 41 #include <sys/sysmacros.h> 42 #include <sys/vfs.h> 43 #include <sys/debug.h> 44 #include <sys/errno.h> 45 #include <sys/cmn_err.h> 46 #include <sys/ddi.h> 47 #include <sys/sunddi.h> 48 #include <sys/byteorder.h> 49 #include <sys/fs/pc_fs.h> 50 #include <sys/fs/pc_label.h> 51 #include <sys/fs/pc_dir.h> 52 #include <sys/fs/pc_node.h> 53 54 /* 55 * Convert time between DOS formats: 56 * - years since 1980 57 * - months/days/hours/minutes/seconds, local TZ 58 * and the UNIX format (seconds since 01/01/1970, 00:00:00 UT). 59 * 60 * Timezones are adjusted for via mount option arg (secondswest), 61 * but daylight savings time corrections are not made. Calculated 62 * time may therefore end up being wrong by an hour, but this: 63 * a) will happen as well if media is interchanged between 64 * two DOS/Windows-based systems that use different 65 * timezone settings 66 * b) is the best option we have unless we decide to put 67 * a full ctime(3C) framework into the kernel, including 68 * all conversion tables - AND keeping them current ... 69 */ 70 71 int pc_tvtopct(timestruc_t *, struct pctime *); 72 void pc_pcttotv(struct pctime *, int64_t *); 73 74 /* 75 * Macros/Definitons required to convert between DOS-style and 76 * UNIX-style time recording. 77 * DOS year zero is 1980. 78 */ 79 static int daysinmonth[] = 80 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 81 82 #define YEAR_ZERO 1980 83 #define YZ_SECS (((8 * 365) + (2 * 366)) * 86400) 84 #define FAT_ENDOFTIME \ 85 LE_16(23 << HOURSHIFT | 59 << MINSHIFT | (59/2) << SECSHIFT) 86 #define FAT_ENDOFDATE \ 87 LE_16(127 << YEARSHIFT | 12 << MONSHIFT | 31 << DAYSHIFT) 88 #define leap_year(y) \ 89 (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) 90 91 static int 92 days_in_year(int y) 93 { 94 return (leap_year((y)) ? 366 : 365); 95 } 96 97 static int 98 days_in_month(int m, int y) 99 { 100 if (m == 2 && leap_year(y)) 101 return (29); 102 else 103 return (daysinmonth[m-1]); 104 } 105 106 struct pcfs_args pc_tz; /* this is set by pcfs_mount */ 107 108 /* 109 * Convert time from UNIX to DOS format. 110 * Return EOVERFLOW in case no valid DOS time representation 111 * exists for the given UNIX time. 112 */ 113 int 114 pc_tvtopct( 115 timestruc_t *tvp, /* UNIX time input */ 116 struct pctime *pctp) /* pctime output */ 117 { 118 uint_t year, month, day, hour, min, sec; 119 int64_t unixtime; 120 121 unixtime = (int64_t)tvp->tv_sec; 122 unixtime -= YZ_SECS; 123 unixtime -= pc_tz.secondswest; 124 if (unixtime <= 0) { 125 /* 126 * "before beginning of all time" for DOS ... 127 */ 128 return (EOVERFLOW); 129 } 130 for (year = YEAR_ZERO; unixtime >= days_in_year(year) * 86400; 131 year++) 132 unixtime -= 86400 * days_in_year(year); 133 134 if (year > 127 + YEAR_ZERO) { 135 /* 136 * "past end of all time" for DOS - can happen 137 * on a 64bit kernel via utimes() syscall ... 138 */ 139 return (EOVERFLOW); 140 } 141 142 for (month = 1; unixtime >= 86400 * days_in_month(month, year); 143 month++) 144 unixtime -= 86400 * days_in_month(month, year); 145 146 year -= YEAR_ZERO; 147 148 day = (int)(unixtime / 86400); 149 unixtime -= 86400 * day++; /* counting starts at 1 */ 150 151 hour = (int)(unixtime / 3600); 152 unixtime -= 3600 * hour; 153 154 min = (int)(unixtime / 60); 155 unixtime -= 60 * min; 156 157 sec = (int)unixtime; 158 159 PC_DPRINTF3(1, "ux2pc date: %d.%d.%d\n", day, month, YEAR_ZERO + year); 160 PC_DPRINTF3(1, "ux2pc time: %dh%dm%ds\n", hour, min, sec); 161 PC_DPRINTF1(1, "ux2pc unixtime: %lld\n", (long long)(unixtime)); 162 163 ASSERT(year >= 0 && year < 128); 164 ASSERT(month >= 1 && month <= 12); 165 ASSERT(day >= 1 && day <= days_in_month(month, year)); 166 ASSERT(hour < 24); 167 ASSERT(min < 60); 168 ASSERT(sec < 60); 169 170 pctp->pct_time = 171 LE_16(hour << HOURSHIFT | min << MINSHIFT | (sec / 2) << SECSHIFT); 172 pctp->pct_date = 173 LE_16(year << YEARSHIFT | month << MONSHIFT | day << DAYSHIFT); 174 175 return (0); 176 } 177 178 /* 179 * Convert time from DOS to UNIX time format. 180 * Since FAT timestamps cannot be expressed in 32bit time_t, 181 * the calculation is performed using 64bit values. It's up to 182 * the caller to decide what to do for out-of-UNIX-range values. 183 */ 184 void 185 pc_pcttotv( 186 struct pctime *pctp, /* DOS time input */ 187 int64_t *unixtime) /* caller converts to time_t */ 188 { 189 uint_t year, month, day, hour, min, sec; 190 191 sec = 2 * ((LE_16(pctp->pct_time) >> SECSHIFT) & SECMASK); 192 min = (LE_16(pctp->pct_time) >> MINSHIFT) & MINMASK; 193 hour = (LE_16(pctp->pct_time) >> HOURSHIFT) & HOURMASK; 194 day = (LE_16(pctp->pct_date) >> DAYSHIFT) & DAYMASK; 195 month = (LE_16(pctp->pct_date) >> MONSHIFT) & MONMASK; 196 year = (LE_16(pctp->pct_date) >> YEARSHIFT) & YEARMASK; 197 year += YEAR_ZERO; 198 199 /* 200 * Basic sanity checks. The FAT timestamp bitfields allow for 201 * impossible dates/times - return the "FAT epoch" for these. 202 */ 203 if (pctp->pct_date == 0) { 204 year = YEAR_ZERO; 205 month = 1; 206 day = 1; 207 } 208 if (month > 12 || month < 1 || 209 day < 1 || day > days_in_month(month, year) || 210 hour > 23 || min > 59 || sec > 59) { 211 cmn_err(CE_NOTE, "impossible FAT timestamp, " 212 "d/m/y %d/%d/%d, h:m:s %d:%d:%d", 213 day, month, year, hour, min, sec); 214 *unixtime = YZ_SECS + pc_tz.secondswest; 215 return; 216 } 217 218 PC_DPRINTF3(1, "pc2ux date: %d.%d.%d\n", day, month, year); 219 PC_DPRINTF3(1, "pc2ux time: %dh%dm%ds\n", hour, min, sec); 220 221 *unixtime = (int64_t)sec; 222 *unixtime += 60 * (int64_t)min; 223 *unixtime += 3600 * (int64_t)hour; 224 *unixtime += 86400 * (int64_t)day; 225 while (month > 1) { 226 month--; 227 *unixtime += 86400 * (int64_t)days_in_month(month, year); 228 } 229 while (year > YEAR_ZERO) { 230 *unixtime += 86400 * (int64_t)days_in_year(year); 231 year--; 232 } 233 /* 234 * For FAT, the beginning of all time is 01/01/1980, 235 * and years are counted relative to that. 236 * We adjust this base value by the timezone offset 237 * that is passed in to pcfs at mount time. 238 */ 239 *unixtime += YZ_SECS; 240 *unixtime += pc_tz.secondswest; 241 242 /* 243 * FAT epoch is past UNIX epoch - negative UNIX times 244 * cannot result from the conversion. 245 */ 246 ASSERT(*unixtime > 0); 247 PC_DPRINTF1(1, "pc2ux unixtime: %lld\n", (long long)(*unixtime)); 248 } 249 250 /* 251 * Determine whether a character is valid for a pc 8.3 file system file name. 252 * The Windows 95 Resource Kit claims that these are valid: 253 * uppercase letters and numbers 254 * blank 255 * ASCII characters greater than 127 256 * $%'-_@~`!()^#& 257 * Long file names can also have 258 * lowercase letters 259 * +,;=[]. 260 */ 261 int 262 pc_valid_lfn_char(char c) 263 { 264 char *cp; 265 int n; 266 static char valtab[] = { 267 "+,;=[].$#&@!%()-{}<>`_^~|' " 268 }; 269 270 if (c >= 'a' && c <= 'z') 271 return (1); 272 if (c >= 'A' && c <= 'Z') 273 return (1); 274 if (c >= '0' && c <= '9') 275 return (1); 276 cp = valtab; 277 n = sizeof (valtab); 278 while (n--) { 279 if (c == *cp++) 280 return (1); 281 } 282 return (0); 283 } 284 285 int 286 pc_valid_long_fn(char *namep) 287 { 288 char *tmp; 289 290 for (tmp = namep; *tmp != '\0'; tmp++) 291 if (!pc_valid_lfn_char(*tmp)) 292 return (0); 293 if ((tmp - namep) >= PCMAXNAMLEN) 294 return (0); 295 return (1); 296 } 297 298 int 299 pc_fname_ext_to_name(char *namep, char *fname, char *ext, int foldcase) 300 { 301 int i; 302 char *tp = namep; 303 char c; 304 305 i = PCFNAMESIZE; 306 while (i-- && ((c = *fname) != ' ')) { 307 if (!(c == '.' || pc_validchar(c))) { 308 return (-1); 309 } 310 if (foldcase) 311 *tp++ = tolower(c); 312 else 313 *tp++ = c; 314 fname++; 315 } 316 if (*ext != ' ') { 317 *tp++ = '.'; 318 i = PCFEXTSIZE; 319 while (i-- && ((c = *ext) != ' ')) { 320 if (!pc_validchar(c)) { 321 return (-1); 322 } 323 if (foldcase) 324 *tp++ = tolower(c); 325 else 326 *tp++ = c; 327 ext++; 328 } 329 } 330 *tp = '\0'; 331 return (0); 332 } 333