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 */
70int
71smbfs_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 */
143void
144smbfs_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
184errout:
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 */
209int
210smbfs_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
322errout:
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 *	FilePositionInformation
343 *	FileModeInformation
344 *	FileAlignmentInformation
345 *	FileNameInformation
346 */
347/*ARGSUSED*/
348int
349smbfs_decode_file_all_info(struct smb_share *ssp,
350	struct mdchain *mdp, struct smbfattr *fap)
351{
352	uint64_t llongint, lsize;
353	uint32_t dattr;
354	int error;
355
356	/*
357	 * This part is: FileBasicInformation
358	 */
359
360	/* creation time */
361	md_get_uint64le(mdp, &llongint);
362	smb_time_NT2local(llongint, &fap->fa_createtime);
363
364	/* last access time */
365	md_get_uint64le(mdp, &llongint);
366	smb_time_NT2local(llongint, &fap->fa_atime);
367
368	/* last write time */
369	md_get_uint64le(mdp, &llongint);
370	smb_time_NT2local(llongint, &fap->fa_mtime);
371
372	/* last change time */
373	md_get_uint64le(mdp, &llongint);
374	smb_time_NT2local(llongint, &fap->fa_ctime);
375
376	/* attributes */
377	md_get_uint32le(mdp, &dattr);
378	fap->fa_attr = dattr;
379
380	/* reserved */
381	md_get_uint32le(mdp, NULL);
382
383	/*
384	 * This part is: FileStandardInformation
385	 */
386
387	/* allocation size */
388	md_get_uint64le(mdp, &lsize);
389	fap->fa_allocsz = lsize;
390
391	/* File size */
392	error = md_get_uint64le(mdp, &lsize);
393	fap->fa_size = lsize;
394
395	/*
396	 * There's more after this but we don't need it:
397	 * Remainder of FileStandardInformation
398	 *	NumLlinks, DeletOnClose, IsDir, reserved.
399	 * Then:
400	 *	FileInternalInformation
401	 *	FileEaInformation
402	 *	FilePositionInformation
403	 *	FileModeInformation
404	 *	FileAlignmentInformation
405	 *	FileNameInformation
406	 */
407
408	return (error);
409}
410
411/*
412 * Decode FileFsAttributeInformation
413 *
414 *    ULONG FileSystemAttributes;
415 *    LONG MaximumComponentNameLength;
416 *    ULONG FileSystemNameLength;
417 *    WCHAR FileSystemName[1];
418 */
419int
420smbfs_decode_fs_attr_info(struct smb_share *ssp,
421	struct mdchain *mdp, struct smb_fs_attr_info *fsa)
422{
423	struct smb_vc *vcp = SSTOVC(ssp);
424	uint32_t nlen;
425	int error;
426
427	md_get_uint32le(mdp, &fsa->fsa_aflags);
428	md_get_uint32le(mdp, &fsa->fsa_maxname);
429	error = md_get_uint32le(mdp, &nlen);	/* fs name length */
430	if (error)
431		goto out;
432
433	/*
434	 * Get the FS type name.
435	 */
436	bzero(fsa->fsa_tname, FSTYPSZ);
437	if (SMB_UNICODE_STRINGS(vcp)) {
438		uint16_t tmpbuf[FSTYPSZ];
439		size_t tmplen, outlen;
440
441		if (nlen > sizeof (tmpbuf))
442			nlen = sizeof (tmpbuf);
443		error = md_get_mem(mdp, tmpbuf, nlen, MB_MSYSTEM);
444		if (error != 0)
445			goto out;
446		tmplen = nlen / 2;	/* UCS-2 chars */
447		outlen = FSTYPSZ - 1;
448		error = uconv_u16tou8(tmpbuf, &tmplen,
449		    (uchar_t *)fsa->fsa_tname, &outlen,
450		    UCONV_IN_LITTLE_ENDIAN);
451	} else {
452		if (nlen > (FSTYPSZ - 1))
453			nlen = FSTYPSZ - 1;
454		error = md_get_mem(mdp, fsa->fsa_tname, nlen, MB_MSYSTEM);
455	}
456
457out:
458	return (error);
459}
460