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