xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c (revision adee678425979226b2b55d1a0b39ce4c989382e9)
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 */
83 int
smbfs_get_xattrdir(vnode_t * pvp,vnode_t ** vpp,cred_t * cr,int flags)84 smbfs_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  */
131 int
smbfs_xa_parent(vnode_t * vp,vnode_t ** vpp)132 smbfs_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  */
196 int
smbfs_xa_exists(vnode_t * vp,cred_t * cr)197 smbfs_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 
232 out:
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  */
245 int
smbfs_xa_getfattr(struct smbnode * xnp,struct smbfattr * fap,struct smb_cred * scrp)246 smbfs_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  */
301 static int
smbfs_xa_get_streaminfo(struct smbfs_fctx * ctx)302 smbfs_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 
343 out:
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  */
352 int
smbfs_xa_findopen(struct smbfs_fctx * ctx,struct smbnode * dnp,const char * wildcard,int wclen)353 smbfs_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 */
374 int
smbfs_xa_findnext(struct smbfs_fctx * ctx,uint16_t limit)375 smbfs_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 	 */
386 again:
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 
449 int
smbfs_xa_findclose(struct smbfs_fctx * ctx)450 smbfs_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