1 /* 2 * Copyright (c) 2000-2001, Boris Popov 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Boris Popov. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $Id: smbfs_subr.c,v 1.18 2005/02/02 00:22:23 lindak Exp $ 33 */ 34 35 /* 36 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 37 * Use is subject to license terms. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/time.h> 45 #include <sys/vnode.h> 46 #include <sys/sunddi.h> 47 48 #ifdef APPLE 49 #include <sys/smb_apple.h> 50 #include <sys/utfconv.h> 51 #include <sys/smb_iconv.h> 52 #else /* APPLE */ 53 #include <netsmb/smb_osdep.h> 54 #endif /* APPLE */ 55 56 #include <netsmb/smb.h> 57 #include <netsmb/smb_conn.h> 58 #include <netsmb/smb_subr.h> 59 #include <netsmb/smb_rq.h> 60 61 #include <smbfs/smbfs.h> 62 #include <smbfs/smbfs_node.h> 63 #include <smbfs/smbfs_subr.h> 64 65 #ifdef APPLE 66 MALLOC_DEFINE(M_SMBFSDATA, "SMBFS data", "SMBFS private data"); 67 #endif /* APPLE */ 68 69 /* 70 * Time & date conversion routines taken from msdosfs. Although leap 71 * year calculation is bogus, it's sufficient before 2100 :) 72 */ 73 /* 74 * This is the format of the contents of the deTime field in the direntry 75 * structure. 76 * We don't use bitfields because we don't know how compilers for 77 * arbitrary machines will lay them out. 78 */ 79 #define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ 80 #define DT_2SECONDS_SHIFT 0 81 #define DT_MINUTES_MASK 0x7E0 /* minutes */ 82 #define DT_MINUTES_SHIFT 5 83 #define DT_HOURS_MASK 0xF800 /* hours */ 84 #define DT_HOURS_SHIFT 11 85 86 /* 87 * This is the format of the contents of the deDate field in the direntry 88 * structure. 89 */ 90 #define DD_DAY_MASK 0x1F /* day of month */ 91 #define DD_DAY_SHIFT 0 92 #define DD_MONTH_MASK 0x1E0 /* month */ 93 #define DD_MONTH_SHIFT 5 94 #define DD_YEAR_MASK 0xFE00 /* year - 1980 */ 95 #define DD_YEAR_SHIFT 9 96 /* 97 * Total number of days that have passed for each month in a regular year. 98 */ 99 static ushort_t regyear[] = { 100 31, 59, 90, 120, 151, 181, 101 212, 243, 273, 304, 334, 365 102 }; 103 104 /* 105 * Total number of days that have passed for each month in a leap year. 106 */ 107 static ushort_t leapyear[] = { 108 31, 60, 91, 121, 152, 182, 109 213, 244, 274, 305, 335, 366 110 }; 111 112 /* 113 * Variables used to remember parts of the last time conversion. Maybe we 114 * can avoid a full conversion. 115 */ 116 static ulong_t lasttime; 117 static ulong_t lastday; 118 static ushort_t lastddate; 119 static ushort_t lastdtime; 120 121 #ifdef APPLE 122 PRIVSYM int wall_cmos_clock = 0; /* XXX */ 123 PRIVSYM int adjkerntz = 0; /* XXX */ 124 #endif /* APPLE */ 125 126 void 127 smb_time_unix2dos(struct timespec *tsp, int tzoff, u_int16_t *ddp, 128 u_int16_t *dtp, u_int8_t *dhp) 129 { 130 long t; 131 ulong_t days, year, month, inc; 132 ushort_t *months; 133 134 /* 135 * If the time from the last conversion is the same as now, then 136 * skip the computations and use the saved result. 137 */ 138 smb_time_local2server(tsp, tzoff, &t); 139 t &= ~1; 140 if (lasttime != t) { 141 lasttime = t; 142 if (t < 0) { 143 /* 144 * This is before 1970, so it's before 1980, 145 * and can't be represented as a DOS time. 146 * Just represent it as the DOS epoch. 147 */ 148 lastdtime = 0; 149 lastddate = (1 << DD_DAY_SHIFT) 150 + (1 << DD_MONTH_SHIFT) 151 + ((1980 - 1980) << DD_YEAR_SHIFT); 152 } else { 153 lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT) 154 + (((t / 60) % 60) << DT_MINUTES_SHIFT) 155 + (((t / 3600) % 24) << DT_HOURS_SHIFT); 156 157 /* 158 * If the number of days since 1970 is the same as 159 * the last time we did the computation then skip 160 * all this leap year and month stuff. 161 */ 162 days = t / (24 * 60 * 60); 163 if (days != lastday) { 164 lastday = days; 165 for (year = 1970; ; year++) { 166 /* 167 * XXX - works in 2000, but won't 168 * work in 2100. 169 */ 170 inc = year & 0x03 ? 365 : 366; 171 if (days < inc) 172 break; 173 days -= inc; 174 } 175 /* 176 * XXX - works in 2000, but won't work in 2100. 177 */ 178 months = year & 0x03 ? regyear : leapyear; 179 for (month = 0; days >= months[month]; month++) 180 ; 181 if (month > 0) 182 days -= months[month - 1]; 183 lastddate = ((days + 1) << DD_DAY_SHIFT) 184 + ((month + 1) << DD_MONTH_SHIFT); 185 /* 186 * Remember DOS's idea of time is relative 187 * to 1980, but UN*X's is relative to 1970. 188 * If somehow we get a time before 1980 then 189 * don't give totally crazy results. 190 */ 191 if (year > 1980) 192 lastddate += (year - 1980) << 193 DD_YEAR_SHIFT; 194 } 195 } 196 } 197 if (dtp) 198 *dtp = lastdtime; 199 if (dhp) 200 *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; 201 202 *ddp = lastddate; 203 } 204 205 /* 206 * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that 207 * interval there were 8 regular years and 2 leap years. 208 */ 209 #define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60)) 210 211 static ushort_t lastdosdate; 212 static ulong_t lastseconds; 213 214 void 215 smb_dos2unixtime(uint_t dd, uint_t dt, uint_t dh, int tzoff, 216 struct timespec *tsp) 217 { 218 ulong_t seconds; 219 ulong_t month; 220 ulong_t year; 221 ulong_t days; 222 ushort_t *months; 223 224 if (dd == 0) { 225 tsp->tv_sec = 0; 226 tsp->tv_nsec = 0; 227 return; 228 } 229 seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1) 230 + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60 231 + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600 232 + dh / 100; 233 /* 234 * If the year, month, and day from the last conversion are the 235 * same then use the saved value. 236 */ 237 if (lastdosdate != dd) { 238 lastdosdate = (ushort_t)dd; 239 days = 0; 240 year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT; 241 days = year * 365; 242 days += year / 4 + 1; /* add in leap days */ 243 /* 244 * XXX - works in 2000, but won't work in 2100. 245 */ 246 if ((year & 0x03) == 0) 247 days--; /* if year is a leap year */ 248 months = year & 0x03 ? regyear : leapyear; 249 month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT; 250 if (month < 1 || month > 12) { 251 month = 1; 252 } 253 if (month > 1) 254 days += months[month - 2]; 255 days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1; 256 lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980; 257 } 258 smb_time_server2local(seconds + lastseconds, tzoff, tsp); 259 tsp->tv_nsec = (dh % 100) * 10000000; 260 } 261 262 /* 263 * In the Darwin code, this function used to compute the full path 264 * by following the chain of n_parent pointers back to the root. 265 * In the Solaris port we found the n_parent pointers inconvenient 266 * because they hold parent nodes busy. We now keep the full path 267 * in every node, so this function need only marshall the directory 268 * path, and (if provided) the separator and last component name. 269 * 270 * Note that this logic must match that in smbfs_getino 271 */ 272 int 273 smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp, 274 const char *name, int *lenp, u_int8_t sep) 275 { 276 int caseopt = SMB_CS_NONE; 277 int error, len = 0; 278 int unicode = (SMB_UNICODE_STRINGS(vcp)) ? 1 : 0; 279 280 if (SMB_DIALECT(vcp) < SMB_DIALECT_LANMAN1_0) 281 caseopt |= SMB_CS_UPPER; 282 283 if (lenp) { 284 len = *lenp; 285 *lenp = 0; 286 } 287 if (unicode) { 288 error = mb_put_padbyte(mbp); 289 if (error) 290 return (error); 291 } 292 293 error = smb_put_dmem(mbp, vcp, 294 dnp->n_rpath, dnp->n_rplen, 295 caseopt, lenp); 296 if (name) { 297 /* 298 * Special case at share root: 299 * Don't put another slash. 300 */ 301 if (dnp->n_rplen <= 1 && sep == '\\') 302 sep = 0; 303 /* 304 * More special cases, now for XATTR: 305 * Our "faked up" XATTR directories use a 306 * full path name ending with ":" so as to 307 * avoid conflicts with any real paths. 308 * (It is not a valid CIFS path name.) 309 * Therefore, when we're composing a full 310 * path name from an XATTR directory, we 311 * need to _ommit_ the ":" separator and 312 * instead copy the one from the "fake" 313 * parent node's path name. 314 */ 315 if (dnp->n_flag & N_XATTR) 316 sep = 0; 317 318 if (sep) { 319 /* Put the separator */ 320 if (unicode) 321 error = mb_put_uint16le(mbp, sep); 322 else 323 error = mb_put_uint8(mbp, sep); 324 if (!error && lenp) 325 *lenp += (unicode + 1); 326 if (error) 327 return (error); 328 } 329 /* Put the name */ 330 error = smb_put_dmem(mbp, vcp, 331 name, len, caseopt, lenp); 332 if (error) 333 return (error); 334 } 335 /* Put NULL termination. */ 336 if (unicode) 337 error = mb_put_uint16le(mbp, 0); 338 else 339 error = mb_put_uint8(mbp, 0); 340 if (!error && lenp) 341 *lenp += (unicode + 1); 342 343 return (error); 344 } 345 346 void 347 smbfs_fname_tolocal(struct smbfs_fctx *ctx) 348 { 349 int length; 350 struct smb_vc *vcp = SSTOVC(ctx->f_ssp); 351 uchar_t *dst; 352 const ushort_t *src; 353 size_t inlen, outlen; 354 int flags = 0; 355 356 if (ctx->f_nmlen == 0) 357 return; 358 359 /* XXX: This is temporary, right? Need iconv... */ 360 if (!SMB_UNICODE_STRINGS(vcp)) 361 return; 362 363 /* 364 * In Unix, the UTF-8 name can be larger and 365 * in-place conversions are not supported. 366 * Note: 3,9 are the maximum UTF-8 expansion 367 * factors when converting strings from UTF-16 368 * XXX: This was removed. REVISIT 369 */ 370 if (SMB_UNICODE_STRINGS(vcp)) 371 length = ctx->f_nmlen * 9; /* why 9 */ 372 else 373 length = ctx->f_nmlen * 3; /* why 3 */ 374 length = min(length, SMB_MAXFNAMELEN); 375 376 dst = kmem_zalloc(length, KM_SLEEP); 377 outlen = length; 378 /*LINTED*/ 379 src = (const ushort_t *)ctx->f_name; 380 inlen = ctx->f_nmlen / 2; /* need number of UCS-2 characters */ 381 flags |= UCONV_IN_LITTLE_ENDIAN; 382 383 if (uconv_u16tou8(src, &inlen, dst, &outlen, flags) == 0) { 384 kmem_free(ctx->f_name, ctx->f_namesz); 385 ctx->f_name = (char *)dst; 386 ctx->f_namesz = length; 387 ctx->f_nmlen = (int)outlen; 388 } else 389 kmem_free(dst, length); 390 } 391