xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c (revision 628e3cbed6489fa1db545d8524a06cd6535af456)
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