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