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 * $FreeBSD$ 33 */ 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/kernel.h> 37 #include <sys/malloc.h> 38 #include <sys/time.h> 39 #include <sys/vnode.h> 40 #include <sys/sysctl.h> 41 #include <sys/iconv.h> 42 43 #include <netsmb/smb.h> 44 #include <netsmb/smb_conn.h> 45 #include <netsmb/smb_subr.h> 46 #include <netsmb/smb_rq.h> 47 #include <netsmb/smb_dev.h> 48 49 #include <fs/smbfs/smbfs.h> 50 #include <fs/smbfs/smbfs_node.h> 51 #include <fs/smbfs/smbfs_subr.h> 52 53 MALLOC_DEFINE(M_SMBFSDATA, "smbfs_data", "SMBFS private data"); 54 55 /* 56 * Time & date conversion routines taken from msdosfs. Although leap 57 * year calculation is bogus, it's sufficient before 2100 :) 58 */ 59 /* 60 * This is the format of the contents of the deTime field in the direntry 61 * structure. 62 * We don't use bitfields because we don't know how compilers for 63 * arbitrary machines will lay them out. 64 */ 65 #define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ 66 #define DT_2SECONDS_SHIFT 0 67 #define DT_MINUTES_MASK 0x7E0 /* minutes */ 68 #define DT_MINUTES_SHIFT 5 69 #define DT_HOURS_MASK 0xF800 /* hours */ 70 #define DT_HOURS_SHIFT 11 71 72 /* 73 * This is the format of the contents of the deDate field in the direntry 74 * structure. 75 */ 76 #define DD_DAY_MASK 0x1F /* day of month */ 77 #define DD_DAY_SHIFT 0 78 #define DD_MONTH_MASK 0x1E0 /* month */ 79 #define DD_MONTH_SHIFT 5 80 #define DD_YEAR_MASK 0xFE00 /* year - 1980 */ 81 #define DD_YEAR_SHIFT 9 82 /* 83 * Total number of days that have passed for each month in a regular year. 84 */ 85 static u_short regyear[] = { 86 31, 59, 90, 120, 151, 181, 87 212, 243, 273, 304, 334, 365 88 }; 89 90 /* 91 * Total number of days that have passed for each month in a leap year. 92 */ 93 static u_short leapyear[] = { 94 31, 60, 91, 121, 152, 182, 95 213, 244, 274, 305, 335, 366 96 }; 97 98 /* 99 * Variables used to remember parts of the last time conversion. Maybe we 100 * can avoid a full conversion. 101 */ 102 static u_long lasttime; 103 static u_long lastday; 104 static u_short lastddate; 105 static u_short lastdtime; 106 107 void 108 smb_time_local2server(struct timespec *tsp, int tzoff, u_long *seconds) 109 { 110 *seconds = tsp->tv_sec - tzoff * 60 /*- tz_minuteswest * 60 - 111 (wall_cmos_clock ? adjkerntz : 0)*/; 112 } 113 114 void 115 smb_time_server2local(u_long seconds, int tzoff, struct timespec *tsp) 116 { 117 tsp->tv_sec = seconds + tzoff * 60; 118 /*+ tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0)*/; 119 } 120 121 /* 122 * Number of seconds between 1970 and 1601 year 123 */ 124 static int64_t DIFF1970TO1601 = 11644473600ULL; 125 126 /* 127 * Time from server comes as UTC, so no need to use tz 128 */ 129 void 130 smb_time_NT2local(int64_t nsec, int tzoff, struct timespec *tsp) 131 { 132 smb_time_server2local(nsec / 10000000 - DIFF1970TO1601, 0, tsp); 133 } 134 135 void 136 smb_time_local2NT(struct timespec *tsp, int tzoff, int64_t *nsec) 137 { 138 u_long seconds; 139 140 smb_time_local2server(tsp, 0, &seconds); 141 *nsec = (((int64_t)(seconds) & ~1) + DIFF1970TO1601) * (int64_t)10000000; 142 } 143 144 void 145 smb_time_unix2dos(struct timespec *tsp, int tzoff, u_int16_t *ddp, 146 u_int16_t *dtp, u_int8_t *dhp) 147 { 148 u_long t, days, year, month, inc; 149 u_short *months; 150 151 /* 152 * If the time from the last conversion is the same as now, then 153 * skip the computations and use the saved result. 154 */ 155 smb_time_local2server(tsp, tzoff, &t); 156 t &= ~1; 157 if (lasttime != t) { 158 lasttime = t; 159 lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT) 160 + (((t / 60) % 60) << DT_MINUTES_SHIFT) 161 + (((t / 3600) % 24) << DT_HOURS_SHIFT); 162 163 /* 164 * If the number of days since 1970 is the same as the last 165 * time we did the computation then skip all this leap year 166 * and month stuff. 167 */ 168 days = t / (24 * 60 * 60); 169 if (days != lastday) { 170 lastday = days; 171 for (year = 1970;; year++) { 172 inc = year & 0x03 ? 365 : 366; 173 if (days < inc) 174 break; 175 days -= inc; 176 } 177 months = year & 0x03 ? regyear : leapyear; 178 for (month = 0; days >= months[month]; month++) 179 ; 180 if (month > 0) 181 days -= months[month - 1]; 182 lastddate = ((days + 1) << DD_DAY_SHIFT) 183 + ((month + 1) << DD_MONTH_SHIFT); 184 /* 185 * Remember dos's idea of time is relative to 1980. 186 * unix's is relative to 1970. If somehow we get a 187 * time before 1980 then don't give totally crazy 188 * results. 189 */ 190 if (year > 1980) 191 lastddate += (year - 1980) << DD_YEAR_SHIFT; 192 } 193 } 194 if (dtp) 195 *dtp = lastdtime; 196 if (dhp) 197 *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; 198 199 *ddp = lastddate; 200 } 201 202 /* 203 * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that 204 * interval there were 8 regular years and 2 leap years. 205 */ 206 #define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60)) 207 208 static u_short lastdosdate; 209 static u_long lastseconds; 210 211 void 212 smb_dos2unixtime(u_int dd, u_int dt, u_int dh, int tzoff, 213 struct timespec *tsp) 214 { 215 u_long seconds; 216 u_long month; 217 u_long year; 218 u_long days; 219 u_short *months; 220 221 if (dd == 0) { 222 tsp->tv_sec = 0; 223 tsp->tv_nsec = 0; 224 return; 225 } 226 seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1) 227 + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60 228 + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600 229 + dh / 100; 230 /* 231 * If the year, month, and day from the last conversion are the 232 * same then use the saved value. 233 */ 234 if (lastdosdate != dd) { 235 lastdosdate = dd; 236 days = 0; 237 year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT; 238 days = year * 365; 239 days += year / 4 + 1; /* add in leap days */ 240 if ((year & 0x03) == 0) 241 days--; /* if year is a leap year */ 242 months = year & 0x03 ? regyear : leapyear; 243 month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT; 244 if (month < 1 || month > 12) { 245 month = 1; 246 } 247 if (month > 1) 248 days += months[month - 2]; 249 days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1; 250 lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980; 251 } 252 smb_time_server2local(seconds + lastseconds, tzoff, tsp); 253 tsp->tv_nsec = (dh % 100) * 10000000; 254 } 255 256 static int 257 smb_fphelp(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *np, 258 int caseopt) 259 { 260 struct smbmount *smp= np->n_mount; 261 struct smbnode **npp = smp->sm_npstack; 262 int i, error = 0; 263 264 /* simple_lock(&smp->sm_npslock);*/ 265 i = 0; 266 while (np->n_parent) { 267 if (i++ == SMBFS_MAXPATHCOMP) { 268 /* simple_unlock(&smp->sm_npslock);*/ 269 return ENAMETOOLONG; 270 } 271 *npp++ = np; 272 if ((np->n_flag & NREFPARENT) == 0) 273 break; 274 np = VTOSMB(np->n_parent); 275 } 276 /* if (i == 0) 277 return smb_put_dmem(mbp, vcp, "\\", 2, caseopt);*/ 278 while (i--) { 279 np = *--npp; 280 error = mb_put_uint8(mbp, '\\'); 281 if (error) 282 break; 283 error = smb_put_dmem(mbp, vcp, np->n_name, np->n_nmlen, caseopt); 284 if (error) 285 break; 286 } 287 /* simple_unlock(&smp->sm_npslock);*/ 288 return error; 289 } 290 291 int 292 smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp, 293 const char *name, int nmlen) 294 { 295 int caseopt = SMB_CS_NONE; 296 int error; 297 298 if (SMB_DIALECT(vcp) < SMB_DIALECT_LANMAN1_0) 299 caseopt |= SMB_CS_UPPER; 300 if (dnp != NULL) { 301 error = smb_fphelp(mbp, vcp, dnp, caseopt); 302 if (error) 303 return error; 304 } 305 if (name) { 306 error = mb_put_uint8(mbp, '\\'); 307 if (error) 308 return error; 309 error = smb_put_dmem(mbp, vcp, name, nmlen, caseopt); 310 if (error) 311 return error; 312 } 313 error = mb_put_uint8(mbp, 0); 314 return error; 315 } 316 317 int 318 smbfs_fname_tolocal(struct smb_vc *vcp, char *name, int *nmlen, int caseopt) 319 { 320 int copt = (caseopt == SMB_CS_LOWER ? KICONV_FROM_LOWER : 321 (caseopt == SMB_CS_UPPER ? KICONV_FROM_UPPER : 0)); 322 int error = 0; 323 size_t ilen = *nmlen; 324 size_t olen; 325 char *ibuf = name; 326 char outbuf[SMB_MAXFNAMELEN]; 327 char *obuf = outbuf; 328 329 if (vcp->vc_tolocal) { 330 olen = sizeof(outbuf); 331 bzero(outbuf, sizeof(outbuf)); 332 333 /* 334 error = iconv_conv_case 335 (vcp->vc_tolocal, NULL, NULL, &obuf, &olen, copt); 336 if (error) return error; 337 */ 338 339 error = iconv_conv_case 340 (vcp->vc_tolocal, (const char **)&ibuf, &ilen, &obuf, &olen, copt); 341 if (!error) { 342 *nmlen = sizeof(outbuf) - olen; 343 memcpy(name, outbuf, *nmlen); 344 } 345 } 346 return error; 347 } 348