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