1 /*	$NetBSD: smbfs_subr.c,v 1.19 2014/11/17 02:23:33 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 2000-2001, Boris Popov
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *    This product includes software developed by Boris Popov.
18  * 4. Neither the name of the author nor the names of any co-contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * FreeBSD: src/sys/fs/smbfs/smbfs_subr.c,v 1.1 2001/04/10 07:59:05 bp Exp
35  */
36 
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: smbfs_subr.c,v 1.19 2014/11/17 02:23:33 christos Exp $");
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/malloc.h>
44 #include <sys/mount.h>
45 #include <sys/time.h>
46 #include <sys/vnode.h>
47 #include <sys/sysctl.h>
48 #include <sys/clock.h>
49 #include <netsmb/iconv.h>
50 
51 #include <netsmb/smb.h>
52 #include <netsmb/smb_conn.h>
53 #include <netsmb/smb_subr.h>
54 #include <netsmb/smb_rq.h>
55 #include <netsmb/smb_dev.h>
56 
57 #include <fs/smbfs/smbfs.h>
58 #include <fs/smbfs/smbfs_node.h>
59 #include <fs/smbfs/smbfs_subr.h>
60 
61 MALLOC_JUSTDEFINE(M_SMBFSDATA, "SMBFS data", "SMBFS private data");
62 
63 /*
64  * Time & date conversion routines taken from msdosfs. Although leap
65  * year calculation is bogus, it's sufficient before 2100 :)
66  */
67 /*
68  * This is the format of the contents of the deTime field in the direntry
69  * structure.
70  * We don't use bitfields because we don't know how compilers for
71  * arbitrary machines will lay them out.
72  */
73 #define DT_2SECONDS_MASK	0x1F	/* seconds divided by 2 */
74 #define DT_2SECONDS_SHIFT	0
75 #define DT_MINUTES_MASK		0x7E0	/* minutes */
76 #define DT_MINUTES_SHIFT	5
77 #define DT_HOURS_MASK		0xF800	/* hours */
78 #define DT_HOURS_SHIFT		11
79 
80 /*
81  * This is the format of the contents of the deDate field in the direntry
82  * structure.
83  */
84 #define DD_DAY_MASK		0x1F	/* day of month */
85 #define DD_DAY_SHIFT		0
86 #define DD_MONTH_MASK		0x1E0	/* month */
87 #define DD_MONTH_SHIFT		5
88 #define DD_YEAR_MASK		0xFE00	/* year - 1980 */
89 #define DD_YEAR_SHIFT		9
90 /*
91  * Total number of days that have passed for each month in a regular year.
92  */
93 static const u_short regyear[] = {
94 	31, 59, 90, 120, 151, 181,
95 	212, 243, 273, 304, 334, 365
96 };
97 
98 /*
99  * Total number of days that have passed for each month in a leap year.
100  */
101 static const u_short leapyear[] = {
102 	31, 60, 91, 121, 152, 182,
103 	213, 244, 274, 305, 335, 366
104 };
105 
106 /*
107  * Variables used to remember parts of the last time conversion.  Maybe we
108  * can avoid a full conversion.
109  */
110 static u_long  lasttime;
111 static u_long  lastday;
112 static u_short lastddate;
113 static u_short lastdtime;
114 
115 void
smb_time_local2server(struct timespec * tsp,int tzoff,u_long * seconds)116 smb_time_local2server(struct timespec *tsp, int tzoff, u_long *seconds)
117 {
118 	*seconds = tsp->tv_sec - tzoff * 60 /*- tz.tz_minuteswest * 60 -
119 	    (wall_cmos_clock ? adjkerntz : 0)*/;
120 }
121 
122 void
smb_time_server2local(u_long seconds,int tzoff,struct timespec * tsp)123 smb_time_server2local(u_long seconds, int tzoff, struct timespec *tsp)
124 {
125 	tsp->tv_sec = seconds + tzoff * 60;
126 	    /*+ tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0)*/;
127 }
128 
129 /*
130  * Number of seconds between 1970 and 1601 year
131  */
132 static const int64_t DIFF1970TO1601 = 11644473600ULL;
133 
134 /*
135  * Time from server comes as UTC, so no need to use tz
136  */
137 void
smb_time_NT2local(int64_t nsec,int tzoff,struct timespec * tsp)138 smb_time_NT2local(int64_t nsec, int tzoff, struct timespec *tsp)
139 {
140 	smb_time_server2local(nsec / 10000000 - DIFF1970TO1601, 0, tsp);
141 }
142 
143 void
smb_time_local2NT(struct timespec * tsp,int tzoff,int64_t * nsec)144 smb_time_local2NT(struct timespec *tsp, int tzoff, int64_t *nsec)
145 {
146 	u_long seconds;
147 
148 	smb_time_local2server(tsp, 0, &seconds);
149 	*nsec = ((int64_t)seconds + DIFF1970TO1601) * (int64_t)10000000;
150 }
151 
152 void
smb_time_unix2dos(struct timespec * tsp,int tzoff,u_int16_t * ddp,u_int16_t * dtp,u_int8_t * dhp)153 smb_time_unix2dos(struct timespec *tsp, int tzoff, u_int16_t *ddp,
154 	u_int16_t *dtp,	u_int8_t *dhp)
155 {
156 	u_long t, days, year, month, inc;
157 	const u_short *months;
158 
159 	/*
160 	 * If the time from the last conversion is the same as now, then
161 	 * skip the computations and use the saved result.
162 	 */
163 	smb_time_local2server(tsp, tzoff, &t);
164 	t &= ~1;
165 	if (lasttime != t) {
166 		lasttime = t;
167 		lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT)
168 		    + (((t / 60) % 60) << DT_MINUTES_SHIFT)
169 		    + (((t / 3600) % 24) << DT_HOURS_SHIFT);
170 
171 		/*
172 		 * If the number of days since 1970 is the same as the last
173 		 * time we did the computation then skip all this leap year
174 		 * and month stuff.
175 		 */
176 		days = t / (24 * 60 * 60);
177 		if (days != lastday) {
178 			lastday = days;
179 			for (year = 1970;; year++) {
180  				inc = days_per_year(year);
181 				if (days < inc)
182 					break;
183 				days -= inc;
184 			}
185  			months = is_leap_year(year) ? leapyear : regyear;
186 			for (month = 0; days >= months[month]; month++)
187 				;
188 			if (month > 0)
189 				days -= months[month - 1];
190 			lastddate = ((days + 1) << DD_DAY_SHIFT)
191 			    + ((month + 1) << DD_MONTH_SHIFT);
192 			/*
193 			 * Remember dos's idea of time is relative to 1980.
194 			 * unix's is relative to 1970.  If somehow we get a
195 			 * time before 1980 then don't give totally crazy
196 			 * results.
197 			 */
198 			if (year > 1980)
199 				lastddate += (year - 1980) << DD_YEAR_SHIFT;
200 		}
201 	}
202 	if (dtp)
203 		*dtp = lastdtime;
204 	if (dhp)
205 		*dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000;
206 
207 	*ddp = lastddate;
208 }
209 
210 /*
211  * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that
212  * interval there were 8 regular years and 2 leap years.
213  */
214 #define	SECONDSTO1980	(((8 * 365) + (2 * 366)) * (24 * 60 * 60))
215 
216 static u_short lastdosdate;
217 static u_long  lastseconds;
218 
219 void
smb_dos2unixtime(u_int dd,u_int dt,u_int dh,int tzoff,struct timespec * tsp)220 smb_dos2unixtime(u_int dd, u_int dt, u_int dh, int tzoff,
221 	struct timespec *tsp)
222 {
223 	u_long seconds;
224 	u_long month;
225 	u_long year;
226 	u_long days;
227 	const u_short *months;
228 
229 	if (dd == 0) {
230 		tsp->tv_sec = 0;
231 		tsp->tv_nsec = 0;
232 		return;
233 	}
234 	seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1)
235 	    + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60
236 	    + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600
237 	    + dh / 100;
238 	/*
239 	 * If the year, month, and day from the last conversion are the
240 	 * same then use the saved value.
241 	 */
242 	if (lastdosdate != dd) {
243 		lastdosdate = dd;
244 		days = 0;
245 		year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT;
246 		days = year * 365;
247 		days += year / 4 + 1;	/* add in leap days */
248 		if ((year & 0x03) == 0)
249 			days--;		/* if year is a leap year */
250 		months = year & 0x03 ? regyear : leapyear;
251 		month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT;
252 		if (month < 1 || month > 12) {
253 			month = 1;
254 		}
255 		if (month > 1)
256 			days += months[month - 2];
257 		days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1;
258 		lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980;
259 	}
260 	smb_time_server2local(seconds + lastseconds, tzoff, tsp);
261 	tsp->tv_nsec = (dh % 100) * 10000000;
262 }
263 
264 static int
smb_fphelp(struct mbchain * mbp,struct smb_vc * vcp,struct smbnode * np,int caseopt)265 smb_fphelp(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *np,
266 	int caseopt)
267 {
268 	struct smbmount *smp= np->n_mount;
269 	struct smbnode **npp = smp->sm_npstack;
270 	int i, error = 0;
271 
272 /*	mutex_enter(&smp->sm_npslock);*/
273 	i = 0;
274 	while (np->n_parent) {
275 		if (i++ == SMBFS_MAXPATHCOMP) {
276 /*			mutex_exit(&smp->sm_npslock);*/
277 			return ENAMETOOLONG;
278 		}
279 		*npp++ = np;
280 		np = VTOSMB(np->n_parent);
281 	}
282 	while (i--) {
283 		np = *--npp;
284 		error = mb_put_uint8(mbp, '\\');
285 		if (error)
286 			break;
287 		error = smb_put_dmem(mbp, vcp, np->n_name, np->n_nmlen, caseopt);
288 		if (error)
289 			break;
290 	}
291 /*	mutex_exit(&smp->sm_npslock);*/
292 	return error;
293 }
294 
295 int
smbfs_fullpath(struct mbchain * mbp,struct smb_vc * vcp,struct smbnode * dnp,const char * name,int nmlen)296 smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp,
297 	const char *name, int nmlen)
298 {
299 	int caseopt = SMB_CS_NONE;
300 	int error;
301 
302 	if (SMB_DIALECT(vcp) < SMB_DIALECT_LANMAN1_0)
303 		caseopt |= SMB_CS_UPPER;
304 	if (dnp != NULL) {
305 		error = smb_fphelp(mbp, vcp, dnp, caseopt);
306 		if (error)
307 			return error;
308 	}
309 	if (name) {
310 		error = mb_put_uint8(mbp, '\\');
311 		if (error)
312 			return error;
313 		error = smb_put_dmem(mbp, vcp, name, nmlen, caseopt);
314 		if (error)
315 			return error;
316 	}
317 	error = mb_put_uint8(mbp, 0);
318 	return error;
319 }
320 
321 int
smbfs_fname_tolocal(struct smb_vc * vcp,char * name,int * nmlen,int caseopt)322 smbfs_fname_tolocal(struct smb_vc *vcp, char *name, int *nmlen,
323     int caseopt)
324 {
325 	int error = 0;
326 	size_t ilen, olen;
327 	const char *ibuf;
328 	char *obuf, *outbuf;
329 
330 #ifdef notyet
331 	if (caseopt & SMB_CS_UPPER)
332 		iconv_convmem(vcp->vc_toupper, name, name, nmlen);
333 	else if (caseopt & SMB_CS_LOWER)
334 		iconv_convmem(vcp->vc_tolower, name, name, nmlen);
335 #endif
336 	if (vcp->vc_tolocal) {
337 		const size_t buflen = SMB_MAXNAMLEN * 2;
338 
339 		outbuf = malloc(buflen, M_SMBTEMP, M_WAITOK);
340 		if (outbuf == NULL)
341 			return ENOMEM;
342 		ilen = *nmlen;
343 		olen = buflen;
344 		ibuf = name;
345 		obuf = outbuf;
346 		error = iconv_conv(vcp->vc_tolocal, &ibuf, &ilen, &obuf, &olen);
347 		if (!error) {
348 			*nmlen = buflen - olen;
349 			memcpy(name, outbuf, *nmlen);
350 		}
351 		free(outbuf, M_SMBTEMP);
352 	}
353 	return error;
354 }
355