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 /* 22*4a37d755Sksn * Copyright 2007 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 #pragma ident "%Z%%M% %I% %E% SMI" 337c478bd9Sstevel@tonic-gate 347c478bd9Sstevel@tonic-gate #ifndef KERNEL 357c478bd9Sstevel@tonic-gate #define KERNEL 367c478bd9Sstevel@tonic-gate #endif 377c478bd9Sstevel@tonic-gate 387c478bd9Sstevel@tonic-gate #include <sys/param.h> 397c478bd9Sstevel@tonic-gate #include <sys/time.h> 407c478bd9Sstevel@tonic-gate #include <sys/conf.h> 417c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 427c478bd9Sstevel@tonic-gate #include <sys/vfs.h> 437c478bd9Sstevel@tonic-gate #include <sys/debug.h> 447c478bd9Sstevel@tonic-gate #include <sys/errno.h> 45264a6e74Sfrankho #include <sys/cmn_err.h> 46264a6e74Sfrankho #include <sys/ddi.h> 47264a6e74Sfrankho #include <sys/sunddi.h> 48264a6e74Sfrankho #include <sys/byteorder.h> 497c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h> 507c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h> 517c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h> 527c478bd9Sstevel@tonic-gate #include <sys/fs/pc_node.h> 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gate /* 55264a6e74Sfrankho * Convert time between DOS formats: 56264a6e74Sfrankho * - years since 1980 57264a6e74Sfrankho * - months/days/hours/minutes/seconds, local TZ 58264a6e74Sfrankho * and the UNIX format (seconds since 01/01/1970, 00:00:00 UT). 59264a6e74Sfrankho * 60264a6e74Sfrankho * Timezones are adjusted for via mount option arg (secondswest), 61264a6e74Sfrankho * but daylight savings time corrections are not made. Calculated 62264a6e74Sfrankho * time may therefore end up being wrong by an hour, but this: 63264a6e74Sfrankho * a) will happen as well if media is interchanged between 64264a6e74Sfrankho * two DOS/Windows-based systems that use different 65264a6e74Sfrankho * timezone settings 66264a6e74Sfrankho * b) is the best option we have unless we decide to put 67264a6e74Sfrankho * a full ctime(3C) framework into the kernel, including 68264a6e74Sfrankho * all conversion tables - AND keeping them current ... 697c478bd9Sstevel@tonic-gate */ 707c478bd9Sstevel@tonic-gate 71264a6e74Sfrankho int pc_tvtopct(timestruc_t *, struct pctime *); 72264a6e74Sfrankho void pc_pcttotv(struct pctime *, int64_t *); 737c478bd9Sstevel@tonic-gate 747c478bd9Sstevel@tonic-gate /* 75264a6e74Sfrankho * Macros/Definitons required to convert between DOS-style and 76264a6e74Sfrankho * UNIX-style time recording. 77264a6e74Sfrankho * DOS year zero is 1980. 787c478bd9Sstevel@tonic-gate */ 79264a6e74Sfrankho static int daysinmonth[] = 80264a6e74Sfrankho { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 81264a6e74Sfrankho 82264a6e74Sfrankho #define YEAR_ZERO 1980 83264a6e74Sfrankho #define YZ_SECS (((8 * 365) + (2 * 366)) * 86400) 84264a6e74Sfrankho #define FAT_ENDOFTIME \ 85264a6e74Sfrankho LE_16(23 << HOURSHIFT | 59 << MINSHIFT | (59/2) << SECSHIFT) 86264a6e74Sfrankho #define FAT_ENDOFDATE \ 87264a6e74Sfrankho LE_16(127 << YEARSHIFT | 12 << MONSHIFT | 31 << DAYSHIFT) 88264a6e74Sfrankho #define leap_year(y) \ 89264a6e74Sfrankho (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) 907c478bd9Sstevel@tonic-gate 91264a6e74Sfrankho static int 92264a6e74Sfrankho days_in_year(int y) 93264a6e74Sfrankho { 94264a6e74Sfrankho return (leap_year((y)) ? 366 : 365); 957c478bd9Sstevel@tonic-gate } 967c478bd9Sstevel@tonic-gate 97264a6e74Sfrankho static int 98264a6e74Sfrankho days_in_month(int m, int y) 997c478bd9Sstevel@tonic-gate { 100264a6e74Sfrankho if (m == 2 && leap_year(y)) 101264a6e74Sfrankho return (29); 102264a6e74Sfrankho else 103264a6e74Sfrankho return (daysinmonth[m-1]); 1047c478bd9Sstevel@tonic-gate } 1057c478bd9Sstevel@tonic-gate 106264a6e74Sfrankho struct pcfs_args pc_tz; /* this is set by pcfs_mount */ 107264a6e74Sfrankho 1087c478bd9Sstevel@tonic-gate /* 109264a6e74Sfrankho * Convert time from UNIX to DOS format. 110264a6e74Sfrankho * Return EOVERFLOW in case no valid DOS time representation 111264a6e74Sfrankho * exists for the given UNIX time. 1127c478bd9Sstevel@tonic-gate */ 113264a6e74Sfrankho int 114264a6e74Sfrankho pc_tvtopct( 115264a6e74Sfrankho timestruc_t *tvp, /* UNIX time input */ 116264a6e74Sfrankho struct pctime *pctp) /* pctime output */ 1177c478bd9Sstevel@tonic-gate { 118264a6e74Sfrankho uint_t year, month, day, hour, min, sec; 119264a6e74Sfrankho int64_t unixtime; 120264a6e74Sfrankho 121264a6e74Sfrankho unixtime = (int64_t)tvp->tv_sec; 122264a6e74Sfrankho unixtime -= YZ_SECS; 123264a6e74Sfrankho unixtime -= pc_tz.secondswest; 124264a6e74Sfrankho if (unixtime <= 0) { 125264a6e74Sfrankho /* 126264a6e74Sfrankho * "before beginning of all time" for DOS ... 127264a6e74Sfrankho */ 128264a6e74Sfrankho return (EOVERFLOW); 129264a6e74Sfrankho } 130264a6e74Sfrankho for (year = YEAR_ZERO; unixtime >= days_in_year(year) * 86400; 131264a6e74Sfrankho year++) 132264a6e74Sfrankho unixtime -= 86400 * days_in_year(year); 133264a6e74Sfrankho 134264a6e74Sfrankho if (year > 127 + YEAR_ZERO) { 135264a6e74Sfrankho /* 136264a6e74Sfrankho * "past end of all time" for DOS - can happen 137264a6e74Sfrankho * on a 64bit kernel via utimes() syscall ... 138264a6e74Sfrankho */ 139264a6e74Sfrankho return (EOVERFLOW); 140264a6e74Sfrankho } 1417c478bd9Sstevel@tonic-gate 142264a6e74Sfrankho for (month = 1; unixtime >= 86400 * days_in_month(month, year); 143264a6e74Sfrankho month++) 144264a6e74Sfrankho unixtime -= 86400 * days_in_month(month, year); 1457c478bd9Sstevel@tonic-gate 146264a6e74Sfrankho year -= YEAR_ZERO; 1477c478bd9Sstevel@tonic-gate 148264a6e74Sfrankho day = (int)(unixtime / 86400); 149264a6e74Sfrankho unixtime -= 86400 * day++; /* counting starts at 1 */ 1507c478bd9Sstevel@tonic-gate 151264a6e74Sfrankho hour = (int)(unixtime / 3600); 152264a6e74Sfrankho unixtime -= 3600 * hour; 1537c478bd9Sstevel@tonic-gate 154264a6e74Sfrankho min = (int)(unixtime / 60); 155264a6e74Sfrankho unixtime -= 60 * min; 1567c478bd9Sstevel@tonic-gate 157264a6e74Sfrankho sec = (int)unixtime; 1587c478bd9Sstevel@tonic-gate 159264a6e74Sfrankho PC_DPRINTF3(1, "ux2pc date: %d.%d.%d\n", day, month, YEAR_ZERO + year); 160264a6e74Sfrankho PC_DPRINTF3(1, "ux2pc time: %dh%dm%ds\n", hour, min, sec); 161264a6e74Sfrankho PC_DPRINTF1(1, "ux2pc unixtime: %lld\n", (long long)(unixtime)); 1627c478bd9Sstevel@tonic-gate 163264a6e74Sfrankho ASSERT(year >= 0 && year < 128); 164264a6e74Sfrankho ASSERT(month >= 1 && month <= 12); 165264a6e74Sfrankho ASSERT(day >= 1 && day <= days_in_month(month, year)); 166264a6e74Sfrankho ASSERT(hour < 24); 167264a6e74Sfrankho ASSERT(min < 60); 168264a6e74Sfrankho ASSERT(sec < 60); 169264a6e74Sfrankho 170264a6e74Sfrankho pctp->pct_time = 171264a6e74Sfrankho LE_16(hour << HOURSHIFT | min << MINSHIFT | (sec / 2) << SECSHIFT); 172264a6e74Sfrankho pctp->pct_date = 173264a6e74Sfrankho LE_16(year << YEARSHIFT | month << MONSHIFT | day << DAYSHIFT); 174264a6e74Sfrankho 175264a6e74Sfrankho return (0); 1767c478bd9Sstevel@tonic-gate } 1777c478bd9Sstevel@tonic-gate 1787c478bd9Sstevel@tonic-gate /* 179264a6e74Sfrankho * Convert time from DOS to UNIX time format. 180264a6e74Sfrankho * Since FAT timestamps cannot be expressed in 32bit time_t, 181264a6e74Sfrankho * the calculation is performed using 64bit values. It's up to 182264a6e74Sfrankho * the caller to decide what to do for out-of-UNIX-range values. 1837c478bd9Sstevel@tonic-gate */ 184264a6e74Sfrankho void 185264a6e74Sfrankho pc_pcttotv( 186264a6e74Sfrankho struct pctime *pctp, /* DOS time input */ 187264a6e74Sfrankho int64_t *unixtime) /* caller converts to time_t */ 1887c478bd9Sstevel@tonic-gate { 189264a6e74Sfrankho uint_t year, month, day, hour, min, sec; 1907c478bd9Sstevel@tonic-gate 191264a6e74Sfrankho sec = 2 * ((LE_16(pctp->pct_time) >> SECSHIFT) & SECMASK); 192264a6e74Sfrankho min = (LE_16(pctp->pct_time) >> MINSHIFT) & MINMASK; 193264a6e74Sfrankho hour = (LE_16(pctp->pct_time) >> HOURSHIFT) & HOURMASK; 194264a6e74Sfrankho day = (LE_16(pctp->pct_date) >> DAYSHIFT) & DAYMASK; 195264a6e74Sfrankho month = (LE_16(pctp->pct_date) >> MONSHIFT) & MONMASK; 196264a6e74Sfrankho year = (LE_16(pctp->pct_date) >> YEARSHIFT) & YEARMASK; 197264a6e74Sfrankho year += YEAR_ZERO; 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate /* 200264a6e74Sfrankho * Basic sanity checks. The FAT timestamp bitfields allow for 201264a6e74Sfrankho * impossible dates/times - return the "FAT epoch" for these. 2027c478bd9Sstevel@tonic-gate */ 203264a6e74Sfrankho if (pctp->pct_date == 0) { 204264a6e74Sfrankho year = YEAR_ZERO; 205264a6e74Sfrankho month = 1; 206264a6e74Sfrankho day = 1; 207264a6e74Sfrankho } 208264a6e74Sfrankho if (month > 12 || month < 1 || 209264a6e74Sfrankho day < 1 || day > days_in_month(month, year) || 210264a6e74Sfrankho hour > 23 || min > 59 || sec > 59) { 211264a6e74Sfrankho cmn_err(CE_NOTE, "impossible FAT timestamp, " 212264a6e74Sfrankho "d/m/y %d/%d/%d, h:m:s %d:%d:%d", 213264a6e74Sfrankho day, month, year, hour, min, sec); 214264a6e74Sfrankho *unixtime = YZ_SECS + pc_tz.secondswest; 215264a6e74Sfrankho return; 2167c478bd9Sstevel@tonic-gate } 2177c478bd9Sstevel@tonic-gate 218264a6e74Sfrankho PC_DPRINTF3(1, "pc2ux date: %d.%d.%d\n", day, month, year); 219264a6e74Sfrankho PC_DPRINTF3(1, "pc2ux time: %dh%dm%ds\n", hour, min, sec); 2207c478bd9Sstevel@tonic-gate 221264a6e74Sfrankho *unixtime = (int64_t)sec; 222264a6e74Sfrankho *unixtime += 60 * (int64_t)min; 223264a6e74Sfrankho *unixtime += 3600 * (int64_t)hour; 224264a6e74Sfrankho *unixtime += 86400 * (int64_t)day; 225264a6e74Sfrankho while (month > 1) { 226264a6e74Sfrankho month--; 227264a6e74Sfrankho *unixtime += 86400 * (int64_t)days_in_month(month, year); 228264a6e74Sfrankho } 229264a6e74Sfrankho while (year > YEAR_ZERO) { 230264a6e74Sfrankho *unixtime += 86400 * (int64_t)days_in_year(year); 231264a6e74Sfrankho year--; 232264a6e74Sfrankho } 2337c478bd9Sstevel@tonic-gate /* 234264a6e74Sfrankho * For FAT, the beginning of all time is 01/01/1980, 235264a6e74Sfrankho * and years are counted relative to that. 236264a6e74Sfrankho * We adjust this base value by the timezone offset 237264a6e74Sfrankho * that is passed in to pcfs at mount time. 2387c478bd9Sstevel@tonic-gate */ 239264a6e74Sfrankho *unixtime += YZ_SECS; 240264a6e74Sfrankho *unixtime += pc_tz.secondswest; 2417c478bd9Sstevel@tonic-gate 2427c478bd9Sstevel@tonic-gate /* 243264a6e74Sfrankho * FAT epoch is past UNIX epoch - negative UNIX times 244264a6e74Sfrankho * cannot result from the conversion. 2457c478bd9Sstevel@tonic-gate */ 246264a6e74Sfrankho ASSERT(*unixtime > 0); 247264a6e74Sfrankho PC_DPRINTF1(1, "pc2ux unixtime: %lld\n", (long long)(*unixtime)); 2487c478bd9Sstevel@tonic-gate } 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate /* 251*4a37d755Sksn * Determine whether a character is valid for a long file name. 252*4a37d755Sksn * It is easier to determine by filtering out invalid characters. 253*4a37d755Sksn * Following are invalid characters in a long filename. 254*4a37d755Sksn * / \ : * ? < > | " 2557c478bd9Sstevel@tonic-gate */ 2567c478bd9Sstevel@tonic-gate int 2577c478bd9Sstevel@tonic-gate pc_valid_lfn_char(char c) 2587c478bd9Sstevel@tonic-gate { 259*4a37d755Sksn const char *cp; 2607c478bd9Sstevel@tonic-gate int n; 261*4a37d755Sksn 262*4a37d755Sksn static const char invaltab[] = { 263*4a37d755Sksn "/\\:*?<>|\"" 2647c478bd9Sstevel@tonic-gate }; 2657c478bd9Sstevel@tonic-gate 266*4a37d755Sksn cp = invaltab; 267*4a37d755Sksn n = sizeof (invaltab) - 1; 2687c478bd9Sstevel@tonic-gate while (n--) { 2697c478bd9Sstevel@tonic-gate if (c == *cp++) 270*4a37d755Sksn return (0); 2717c478bd9Sstevel@tonic-gate } 272*4a37d755Sksn return (1); 2737c478bd9Sstevel@tonic-gate } 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate int 276*4a37d755Sksn pc_valid_long_fn(char *namep, int utf8) 2777c478bd9Sstevel@tonic-gate { 2787c478bd9Sstevel@tonic-gate char *tmp; 2797c478bd9Sstevel@tonic-gate 280*4a37d755Sksn if (utf8) { 281*4a37d755Sksn /* UTF-8 */ 282*4a37d755Sksn for (tmp = namep; *tmp != '\0'; tmp++) 283*4a37d755Sksn if (!pc_valid_lfn_char(*tmp)) 284*4a37d755Sksn return (0); 285*4a37d755Sksn if ((tmp - namep) > PCMAXNAMLEN) 2867c478bd9Sstevel@tonic-gate return (0); 287*4a37d755Sksn } else { 288*4a37d755Sksn /* UTF-16 */ 289*4a37d755Sksn for (tmp = namep; (*tmp != '\0') && (*(tmp+1) != '\0'); 290*4a37d755Sksn tmp += 2) { 291*4a37d755Sksn if ((*(tmp+1) == '\0') && !pc_valid_lfn_char(*tmp)) 292*4a37d755Sksn return (0); 293*4a37d755Sksn } 294*4a37d755Sksn if ((tmp - namep) > (PCMAXNAMLEN * sizeof (uint16_t))) 295*4a37d755Sksn return (0); 296*4a37d755Sksn } 2977c478bd9Sstevel@tonic-gate return (1); 2987c478bd9Sstevel@tonic-gate } 2997c478bd9Sstevel@tonic-gate 3007c478bd9Sstevel@tonic-gate int 3017c478bd9Sstevel@tonic-gate pc_fname_ext_to_name(char *namep, char *fname, char *ext, int foldcase) 3027c478bd9Sstevel@tonic-gate { 3037c478bd9Sstevel@tonic-gate int i; 3047c478bd9Sstevel@tonic-gate char *tp = namep; 3057c478bd9Sstevel@tonic-gate char c; 3067c478bd9Sstevel@tonic-gate 3077c478bd9Sstevel@tonic-gate i = PCFNAMESIZE; 3087c478bd9Sstevel@tonic-gate while (i-- && ((c = *fname) != ' ')) { 3097c478bd9Sstevel@tonic-gate if (!(c == '.' || pc_validchar(c))) { 3107c478bd9Sstevel@tonic-gate return (-1); 3117c478bd9Sstevel@tonic-gate } 3127c478bd9Sstevel@tonic-gate if (foldcase) 3137c478bd9Sstevel@tonic-gate *tp++ = tolower(c); 3147c478bd9Sstevel@tonic-gate else 3157c478bd9Sstevel@tonic-gate *tp++ = c; 3167c478bd9Sstevel@tonic-gate fname++; 3177c478bd9Sstevel@tonic-gate } 3187c478bd9Sstevel@tonic-gate if (*ext != ' ') { 3197c478bd9Sstevel@tonic-gate *tp++ = '.'; 3207c478bd9Sstevel@tonic-gate i = PCFEXTSIZE; 3217c478bd9Sstevel@tonic-gate while (i-- && ((c = *ext) != ' ')) { 3227c478bd9Sstevel@tonic-gate if (!pc_validchar(c)) { 3237c478bd9Sstevel@tonic-gate return (-1); 3247c478bd9Sstevel@tonic-gate } 3257c478bd9Sstevel@tonic-gate if (foldcase) 3267c478bd9Sstevel@tonic-gate *tp++ = tolower(c); 3277c478bd9Sstevel@tonic-gate else 3287c478bd9Sstevel@tonic-gate *tp++ = c; 3297c478bd9Sstevel@tonic-gate ext++; 3307c478bd9Sstevel@tonic-gate } 3317c478bd9Sstevel@tonic-gate } 3327c478bd9Sstevel@tonic-gate *tp = '\0'; 3337c478bd9Sstevel@tonic-gate return (0); 3347c478bd9Sstevel@tonic-gate } 335