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