1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
25 */
26
27/*
28 * Functions supporting Solaris Extended Attributes,
29 * used to provide access to CIFS "named streams".
30 */
31
32#include <sys/systm.h>
33#include <sys/inttypes.h>
34#include <sys/cred.h>
35#include <sys/vnode.h>
36#include <sys/vfs.h>
37#include <sys/filio.h>
38#include <sys/uio.h>
39#include <sys/dirent.h>
40#include <sys/errno.h>
41#include <sys/sysmacros.h>
42#include <sys/kmem.h>
43#include <sys/stat.h>
44#include <sys/cmn_err.h>
45#include <sys/u8_textprep.h>
46
47#include <netsmb/smb_osdep.h>
48
49#include <netsmb/smb.h>
50#include <netsmb/smb2.h>
51#include <netsmb/smb_conn.h>
52#include <netsmb/smb_subr.h>
53#include <netsmb/smb_rq.h>
54
55#include <smbfs/smbfs.h>
56#include <smbfs/smbfs_node.h>
57#include <smbfs/smbfs_subr.h>
58
59#include <fs/fs_subr.h>
60
61/*
62 * Solaris wants there to be a directory node to contain
63 * all the extended attributes.  The SMB protocol does not
64 * really support a directory here, and uses very different
65 * operations to list attributes, etc. so we "fake up" an
66 * smbnode here to represent the attributes directory.
67 *
68 * We need to give this (fake) directory a unique identity,
69 * and since we're using the full remote pathname as the
70 * unique identity of all nodes, the easiest thing to do
71 * here is append a colon (:) to the given pathname.
72 *
73 * There are several places where smbfs_fullpath and its
74 * callers must decide what separator to use when building
75 * a remote path name, and the rule is now as follows:
76 * 1: When no XATTR involved, use "\\" as the separator.
77 * 2: Traversal into the (fake) XATTR dir adds one ":"
78 * 3: Children of the XATTR dir add nothing (sep=0)
79 * The result should be _one_ colon before the attr name.
80 */
81
82/* ARGSUSED */
83int
84smbfs_get_xattrdir(vnode_t *pvp, vnode_t **vpp, cred_t *cr, int flags)
85{
86	vnode_t *xvp;
87	smbnode_t *pnp, *xnp;
88
89	pnp = VTOSMB(pvp);
90
91	/*
92	 * We don't allow recursive extended attributes
93	 * (xattr under xattr dir.) so the "parent" node
94	 * (pnp) must NOT be an XATTR directory or file.
95	 */
96	if (pnp->n_flag & N_XATTR)
97		return (EINVAL);
98
99	xnp = smbfs_node_findcreate(pnp->n_mount,
100	    pnp->n_rpath, pnp->n_rplen, NULL, 0, ':',
101	    &smbfs_fattr0); /* force create */
102	ASSERT(xnp != NULL);
103	xvp = SMBTOV(xnp);
104	/* Note: xvp has a VN_HOLD, which our caller expects. */
105
106	/* If it's a new node, initialize. */
107	if (xvp->v_type == VNON) {
108
109		mutex_enter(&xvp->v_lock);
110		xvp->v_type = VDIR;
111		xvp->v_flag |= V_XATTRDIR;
112		mutex_exit(&xvp->v_lock);
113
114		mutex_enter(&xnp->r_statelock);
115		xnp->n_flag |= N_XATTR;
116		mutex_exit(&xnp->r_statelock);
117	}
118
119	/* Success! */
120	*vpp = xvp;
121	return (0);
122}
123
124/*
125 * Find the parent of an XATTR directory or file,
126 * by trimming off the ":attrname" part of rpath.
127 * Called on XATTR files to get the XATTR dir, and
128 * called on the XATTR dir to get the real object
129 * under which the (faked up) XATTR dir lives.
130 */
131int
132smbfs_xa_parent(vnode_t *vp, vnode_t **vpp)
133{
134	smbnode_t *np = VTOSMB(vp);
135	smbnode_t *pnp;
136	int rplen;
137
138	*vpp = NULL;
139
140	if ((np->n_flag & N_XATTR) == 0)
141		return (EINVAL);
142
143	if (vp->v_flag & V_XATTRDIR) {
144		/*
145		 * Want the parent of the XATTR directory.
146		 * That's easy: just remove trailing ":"
147		 */
148		rplen = np->n_rplen - 1;
149		if (rplen < 1) {
150			SMBVDEBUG("rplen < 1?");
151			return (ENOENT);
152		}
153		if (np->n_rpath[rplen] != ':') {
154			SMBVDEBUG("last is not colon");
155			return (ENOENT);
156		}
157	} else {
158		/*
159		 * Want the XATTR directory given
160		 * one of its XATTR files (children).
161		 * Find the ":" and trim after it.
162		 */
163		for (rplen = 1; rplen < np->n_rplen; rplen++)
164			if (np->n_rpath[rplen] == ':')
165				break;
166		/* Should have found ":stream_name" */
167		if (rplen >= np->n_rplen) {
168			SMBVDEBUG("colon not found");
169			return (ENOENT);
170		}
171		rplen++; /* keep the ":" */
172		if (rplen >= np->n_rplen) {
173			SMBVDEBUG("no stream name");
174			return (ENOENT);
175		}
176	}
177
178	pnp = smbfs_node_findcreate(np->n_mount,
179	    np->n_rpath, rplen, NULL, 0, 0,
180	    &smbfs_fattr0); /* force create */
181	ASSERT(pnp != NULL);
182	/* Note: have VN_HOLD from smbfs_node_findcreate */
183	*vpp = SMBTOV(pnp);
184	return (0);
185}
186
187/*
188 * This is called by smbfs_pathconf to find out
189 * if some file has any extended attributes.
190 * There's no short-cut way to find out, so we
191 * just list the attributes the usual way and
192 * check for an empty result.
193 *
194 * Returns 1: (exists) or 0: (none found)
195 */
196int
197smbfs_xa_exists(vnode_t *vp, cred_t *cr)
198{
199	smbnode_t *xnp;
200	vnode_t *xvp;
201	struct smb_cred scred;
202	struct smbfs_fctx ctx;
203	int error, rc = 0;
204
205	/* Get the xattr dir */
206	error = smbfs_get_xattrdir(vp, &xvp, cr, LOOKUP_XATTR);
207	if (error)
208		return (0);
209	/* NB: have VN_HOLD on xpv */
210	xnp = VTOSMB(xvp);
211
212	smb_credinit(&scred, cr);
213
214	bzero(&ctx, sizeof (ctx));
215	ctx.f_flags = SMBFS_RDD_FINDFIRST;
216	ctx.f_dnp = xnp;
217	ctx.f_scred = &scred;
218	ctx.f_ssp = xnp->n_mount->smi_share;
219
220	error = smbfs_xa_findopen(&ctx, xnp, "*", 1);
221	if (error)
222		goto out;
223
224	error = smbfs_xa_findnext(&ctx, 1);
225	if (error)
226		goto out;
227
228	/* Have at least one named stream. */
229	SMBVDEBUG("ctx.f_name: %s\n", ctx.f_name);
230	rc = 1;
231
232out:
233	/* NB: Always call findclose, error or not. */
234	(void) smbfs_xa_findclose(&ctx);
235	smb_credrele(&scred);
236	VN_RELE(xvp);
237	return (rc);
238}
239
240
241/*
242 * This is called to get attributes (size, etc.) of either
243 * the "faked up" XATTR directory or a named stream.
244 */
245int
246smbfs_xa_getfattr(struct smbnode *xnp, struct smbfattr *fap,
247	struct smb_cred *scrp)
248{
249	vnode_t *xvp;	/* xattr */
250	vnode_t *pvp;	/* parent */
251	smbnode_t *pnp;	/* parent */
252	int error, nlen;
253	const char *name, *sname;
254
255	xvp = SMBTOV(xnp);
256
257	/*
258	 * Simulate smbfs_smb_getfattr() for a named stream.
259	 * OK to leave a,c,m times zero (expected w/ XATTR).
260	 * The XATTR directory is easy (all fake).
261	 */
262	if (xvp->v_flag & V_XATTRDIR) {
263		fap->fa_attr = SMB_FA_DIR;
264		fap->fa_size = DEV_BSIZE;
265		return (0);
266	}
267
268	/*
269	 * Do a lookup in the XATTR directory,
270	 * using the stream name (last part)
271	 * from the xattr node.
272	 */
273	error = smbfs_xa_parent(xvp, &pvp);
274	if (error)
275		return (error);
276	/* Note: pvp has a VN_HOLD */
277	pnp = VTOSMB(pvp);
278
279	/* Get stream name (ptr and length) */
280	ASSERT(xnp->n_rplen > pnp->n_rplen);
281	nlen = xnp->n_rplen - pnp->n_rplen;
282	name = xnp->n_rpath + pnp->n_rplen;
283	sname = name;
284
285	/* Note: this can allocate a new "name" */
286	error = smbfs_smb_lookup(pnp, &name, &nlen, fap, scrp);
287	if (error == 0 && name != sname)
288		smbfs_name_free(name, nlen);
289
290	VN_RELE(pvp);
291
292	return (error);
293}
294
295/*
296 * Actually go OtW to get the list of "streams".
297 *
298 * This is called on the XATTR directory, so we
299 * have to get the (real) parent object first.
300 */
301static int
302smbfs_xa_get_streaminfo(struct smbfs_fctx *ctx)
303{
304	vnode_t *pvp;	/* parent */
305	smbnode_t *pnp;
306	smbnode_t *dnp = ctx->f_dnp;
307	struct mdchain *mdp;
308	int error;
309
310	error = smbfs_xa_parent(SMBTOV(dnp), &pvp);
311	if (error)
312		return (error);
313	ASSERT(pvp);
314	/* Note: pvp has a VN_HOLD */
315	pnp = VTOSMB(pvp);
316
317	/*
318	 * Get stream info into f_mdchain
319	 */
320	mdp = &ctx->f_mdchain;
321	md_done(mdp);
322
323	if (SSTOVC(ctx->f_ssp)->vc_flags & SMBV_SMB2) {
324		error = smbfs_smb2_get_streaminfo(pnp, mdp, ctx->f_scred);
325	} else {
326		error = smbfs_smb1_get_streaminfo(pnp, mdp, ctx->f_scred);
327	}
328	if (error)
329		goto out;
330
331	/*
332	 * Have stream info in ctx->f_mdchain
333	 * Initialize buffer length, position.
334	 */
335	ctx->f_left = m_fixhdr(mdp->md_top);
336	ctx->f_eofs = 0;
337
338	/*
339	 * After one successful call, we're at EOF.
340	 */
341	ctx->f_flags |= SMBFS_RDD_EOF;
342
343out:
344	VN_RELE(pvp);
345	return (error);
346}
347
348/*
349 * Get a buffer of directory entries (if we don't already have
350 * some remaining in the current buffer) then decode one.
351 */
352int
353smbfs_xa_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp,
354	const char *wildcard, int wclen)
355{
356
357	ASSERT(dnp->n_flag & N_XATTR);
358
359	ctx->f_type = ft_XA;
360	ctx->f_namesz = SMB_MAXFNAMELEN + 1;
361	ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP);
362	ctx->f_infolevel = FileStreamInformation;
363	ctx->f_wildcard = wildcard;
364	ctx->f_wclen = wclen;
365
366	return (0);
367}
368
369
370/*
371 * Get the next name in an XATTR directory
372 */
373/* ARGSUSED */
374int
375smbfs_xa_findnext(struct smbfs_fctx *ctx, uint16_t limit)
376{
377	int error;
378
379	/*
380	 * If we've scanned to the end of the current buffer
381	 * try to read anohther buffer of dir entries.
382	 * Treat anything less than 8 bytes as an "empty"
383	 * buffer to ensure we can read something.
384	 * (There may be up to 8 bytes of padding.)
385	 */
386again:
387	if ((ctx->f_eofs + 8) > ctx->f_left) {
388		/* Scanned the whole buffer. */
389		if (ctx->f_flags & SMBFS_RDD_EOF)
390			return (ENOENT);
391		ctx->f_limit = limit;
392		error = smbfs_xa_get_streaminfo(ctx);
393		if (error)
394			return (error);
395		ctx->f_otws++;
396	}
397
398	/*
399	 * Decode one entry, advance f_eofs
400	 */
401	error = smbfs_decode_dirent(ctx);
402	if (error)
403		return (error);
404	SMBVDEBUG("name: %s\n", ctx->f_name);
405
406	/*
407	 * Chop off the trailing ":$DATA"
408	 * The 6 here is strlen(":$DATA")
409	 */
410	if (ctx->f_nmlen >= 6) {
411		char *p = ctx->f_name + ctx->f_nmlen - 6;
412		if (strncmp(p, ":$DATA", 6) == 0) {
413			*p = '\0'; /* Chop! */
414			ctx->f_nmlen -= 6;
415		}
416	}
417
418	/*
419	 * The Chop above will typically leave
420	 * an empty name in the first slot,
421	 * which we will skip here.
422	 */
423	if (ctx->f_nmlen == 0)
424		goto again;
425
426	/*
427	 * When called by lookup, we'll have the "single" flag,
428	 * and a name with no wildcards.  We need to filter here
429	 * because smbfs_xa_get_streaminfo() gets ALL the names
430	 * (not just those matching our pattern).
431	 */
432	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
433		if (ctx->f_wclen != ctx->f_nmlen)
434			goto again;
435		if (u8_strcmp(ctx->f_wildcard, ctx->f_name,
436		    ctx->f_nmlen, U8_STRCMP_CI_LOWER,
437		    U8_UNICODE_LATEST, &error) || error)
438			goto again;
439	}
440
441	return (0);
442}
443
444/*
445 * Find first/next/close for XATTR directories.
446 * NB: also used by smbfs_smb_lookup
447 */
448
449int
450smbfs_xa_findclose(struct smbfs_fctx *ctx)
451{
452
453	if (ctx->f_name)
454		kmem_free(ctx->f_name, ctx->f_namesz);
455
456	return (0);
457}
458