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