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 * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
40 */
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 #include <netsmb/smb_osdep.h>
49
50 #include <netsmb/smb.h>
51 #include <netsmb/smb2.h>
52 #include <netsmb/smb_conn.h>
53 #include <netsmb/smb_subr.h>
54 #include <netsmb/smb_rq.h>
55
56 #include <smbfs/smbfs.h>
57 #include <smbfs/smbfs_node.h>
58 #include <smbfs/smbfs_subr.h>
59
60 /*
61 * In the Darwin code, this function used to compute the full path
62 * by following the chain of n_parent pointers back to the root.
63 * In the Solaris port we found the n_parent pointers inconvenient
64 * because they hold parent nodes busy. We now keep the full path
65 * in every node, so this function need only marshall the directory
66 * path, and (if provided) the separator and last component name.
67 *
68 * Note that this logic must match that in smbfs_getino
69 */
70 int
smbfs_fullpath(struct mbchain * mbp,struct smb_vc * vcp,struct smbnode * dnp,const char * name,int nmlen,u_int8_t sep)71 smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp,
72 const char *name, int nmlen, u_int8_t sep)
73 {
74 int caseopt = SMB_CS_NONE;
75 int unicode = (SMB_UNICODE_STRINGS(vcp)) ? 1 : 0;
76 int error;
77
78 /*
79 * SMB1 may need an alignment pad before (not SMB2)
80 */
81 if (((vcp)->vc_flags & SMBV_SMB2) == 0 &&
82 ((vcp)->vc_hflags2 & SMB_FLAGS2_UNICODE) != 0) {
83 error = mb_put_padbyte(mbp);
84 if (error)
85 return (error);
86 }
87
88 error = smb_put_dmem(mbp, vcp,
89 dnp->n_rpath, dnp->n_rplen,
90 caseopt, NULL);
91 if (name) {
92 /*
93 * Special case at share root:
94 * Don't put another slash.
95 */
96 if (dnp->n_rplen <= 1 && sep == '\\')
97 sep = 0;
98 /*
99 * More special cases, now for XATTR:
100 * Our "faked up" XATTR directories use a
101 * full path name ending with ":" so as to
102 * avoid conflicts with any real paths.
103 * (It is not a valid CIFS path name.)
104 * Therefore, when we're composing a full
105 * path name from an XATTR directory, we
106 * need to _ommit_ the ":" separator and
107 * instead copy the one from the "fake"
108 * parent node's path name.
109 */
110 if (dnp->n_flag & N_XATTR)
111 sep = 0;
112
113 if (sep) {
114 /* Put the separator */
115 if (unicode)
116 error = mb_put_uint16le(mbp, sep);
117 else
118 error = mb_put_uint8(mbp, sep);
119 if (error)
120 return (error);
121 }
122 /* Put the name */
123 error = smb_put_dmem(mbp, vcp,
124 name, nmlen, caseopt, NULL);
125 if (error)
126 return (error);
127 }
128
129 /* SMB1 wants NULL termination. */
130 if (((vcp)->vc_flags & SMBV_SMB2) == 0) {
131 if (unicode)
132 error = mb_put_uint16le(mbp, 0);
133 else
134 error = mb_put_uint8(mbp, 0);
135 }
136
137 return (error);
138 }
139
140 /*
141 * Convert a Unicode directory entry to UTF-8
142 */
143 void
smbfs_fname_tolocal(struct smbfs_fctx * ctx)144 smbfs_fname_tolocal(struct smbfs_fctx *ctx)
145 {
146 uchar_t tmpbuf[SMB_MAXFNAMELEN+1];
147 struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
148 uchar_t *dst;
149 const ushort_t *src;
150 size_t inlen, outlen;
151 int flags;
152
153 if (ctx->f_nmlen == 0)
154 return;
155
156 if (!SMB_UNICODE_STRINGS(vcp))
157 return;
158
159 if (ctx->f_namesz < sizeof (tmpbuf)) {
160 ASSERT(0);
161 goto errout;
162 }
163
164 /*
165 * In-place conversions are not supported,
166 * so convert into tmpbuf and copy.
167 */
168 dst = tmpbuf;
169 outlen = SMB_MAXFNAMELEN;
170 /*LINTED*/
171 src = (const ushort_t *)ctx->f_name;
172 inlen = ctx->f_nmlen / 2; /* number of UCS-2 characters */
173 flags = UCONV_IN_LITTLE_ENDIAN;
174
175 if (uconv_u16tou8(src, &inlen, dst, &outlen, flags) != 0)
176 goto errout;
177
178 ASSERT(outlen < sizeof (tmpbuf));
179 tmpbuf[outlen] = '\0';
180 bcopy(tmpbuf, ctx->f_name, outlen + 1);
181 ctx->f_nmlen = (int)outlen;
182 return;
183
184 errout:
185 /*
186 * Conversion failed, but our caller does not
187 * deal with errors here, so just put a "?".
188 * Don't expect to ever see this.
189 */
190 (void) strlcpy(ctx->f_name, "?", ctx->f_namesz);
191 }
192
193 /*
194 * Decode a directory entry from OtW form into ctx->f_attr
195 *
196 * Caller already put some (wire-format) directory entries
197 * into ctx->f_mdchain and we expect to find one.
198 *
199 * Advancing correctly through the buffer can be tricky if one
200 * tries to add up the size of an entry as you go (which is how
201 * the darwin code this is derived from did it). The easiest way
202 * to correctly advance the position is to get a whole dirent
203 * into another mdchain (entry_mdc) based on NextEntryOffset,
204 * and then scan the data from that mdchain. On the last entry,
205 * we don't know the entire length, so just scan directly from
206 * what remains of the multi-entry buffer instead of trying to
207 * figure out the length to copy into a separate mdchain.
208 */
209 int
smbfs_decode_dirent(struct smbfs_fctx * ctx)210 smbfs_decode_dirent(struct smbfs_fctx *ctx)
211 {
212 struct mdchain entry_mdc;
213 struct mdchain *mdp = &ctx->f_mdchain;
214 size_t nmlen;
215 uint64_t llongint;
216 uint32_t nmsize, dattr;
217 uint32_t nextoff = 0;
218 int error;
219
220 /* In case we error out... */
221 ctx->f_nmlen = 0;
222 ctx->f_rkey = (uint32_t)-1;
223 bzero(&entry_mdc, sizeof (entry_mdc));
224
225 /*
226 * Setup mdp to point to an mbchain holding
227 * what should be a single directory entry.
228 */
229 error = md_get_uint32le(mdp, &nextoff);
230 if (error != 0)
231 goto errout;
232 if (nextoff >= 4) {
233 /*
234 * More entries follow. Make a new mbchain
235 * holding just this one entry, then advance.
236 */
237 mblk_t *m = NULL;
238 error = md_get_mbuf(mdp, nextoff - 4, &m);
239 if (error != 0)
240 goto errout;
241 md_initm(&entry_mdc, m);
242 mdp = &entry_mdc;
243 ctx->f_eofs += nextoff;
244 } else {
245 /* Scan directly from ctx->f_mdchain */
246 ctx->f_eofs = ctx->f_left;
247 }
248
249 /*
250 * Decode the fixed-size parts
251 */
252 switch (ctx->f_infolevel) {
253 case FileFullDirectoryInformation:
254 case SMB_FIND_FULL_DIRECTORY_INFO:
255 md_get_uint32le(mdp, &ctx->f_rkey); /* resume key (idx) */
256 md_get_uint64le(mdp, &llongint); /* creation time */
257 smb_time_NT2local(llongint, &ctx->f_attr.fa_createtime);
258 md_get_uint64le(mdp, &llongint);
259 smb_time_NT2local(llongint, &ctx->f_attr.fa_atime);
260 md_get_uint64le(mdp, &llongint);
261 smb_time_NT2local(llongint, &ctx->f_attr.fa_mtime);
262 md_get_uint64le(mdp, &llongint);
263 smb_time_NT2local(llongint, &ctx->f_attr.fa_ctime);
264 md_get_uint64le(mdp, &llongint); /* file size */
265 ctx->f_attr.fa_size = llongint;
266 md_get_uint64le(mdp, &llongint); /* alloc. size */
267 ctx->f_attr.fa_allocsz = llongint;
268 md_get_uint32le(mdp, &dattr); /* ext. file attributes */
269 ctx->f_attr.fa_attr = dattr;
270 error = md_get_uint32le(mdp, &nmsize); /* name size (otw) */
271 if (error)
272 goto errout;
273 md_get_uint32le(mdp, NULL); /* Ea size */
274 break;
275
276 case FileStreamInformation:
277 error = md_get_uint32le(mdp, &nmsize); /* name size (otw) */
278 md_get_uint64le(mdp, &llongint); /* file size */
279 ctx->f_attr.fa_size = llongint;
280 md_get_uint64le(mdp, &llongint); /* alloc. size */
281 ctx->f_attr.fa_allocsz = llongint;
282 /*
283 * Stream names start with a ':' that we want to skip.
284 * This is the easiest place to take care of that.
285 * Always unicode here.
286 */
287 if (nmsize >= 2) {
288 struct mdchain save_mdc;
289 uint16_t wch;
290 save_mdc = *mdp;
291 md_get_uint16le(mdp, &wch);
292 if (wch == ':') {
293 /* OK, we skipped the ':' */
294 nmsize -= 2;
295 } else {
296 SMBVDEBUG("No leading : in stream?\n");
297 /* restore position */
298 *mdp = save_mdc;
299 }
300 }
301 break;
302
303 default:
304 SMBVDEBUG("unexpected info level %d\n", ctx->f_infolevel);
305 error = EINVAL;
306 goto errout;
307 }
308
309 /*
310 * Get the filename, and convert to utf-8
311 * Allocated f_name in findopen
312 */
313 nmlen = ctx->f_namesz;
314 error = smb_get_dstring(mdp, SSTOVC(ctx->f_ssp),
315 ctx->f_name, &nmlen, nmsize);
316 if (error != 0)
317 goto errout;
318 ctx->f_nmlen = (int)nmlen;
319 md_done(&entry_mdc);
320 return (0);
321
322 errout:
323 /*
324 * Something bad has happened and we ran out of data
325 * before we could parse all f_ecnt entries expected.
326 * Give up on the current buffer.
327 */
328 SMBVDEBUG("ran out of data\n");
329 ctx->f_eofs = ctx->f_left;
330 md_done(&entry_mdc);
331 return (error);
332 }
333
334 /*
335 * Decode FileAllInformation
336 *
337 * The data is a concatenation of:
338 * FileBasicInformation
339 * FileStandardInformation
340 * FileInternalInformation
341 * FileEaInformation
342 * FileAccessInformation
343 * FilePositionInformation
344 * FileModeInformation
345 * FileAlignmentInformation
346 * FileNameInformation
347 */
348 /*ARGSUSED*/
349 int
smbfs_decode_file_all_info(struct smb_share * ssp,struct mdchain * mdp,struct smbfattr * fap)350 smbfs_decode_file_all_info(struct smb_share *ssp,
351 struct mdchain *mdp, struct smbfattr *fap)
352 {
353 uint64_t llongint, lsize;
354 uint32_t dattr;
355 int error;
356
357 /*
358 * This part is: FileBasicInformation
359 */
360
361 /* creation time */
362 md_get_uint64le(mdp, &llongint);
363 smb_time_NT2local(llongint, &fap->fa_createtime);
364
365 /* last access time */
366 md_get_uint64le(mdp, &llongint);
367 smb_time_NT2local(llongint, &fap->fa_atime);
368
369 /* last write time */
370 md_get_uint64le(mdp, &llongint);
371 smb_time_NT2local(llongint, &fap->fa_mtime);
372
373 /* last change time */
374 md_get_uint64le(mdp, &llongint);
375 smb_time_NT2local(llongint, &fap->fa_ctime);
376
377 /* attributes */
378 md_get_uint32le(mdp, &dattr);
379 fap->fa_attr = dattr;
380
381 /* reserved */
382 md_get_uint32le(mdp, NULL);
383
384 /*
385 * This part is: FileStandardInformation
386 */
387
388 /* allocation size */
389 md_get_uint64le(mdp, &lsize);
390 fap->fa_allocsz = lsize;
391
392 /* File size */
393 error = md_get_uint64le(mdp, &lsize);
394 fap->fa_size = lsize;
395
396 /*
397 * There's more after this but we don't need it:
398 * Remainder of FileStandardInformation
399 * NumLlinks, DeletOnClose, IsDir, reserved.
400 * Then:
401 * FileInternalInformation
402 * FileEaInformation
403 * FileAccessInformation
404 * FilePositionInformation
405 * FileModeInformation
406 * FileAlignmentInformation
407 * FileNameInformation
408 */
409
410 return (error);
411 }
412
413 /*
414 * Decode FileFsAttributeInformation
415 *
416 * ULONG FileSystemAttributes;
417 * LONG MaximumComponentNameLength;
418 * ULONG FileSystemNameLength;
419 * WCHAR FileSystemName[1];
420 */
421 int
smbfs_decode_fs_attr_info(struct smb_share * ssp,struct mdchain * mdp,struct smb_fs_attr_info * fsa)422 smbfs_decode_fs_attr_info(struct smb_share *ssp,
423 struct mdchain *mdp, struct smb_fs_attr_info *fsa)
424 {
425 struct smb_vc *vcp = SSTOVC(ssp);
426 uint32_t nlen;
427 int error;
428
429 md_get_uint32le(mdp, &fsa->fsa_aflags);
430 md_get_uint32le(mdp, &fsa->fsa_maxname);
431 error = md_get_uint32le(mdp, &nlen); /* fs name length */
432 if (error)
433 goto out;
434
435 /*
436 * Get the FS type name.
437 */
438 bzero(fsa->fsa_tname, FSTYPSZ);
439 if (SMB_UNICODE_STRINGS(vcp)) {
440 uint16_t tmpbuf[FSTYPSZ];
441 size_t tmplen, outlen;
442
443 if (nlen > sizeof (tmpbuf))
444 nlen = sizeof (tmpbuf);
445 error = md_get_mem(mdp, tmpbuf, nlen, MB_MSYSTEM);
446 if (error != 0)
447 goto out;
448 tmplen = nlen / 2; /* UCS-2 chars */
449 outlen = FSTYPSZ - 1;
450 error = uconv_u16tou8(tmpbuf, &tmplen,
451 (uchar_t *)fsa->fsa_tname, &outlen,
452 UCONV_IN_LITTLE_ENDIAN);
453 } else {
454 if (nlen > (FSTYPSZ - 1))
455 nlen = FSTYPSZ - 1;
456 error = md_get_mem(mdp, fsa->fsa_tname, nlen, MB_MSYSTEM);
457 }
458
459 out:
460 return (error);
461 }
462