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