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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 | |