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 2009 Sun Microsystems, Inc. All rights reserved. 37 * Use is subject to license terms. 38 */ 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/time.h> 43 #include <sys/vnode.h> 44 #include <sys/sunddi.h> 45 46 #ifdef APPLE 47 #include <sys/smb_apple.h> 48 #include <sys/utfconv.h> 49 #include <sys/smb_iconv.h> 50 #else /* APPLE */ 51 #include <netsmb/smb_osdep.h> 52 #endif /* APPLE */ 53 54 #include <netsmb/smb.h> 55 #include <netsmb/smb_conn.h> 56 #include <netsmb/smb_subr.h> 57 #include <netsmb/smb_rq.h> 58 59 #include <smbfs/smbfs.h> 60 #include <smbfs/smbfs_node.h> 61 #include <smbfs/smbfs_subr.h> 62 63 #ifdef APPLE 64 MALLOC_DEFINE(M_SMBFSDATA, "SMBFS data", "SMBFS private data"); 65 #endif /* APPLE */ 66 67 /* 68 * Time & date conversion routines taken from msdosfs. Although leap 69 * year calculation is bogus, it's sufficient before 2100 :) 70 */ 71 /* 72 * This is the format of the contents of the deTime field in the direntry 73 * structure. 74 * We don't use bitfields because we don't know how compilers for 75 * arbitrary machines will lay them out. 76 */ 77 #define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ 78 #define DT_2SECONDS_SHIFT 0 79 #define DT_MINUTES_MASK 0x7E0 /* minutes */ 80 #define DT_MINUTES_SHIFT 5 81 #define DT_HOURS_MASK 0xF800 /* hours */ 82 #define DT_HOURS_SHIFT 11 83 84 /* 85 * This is the format of the contents of the deDate field in the direntry 86 * structure. 87 */ 88 #define DD_DAY_MASK 0x1F /* day of month */ 89 #define DD_DAY_SHIFT 0 90 #define DD_MONTH_MASK 0x1E0 /* month */ 91 #define DD_MONTH_SHIFT 5 92 #define DD_YEAR_MASK 0xFE00 /* year - 1980 */ 93 #define DD_YEAR_SHIFT 9 94 /* 95 * Total number of days that have passed for each month in a regular year. 96 */ 97 static ushort_t regyear[] = { 98 31, 59, 90, 120, 151, 181, 99 212, 243, 273, 304, 334, 365 100 }; 101 102 /* 103 * Total number of days that have passed for each month in a leap year. 104 */ 105 static ushort_t leapyear[] = { 106 31, 60, 91, 121, 152, 182, 107 213, 244, 274, 305, 335, 366 108 }; 109 110 /* 111 * Variables used to remember parts of the last time conversion. Maybe we 112 * can avoid a full conversion. 113 */ 114 static ulong_t lasttime; 115 static ulong_t lastday; 116 static ushort_t lastddate; 117 static ushort_t lastdtime; 118 119 #ifdef APPLE 120 PRIVSYM int wall_cmos_clock = 0; /* XXX */ 121 PRIVSYM int adjkerntz = 0; /* XXX */ 122 #endif /* APPLE */ 123 124 void 125 smb_time_unix2dos(struct timespec *tsp, int tzoff, u_int16_t *ddp, 126 u_int16_t *dtp, u_int8_t *dhp) 127 { 128 long t; 129 ulong_t days, year, month, inc; 130 ushort_t *months; 131 132 /* 133 * If the time from the last conversion is the same as now, then 134 * skip the computations and use the saved result. 135 */ 136 smb_time_local2server(tsp, tzoff, &t); 137 t &= ~1; 138 if (lasttime != t) { 139 lasttime = t; 140 if (t < 0) { 141 /* 142 * This is before 1970, so it's before 1980, 143 * and can't be represented as a DOS time. 144 * Just represent it as the DOS epoch. 145 */ 146 lastdtime = 0; 147 lastddate = (1 << DD_DAY_SHIFT) 148 + (1 << DD_MONTH_SHIFT) 149 + ((1980 - 1980) << DD_YEAR_SHIFT); 150 } else { 151 lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT) 152 + (((t / 60) % 60) << DT_MINUTES_SHIFT) 153 + (((t / 3600) % 24) << DT_HOURS_SHIFT); 154 155 /* 156 * If the number of days since 1970 is the same as 157 * the last time we did the computation then skip 158 * all this leap year and month stuff. 159 */ 160 days = t / (24 * 60 * 60); 161 if (days != lastday) { 162 lastday = days; 163 for (year = 1970; ; year++) { 164 /* 165 * XXX - works in 2000, but won't 166 * work in 2100. 167 */ 168 inc = year & 0x03 ? 365 : 366; 169 if (days < inc) 170 break; 171 days -= inc; 172 } 173 /* 174 * XXX - works in 2000, but won't work in 2100. 175 */ 176 months = year & 0x03 ? regyear : leapyear; 177 for (month = 0; days >= months[month]; month++) 178 ; 179 if (month > 0) 180 days -= months[month - 1]; 181 lastddate = ((days + 1) << DD_DAY_SHIFT) 182 + ((month + 1) << DD_MONTH_SHIFT); 183 /* 184 * Remember DOS's idea of time is relative 185 * to 1980, but UN*X's is relative to 1970. 186 * If somehow we get a time before 1980 then 187 * don't give totally crazy results. 188 */ 189 if (year > 1980) 190 lastddate += (year - 1980) << 191 DD_YEAR_SHIFT; 192 } 193 } 194 } 195 if (dtp) 196 *dtp = lastdtime; 197 if (dhp) 198 *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; 199 200 *ddp = lastddate; 201 } 202 203 /* 204 * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that 205 * interval there were 8 regular years and 2 leap years. 206 */ 207 #define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60)) 208 209 static ushort_t lastdosdate; 210 static ulong_t lastseconds; 211 212 void 213 smb_dos2unixtime(uint_t dd, uint_t dt, uint_t dh, int tzoff, 214 struct timespec *tsp) 215 { 216 ulong_t seconds; 217 ulong_t month; 218 ulong_t year; 219 ulong_t days; 220 ushort_t *months; 221 222 if (dd == 0) { 223 tsp->tv_sec = 0; 224 tsp->tv_nsec = 0; 225 return; 226 } 227 seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1) 228 + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60 229 + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600 230 + dh / 100; 231 /* 232 * If the year, month, and day from the last conversion are the 233 * same then use the saved value. 234 */ 235 if (lastdosdate != dd) { 236 lastdosdate = (ushort_t)dd; 237 days = 0; 238 year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT; 239 days = year * 365; 240 days += year / 4 + 1; /* add in leap days */ 241 /* 242 * XXX - works in 2000, but won't work in 2100. 243 */ 244 if ((year & 0x03) == 0) 245 days--; /* if year is a leap year */ 246 months = year & 0x03 ? regyear : leapyear; 247 month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT; 248 if (month < 1 || month > 12) { 249 month = 1; 250 } 251 if (month > 1) 252 days += months[month - 2]; 253 days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1; 254 lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980; 255 } 256 smb_time_server2local(seconds + lastseconds, tzoff, tsp); 257 tsp->tv_nsec = (dh % 100) * 10000000; 258 } 259 260 /* 261 * In the Darwin code, this function used to compute the full path 262 * by following the chain of n_parent pointers back to the root. 263 * In the Solaris port we found the n_parent pointers inconvenient 264 * because they hold parent nodes busy. We now keep the full path 265 * in every node, so this function need only marshall the directory 266 * path, and (if provided) the separator and last component name. 267 * 268 * Note that this logic must match that in smbfs_getino 269 */ 270 int 271 smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp, 272 const char *name, int *lenp, u_int8_t sep) 273 { 274 int caseopt = SMB_CS_NONE; 275 int error, len = 0; 276 int unicode = (SMB_UNICODE_STRINGS(vcp)) ? 1 : 0; 277 278 if (SMB_DIALECT(vcp) < SMB_DIALECT_LANMAN1_0) 279 caseopt |= SMB_CS_UPPER; 280 281 if (lenp) { 282 len = *lenp; 283 *lenp = 0; 284 } 285 if (unicode) { 286 error = mb_put_padbyte(mbp); 287 if (error) 288 return (error); 289 } 290 291 error = smb_put_dmem(mbp, vcp, 292 dnp->n_rpath, dnp->n_rplen, 293 caseopt, lenp); 294 if (name) { 295 /* 296 * Special case at share root: 297 * Don't put another slash. 298 */ 299 if (dnp->n_rplen <= 1 && sep == '\\') 300 sep = 0; 301 /* 302 * More special cases, now for XATTR: 303 * Our "faked up" XATTR directories use a 304 * full path name ending with ":" so as to 305 * avoid conflicts with any real paths. 306 * (It is not a valid CIFS path name.) 307 * Therefore, when we're composing a full 308 * path name from an XATTR directory, we 309 * need to _ommit_ the ":" separator and 310 * instead copy the one from the "fake" 311 * parent node's path name. 312 */ 313 if (dnp->n_flag & N_XATTR) 314 sep = 0; 315 316 if (sep) { 317 /* Put the separator */ 318 if (unicode) 319 error = mb_put_uint16le(mbp, sep); 320 else 321 error = mb_put_uint8(mbp, sep); 322 if (!error && lenp) 323 *lenp += (unicode + 1); 324 if (error) 325 return (error); 326 } 327 /* Put the name */ 328 error = smb_put_dmem(mbp, vcp, 329 name, len, caseopt, lenp); 330 if (error) 331 return (error); 332 } 333 /* Put NULL termination. */ 334 if (unicode) 335 error = mb_put_uint16le(mbp, 0); 336 else 337 error = mb_put_uint8(mbp, 0); 338 if (!error && lenp) 339 *lenp += (unicode + 1); 340 341 return (error); 342 } 343 344 /* 345 * Convert a Unicode directory entry to UTF-8 346 */ 347 void 348 smbfs_fname_tolocal(struct smbfs_fctx *ctx) 349 { 350 uchar_t tmpbuf[SMB_MAXFNAMELEN+1]; 351 struct smb_vc *vcp = SSTOVC(ctx->f_ssp); 352 uchar_t *dst; 353 const ushort_t *src; 354 size_t inlen, outlen; 355 int flags; 356 357 if (ctx->f_nmlen == 0) 358 return; 359 360 if (!SMB_UNICODE_STRINGS(vcp)) 361 return; 362 363 if (ctx->f_namesz < sizeof (tmpbuf)) { 364 ASSERT(0); 365 goto errout; 366 } 367 368 /* 369 * In-place conversions are not supported, 370 * so convert into tmpbuf and copy. 371 */ 372 dst = tmpbuf; 373 outlen = SMB_MAXFNAMELEN; 374 /*LINTED*/ 375 src = (const ushort_t *)ctx->f_name; 376 inlen = ctx->f_nmlen / 2; /* number of UCS-2 characters */ 377 flags = UCONV_IN_LITTLE_ENDIAN; 378 379 if (uconv_u16tou8(src, &inlen, dst, &outlen, flags) != 0) 380 goto errout; 381 382 ASSERT(outlen < sizeof (tmpbuf)); 383 tmpbuf[outlen] = '\0'; 384 bcopy(tmpbuf, ctx->f_name, outlen + 1); 385 ctx->f_nmlen = (int)outlen; 386 return; 387 388 errout: 389 /* 390 * Conversion failed, but our caller does not 391 * deal with errors here, so... (hack). 392 * Don't expect to ever see this. 393 */ 394 (void) strlcpy(ctx->f_name, "?", ctx->f_namesz); 395 } 396