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