Home | History | Annotate | Download | only in smbfs
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     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/cred.h>
     34 #include <sys/vnode.h>
     35 #include <sys/vfs.h>
     36 #include <sys/filio.h>
     37 #include <sys/uio.h>
     38 #include <sys/dirent.h>
     39 #include <sys/errno.h>
     40 #include <sys/sysmacros.h>
     41 #include <sys/kmem.h>
     42 #include <sys/stat.h>
     43 #include <sys/cmn_err.h>
     44 #include <sys/u8_textprep.h>
     45 
     46 #include <netsmb/smb_osdep.h>
     47 #include <netsmb/smb.h>
     48 #include <netsmb/smb_conn.h>
     49 #include <netsmb/smb_subr.h>
     50 #include <netsmb/smb_rq.h>
     51 
     52 #include <smbfs/smbfs.h>
     53 #include <smbfs/smbfs_node.h>
     54 #include <smbfs/smbfs_subr.h>
     55 
     56 #include <fs/fs_subr.h>
     57 
     58 /*
     59  * Solaris wants there to be a directory node to contain
     60  * all the extended attributes.  The SMB protocol does not
     61  * really support a directory here, and uses very different
     62  * operations to list attributes, etc. so we "fake up" an
     63  * smbnode here to represent the attributes directory.
     64  *
     65  * We need to give this (fake) directory a unique identity,
     66  * and since we're using the full remote pathname as the
     67  * unique identity of all nodes, the easiest thing to do
     68  * here is append a colon (:) to the given pathname.
     69  *
     70  * There are several places where smbfs_fullpath and its
     71  * callers must decide what separator to use when building
     72  * a remote path name, and the rule is now as follows:
     73  * 1: When no XATTR involved, use "\\" as the separator.
     74  * 2: Traversal into the (fake) XATTR dir adds one ":"
     75  * 3: Children of the XATTR dir add nothing (sep=0)
     76  * The result should be _one_ colon before the attr name.
     77  */
     78 
     79 /* ARGSUSED */
     80 int
     81 smbfs_get_xattrdir(vnode_t *pvp, vnode_t **vpp, cred_t *cr, int flags)
     82 {
     83 	vnode_t *xvp;
     84 	smbnode_t *pnp, *xnp;
     85 
     86 	pnp = VTOSMB(pvp);
     87 
     88 	/*
     89 	 * We don't allow recursive extended attributes
     90 	 * (xattr under xattr dir.) so the "parent" node
     91 	 * (pnp) must NOT be an XATTR directory or file.
     92 	 */
     93 	if (pnp->n_flag & N_XATTR)
     94 		return (EINVAL);
     95 
     96 	xnp = smbfs_node_findcreate(pnp->n_mount,
     97 	    pnp->n_rpath, pnp->n_rplen, NULL, 0, ':',
     98 	    &smbfs_fattr0); /* force create */
     99 	ASSERT(xnp != NULL);
    100 	xvp = SMBTOV(xnp);
    101 	/* Note: xvp has a VN_HOLD, which our caller expects. */
    102 
    103 	/* If it's a new node, initialize. */
    104 	if (xvp->v_type == VNON) {
    105 
    106 		mutex_enter(&xvp->v_lock);
    107 		xvp->v_type = VDIR;
    108 		xvp->v_flag |= V_XATTRDIR;
    109 		mutex_exit(&xvp->v_lock);
    110 
    111 		mutex_enter(&xnp->r_statelock);
    112 		xnp->n_flag |= N_XATTR;
    113 		mutex_exit(&xnp->r_statelock);
    114 	}
    115 
    116 	/* Success! */
    117 	*vpp = xvp;
    118 	return (0);
    119 }
    120 
    121 /*
    122  * Find the parent of an XATTR directory or file,
    123  * by trimming off the ":attrname" part of rpath.
    124  * Called on XATTR files to get the XATTR dir, and
    125  * called on the XATTR dir to get the real object
    126  * under which the (faked up) XATTR dir lives.
    127  */
    128 int
    129 smbfs_xa_parent(vnode_t *vp, vnode_t **vpp)
    130 {
    131 	smbnode_t *np = VTOSMB(vp);
    132 	smbnode_t *pnp;
    133 	int rplen;
    134 
    135 	*vpp = NULL;
    136 
    137 	if ((np->n_flag & N_XATTR) == 0)
    138 		return (EINVAL);
    139 
    140 	if (vp->v_flag & V_XATTRDIR) {
    141 		/*
    142 		 * Want the parent of the XATTR directory.
    143 		 * That's easy: just remove trailing ":"
    144 		 */
    145 		rplen = np->n_rplen - 1;
    146 		if (rplen < 1) {
    147 			SMBVDEBUG("rplen < 1?");
    148 			return (ENOENT);
    149 		}
    150 		if (np->n_rpath[rplen] != ':') {
    151 			SMBVDEBUG("last is not colon");
    152 			return (ENOENT);
    153 		}
    154 	} else {
    155 		/*
    156 		 * Want the XATTR directory given
    157 		 * one of its XATTR files (children).
    158 		 * Find the ":" and trim after it.
    159 		 */
    160 		for (rplen = 1; rplen < np->n_rplen; rplen++)
    161 			if (np->n_rpath[rplen] == ':')
    162 				break;
    163 		/* Should have found ":stream_name" */
    164 		if (rplen >= np->n_rplen) {
    165 			SMBVDEBUG("colon not found");
    166 			return (ENOENT);
    167 		}
    168 		rplen++; /* keep the ":" */
    169 		if (rplen >= np->n_rplen) {
    170 			SMBVDEBUG("no stream name");
    171 			return (ENOENT);
    172 		}
    173 	}
    174 
    175 	pnp = smbfs_node_findcreate(np->n_mount,
    176 	    np->n_rpath, rplen, NULL, 0, 0,
    177 	    &smbfs_fattr0); /* force create */
    178 	ASSERT(pnp != NULL);
    179 	/* Note: have VN_HOLD from smbfs_node_findcreate */
    180 	*vpp = SMBTOV(pnp);
    181 	return (0);
    182 }
    183 
    184 /*
    185  * This is called by smbfs_pathconf to find out
    186  * if some file has any extended attributes.
    187  * There's no short-cut way to find out, so we
    188  * just list the attributes the usual way and
    189  * check for an empty result.
    190  *
    191  * Returns 1: (exists) or 0: (none found)
    192  */
    193 int
    194 smbfs_xa_exists(vnode_t *vp, cred_t *cr)
    195 {
    196 	smbnode_t *xnp;
    197 	vnode_t *xvp;
    198 	struct smb_cred scred;
    199 	struct smbfs_fctx ctx;
    200 	int error, rc = 0;
    201 
    202 	/* Get the xattr dir */
    203 	error = smbfs_get_xattrdir(vp, &xvp, cr, LOOKUP_XATTR);
    204 	if (error)
    205 		return (0);
    206 	/* NB: have VN_HOLD on xpv */
    207 	xnp = VTOSMB(xvp);
    208 
    209 	smb_credinit(&scred, cr);
    210 
    211 	bzero(&ctx, sizeof (ctx));
    212 	ctx.f_flags = SMBFS_RDD_FINDFIRST;
    213 	ctx.f_dnp = xnp;
    214 	ctx.f_scred = &scred;
    215 	ctx.f_ssp = xnp->n_mount->smi_share;
    216 
    217 	error = smbfs_xa_findopen(&ctx, xnp, "*", 1);
    218 	if (error)
    219 		goto out;
    220 
    221 	error = smbfs_xa_findnext(&ctx, 1);
    222 	if (error)
    223 		goto out;
    224 
    225 	/* Have at least one named stream. */
    226 	SMBVDEBUG("ctx.f_name: %s\n", ctx.f_name);
    227 	rc = 1;
    228 
    229 out:
    230 	/* NB: Always call findclose, error or not. */
    231 	(void) smbfs_xa_findclose(&ctx);
    232 	smb_credrele(&scred);
    233 	VN_RELE(xvp);
    234 	return (rc);
    235 }
    236 
    237 
    238 /*
    239  * This is called to get attributes (size, etc.) of either
    240  * the "faked up" XATTR directory or a named stream.
    241  */
    242 int
    243 smbfs_xa_getfattr(struct smbnode *xnp, struct smbfattr *fap,
    244 	struct smb_cred *scrp)
    245 {
    246 	vnode_t *xvp;	/* xattr */
    247 	vnode_t *pvp;	/* parent */
    248 	smbnode_t *pnp;	/* parent */
    249 	int error, nlen;
    250 	const char *name, *sname;
    251 
    252 	xvp = SMBTOV(xnp);
    253 
    254 	/*
    255 	 * Simulate smbfs_smb_getfattr() for a named stream.
    256 	 * OK to leave a,c,m times zero (expected w/ XATTR).
    257 	 * The XATTR directory is easy (all fake).
    258 	 */
    259 	if (xvp->v_flag & V_XATTRDIR) {
    260 		fap->fa_attr = SMB_FA_DIR;
    261 		fap->fa_size = DEV_BSIZE;
    262 		return (0);
    263 	}
    264 
    265 	/*
    266 	 * Do a lookup in the XATTR directory,
    267 	 * using the stream name (last part)
    268 	 * from the xattr node.
    269 	 */
    270 	error = smbfs_xa_parent(xvp, &pvp);
    271 	if (error)
    272 		return (error);
    273 	/* Note: pvp has a VN_HOLD */
    274 	pnp = VTOSMB(pvp);
    275 
    276 	/* Get stream name (ptr and length) */
    277 	ASSERT(xnp->n_rplen > pnp->n_rplen);
    278 	nlen = xnp->n_rplen - pnp->n_rplen;
    279 	name = xnp->n_rpath + pnp->n_rplen;
    280 	sname = name;
    281 
    282 	/* Note: this can allocate a new "name" */
    283 	error = smbfs_smb_lookup(pnp, &name, &nlen, fap, scrp);
    284 	if (error == 0 && name != sname)
    285 		smbfs_name_free(name, nlen);
    286 
    287 	VN_RELE(pvp);
    288 
    289 	return (error);
    290 }
    291 
    292 /*
    293  * Fetch the entire attribute list here in findopen.
    294  * Will parse the results in findnext.
    295  *
    296  * This is called on the XATTR directory, so we
    297  * have to get the (real) parent object first.
    298  */
    299 /* ARGSUSED */
    300 int
    301 smbfs_xa_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp,
    302 	const char *wildcard, int wclen)
    303 {
    304 	vnode_t *pvp;	/* parent */
    305 	smbnode_t *pnp;
    306 	struct smb_t2rq *t2p;
    307 	struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
    308 	struct mbchain *mbp;
    309 	int error;
    310 
    311 	ASSERT(dnp->n_flag & N_XATTR);
    312 
    313 	ctx->f_type = ft_XA;
    314 	ctx->f_namesz = SMB_MAXFNAMELEN + 1;
    315 	if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp)))
    316 		ctx->f_namesz *= 2;
    317 	ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP);
    318 
    319 	error = smbfs_xa_parent(SMBTOV(dnp), &pvp);
    320 	if (error)
    321 		return (error);
    322 	ASSERT(pvp);
    323 	/* Note: pvp has a VN_HOLD */
    324 	pnp = VTOSMB(pvp);
    325 
    326 	if (ctx->f_t2) {
    327 		smb_t2_done(ctx->f_t2);
    328 		ctx->f_t2 = NULL;
    329 	}
    330 
    331 	error = smb_t2_alloc(SSTOCP(ctx->f_ssp),
    332 	    SMB_TRANS2_QUERY_PATH_INFORMATION,
    333 	    ctx->f_scred, &t2p);
    334 	if (error)
    335 		goto out;
    336 	ctx->f_t2 = t2p;
    337 
    338 	mbp = &t2p->t2_tparam;
    339 	(void) mb_init(mbp);
    340 	(void) mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFO);
    341 	(void) mb_put_uint32le(mbp, 0);
    342 	error = smbfs_fullpath(mbp, vcp, pnp, NULL, NULL, 0);
    343 	if (error)
    344 		goto out;
    345 	t2p->t2_maxpcount = 2;
    346 	t2p->t2_maxdcount = INT16_MAX;
    347 	error = smb_t2_request(t2p);
    348 	if (error) {
    349 		if (smb_t2_err(t2p) == NT_STATUS_INVALID_PARAMETER)
    350 			error = ENOTSUP;
    351 	}
    352 	/*
    353 	 * No returned parameters to parse.
    354 	 * Returned data are in t2_rdata,
    355 	 * which we'll parse in _findnext.
    356 	 * However, save the wildcard.
    357 	 */
    358 	ctx->f_wildcard = wildcard;
    359 	ctx->f_wclen = wclen;
    360 
    361 out:
    362 	VN_RELE(pvp);
    363 	return (error);
    364 }
    365 
    366 /*
    367  * Get the next name in an XATTR directory into f_name
    368  */
    369 /* ARGSUSED */
    370 int
    371 smbfs_xa_findnext(struct smbfs_fctx *ctx, uint16_t limit)
    372 {
    373 	struct mdchain *mdp;
    374 	struct smb_t2rq *t2p;
    375 	uint32_t size, next;
    376 	uint64_t llongint;
    377 	int error, skip, used, nmlen;
    378 
    379 	t2p = ctx->f_t2;
    380 	mdp = &t2p->t2_rdata;
    381 
    382 	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
    383 		ASSERT(ctx->f_wildcard);
    384 		SMBVDEBUG("wildcard: %s\n", ctx->f_wildcard);
    385 	}
    386 
    387 again:
    388 	if (ctx->f_flags & SMBFS_RDD_EOF)
    389 		return (ENOENT);
    390 
    391 	/* Parse FILE_STREAM_INFORMATION */
    392 	if ((error = md_get_uint32le(mdp, &next)) != 0)	/* offset to */
    393 		return (ENOENT);
    394 	if ((error = md_get_uint32le(mdp, &size)) != 0) /* name len */
    395 		return (ENOENT);
    396 	(void) md_get_uint64le(mdp, &llongint); /* file size */
    397 	ctx->f_attr.fa_size = llongint;
    398 	(void) md_get_uint64le(mdp, NULL);	/* alloc. size */
    399 	used = 4 + 4 + 8 + 8;	/* how much we consumed */
    400 
    401 	/*
    402 	 * Copy the string, but skip the first char (":")
    403 	 * Watch out for zero-length strings here.
    404 	 */
    405 	if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) {
    406 		if (size >= 2) {
    407 			size -= 2; used += 2;
    408 			(void) md_get_uint16le(mdp, NULL);
    409 		}
    410 		nmlen = min(size, SMB_MAXFNAMELEN * 2);
    411 	} else {
    412 		if (size >= 1) {
    413 			size -= 1; used += 1;
    414 			(void) md_get_uint8(mdp, NULL);
    415 		}
    416 		nmlen = min(size, SMB_MAXFNAMELEN);
    417 	}
    418 
    419 	ASSERT(nmlen < ctx->f_namesz);
    420 	ctx->f_nmlen = nmlen;
    421 	error = md_get_mem(mdp, ctx->f_name, nmlen, MB_MSYSTEM);
    422 	if (error)
    423 		return (error);
    424 	used += nmlen;
    425 
    426 	/*
    427 	 * Convert UCS-2 to UTF-8
    428 	 */
    429 	smbfs_fname_tolocal(ctx);
    430 	if (nmlen)
    431 		SMBVDEBUG("name: %s\n", ctx->f_name);
    432 	else
    433 		SMBVDEBUG("null name!\n");
    434 
    435 	/*
    436 	 * Skip padding until next offset
    437 	 */
    438 	if (next > used) {
    439 		skip = next - used;
    440 		(void) md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
    441 	}
    442 	if (next == 0)
    443 		ctx->f_flags |= SMBFS_RDD_EOF;
    444 
    445 	/*
    446 	 * Chop off the trailing ":$DATA"
    447 	 * The 6 here is strlen(":$DATA")
    448 	 */
    449 	if (ctx->f_nmlen >= 6) {
    450 		char *p = ctx->f_name + ctx->f_nmlen - 6;
    451 		if (strncmp(p, ":$DATA", 6) == 0) {
    452 			*p = '\0'; /* Chop! */
    453 			ctx->f_nmlen -= 6;
    454 		}
    455 	}
    456 
    457 	/*
    458 	 * The Chop above will typically leave
    459 	 * an empty name in the first slot,
    460 	 * which we will skip here.
    461 	 */
    462 	if (ctx->f_nmlen == 0)
    463 		goto again;
    464 
    465 	/*
    466 	 * If this is a lookup of a specific name,
    467 	 * skip past any non-matching names.
    468 	 */
    469 	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
    470 		if (ctx->f_wclen != ctx->f_nmlen)
    471 			goto again;
    472 		if (u8_strcmp(ctx->f_wildcard, ctx->f_name,
    473 		    ctx->f_nmlen, U8_STRCMP_CI_LOWER,
    474 		    U8_UNICODE_LATEST, &error) || error)
    475 			goto again;
    476 	}
    477 
    478 	return (0);
    479 }
    480 
    481 /*
    482  * Find first/next/close for XATTR directories.
    483  * NB: also used by smbfs_smb_lookup
    484  */
    485 
    486 int
    487 smbfs_xa_findclose(struct smbfs_fctx *ctx)
    488 {
    489 
    490 	if (ctx->f_name)
    491 		kmem_free(ctx->f_name, ctx->f_namesz);
    492 	if (ctx->f_t2)
    493 		smb_t2_done(ctx->f_t2);
    494 
    495 	return (0);
    496 }
    497