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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/types.h> 29 #include <sys/stat.h> 30 #include <sys/uio.h> 31 #include <sys/statvfs.h> 32 #include <sys/vnode.h> 33 #include <sys/thread.h> 34 #include <sys/pathname.h> 35 #include <sys/cred.h> 36 #include <sys/extdirent.h> 37 38 #include <smbsrv/smb_vops.h> 39 #include <smbsrv/string.h> 40 41 #include <smbsrv/smbtrans.h> 42 #include <smbsrv/smb_fsops.h> 43 #include <smbsrv/smb_kproto.h> 44 #include <smbsrv/smb_incl.h> 45 46 47 static int 48 smb_vop_readdir_readpage(vnode_t *vp, void *buf, uint32_t offset, int *count, 49 cred_t *cr, caller_context_t *ct, int flags); 50 51 static int 52 smb_vop_readdir_entry(vnode_t *dvp, uint32_t *cookiep, char *name, int *namelen, 53 ino64_t *inop, vnode_t **vpp, char *od_name, int flags, cred_t *cr, 54 caller_context_t *ct, char *dirbuf, int num_bytes); 55 56 static int 57 smb_vop_getdents_entries(smb_node_t *dir_snode, uint32_t *cookiep, 58 int32_t *dircountp, char *arg, uint32_t flags, struct smb_request *sr, 59 cred_t *cr, caller_context_t *ct, char *dirbuf, int *maxentries, 60 int num_bytes, char *); 61 62 extern int 63 smb_gather_dents_info(char *args, ino_t fileid, int namelen, 64 char *name, uint32_t cookie, int32_t *countp, 65 smb_attr_t *attr, struct smb_node *snode, 66 char *shortname, char *name83); 67 68 static void 69 smb_sa_to_va_mask(uint_t sa_mask, uint_t *va_maskp); 70 71 #define SMB_AT_MAX 16 72 static uint_t smb_attrmap[SMB_AT_MAX] = { 73 0, 74 AT_TYPE, 75 AT_MODE, 76 AT_UID, 77 AT_GID, 78 AT_FSID, 79 AT_NODEID, 80 AT_NLINK, 81 AT_SIZE, 82 AT_ATIME, 83 AT_MTIME, 84 AT_CTIME, 85 AT_RDEV, 86 AT_BLKSIZE, 87 AT_NBLOCKS, 88 AT_SEQ 89 }; 90 91 int 92 smb_vop_open(vnode_t **vpp, int mode, cred_t *cred, caller_context_t *ct) 93 { 94 return (VOP_OPEN(vpp, mode, cred, ct)); 95 } 96 97 int 98 smb_vop_close(vnode_t *vp, int mode, cred_t *cred, caller_context_t *ct) 99 { 100 return (VOP_CLOSE(vp, mode, 1, (offset_t)0, cred, ct)); 101 } 102 103 /* 104 * The smb_vop_* functions have minimal knowledge of CIFS semantics and 105 * serve as an interface to the VFS layer. 106 * 107 * Only smb_fsop_* layer functions should call smb_vop_* layer functions. 108 * (Higher-level CIFS service code should never skip the smb_fsop_* layer 109 * to call smb_vop_* layer functions directly.) 110 */ 111 112 /* 113 * XXX - Extended attributes support in the file system assumed. 114 * This is needed for full NT Streams functionality. 115 */ 116 117 int 118 smb_vop_read(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct) 119 { 120 int error; 121 122 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 123 error = VOP_READ(vp, uiop, 0, cr, ct); 124 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 125 return (error); 126 } 127 128 int 129 smb_vop_write(vnode_t *vp, uio_t *uiop, uint32_t *flag, uint32_t *lcount, 130 cred_t *cr, caller_context_t *ct) 131 { 132 int error; 133 int ioflag = 0; 134 135 *lcount = uiop->uio_resid; 136 137 if (*flag == FSSTAB_FILE_SYNC) 138 ioflag = FSYNC; 139 140 uiop->uio_llimit = MAXOFFSET_T; 141 142 (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); 143 error = VOP_WRITE(vp, uiop, ioflag, cr, ct); 144 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); 145 146 *lcount -= uiop->uio_resid; 147 148 return (error); 149 } 150 151 /* 152 * smb_vop_getattr() 153 * 154 * smb_fsop_getattr()/smb_vop_getattr() should always be called from the CIFS 155 * service (instead of calling VOP_GETATTR directly) to retrieve attributes 156 * due to special processing needed for streams files. 157 * 158 * All attributes are retrieved. 159 * 160 * A named stream's attributes (as far as CIFS is concerned) are those of the 161 * unnamed (i.e. data) stream (minus the size attribute), and the size of the 162 * named stream. Though the file system may store attributes other than size 163 * with the named stream, these should not be used by CIFS for any purpose. 164 * 165 * When vp denotes a named stream, then unnamed_vp should be passed in (denoting 166 * the corresponding unnamed stream). 167 */ 168 169 int 170 smb_vop_getattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *ret_attr, 171 int flags, cred_t *cr, caller_context_t *ct) 172 { 173 int error; 174 vnode_t *use_vp; 175 smb_attr_t tmp_attr; 176 xvattr_t tmp_xvattr; 177 xoptattr_t *xoap = NULL; 178 179 if (unnamed_vp) 180 use_vp = unnamed_vp; 181 else 182 use_vp = vp; 183 184 if (vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) { 185 xva_init(&tmp_xvattr); 186 xoap = xva_getxoptattr(&tmp_xvattr); 187 188 ASSERT(xoap); 189 190 smb_sa_to_va_mask(ret_attr->sa_mask, 191 &tmp_xvattr.xva_vattr.va_mask); 192 193 XVA_SET_REQ(&tmp_xvattr, XAT_READONLY); 194 XVA_SET_REQ(&tmp_xvattr, XAT_HIDDEN); 195 XVA_SET_REQ(&tmp_xvattr, XAT_SYSTEM); 196 XVA_SET_REQ(&tmp_xvattr, XAT_ARCHIVE); 197 XVA_SET_REQ(&tmp_xvattr, XAT_CREATETIME); 198 199 if ((error = VOP_GETATTR(use_vp, (vattr_t *)&tmp_xvattr, flags, 200 cr, ct)) != 0) 201 return (error); 202 203 ret_attr->sa_vattr = tmp_xvattr.xva_vattr; 204 205 /* 206 * Copy special attributes to ret_attr parameter 207 */ 208 209 ret_attr->sa_dosattr = 0; 210 211 ASSERT(tmp_xvattr.xva_vattr.va_mask & AT_XVATTR); 212 213 xoap = xva_getxoptattr(&tmp_xvattr); 214 ASSERT(xoap); 215 216 if (XVA_ISSET_RTN(&tmp_xvattr, XAT_READONLY)) { 217 if (xoap->xoa_readonly) 218 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_READONLY; 219 } 220 221 if (XVA_ISSET_RTN(&tmp_xvattr, XAT_HIDDEN)) { 222 if (xoap->xoa_hidden) 223 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_HIDDEN; 224 } 225 226 if (XVA_ISSET_RTN(&tmp_xvattr, XAT_SYSTEM)) { 227 if (xoap->xoa_system) 228 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_SYSTEM; 229 } 230 231 if (XVA_ISSET_RTN(&tmp_xvattr, XAT_ARCHIVE)) { 232 if (xoap->xoa_archive) 233 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_ARCHIVE; 234 } 235 236 ret_attr->sa_crtime = xoap->xoa_createtime; 237 238 if (unnamed_vp && (ret_attr->sa_mask & SMB_AT_SIZE)) { 239 /* 240 * Retrieve stream size attribute into temporary 241 * structure, in case the underlying file system 242 * returns attributes other than the size (we do not 243 * want to have ret_attr's other fields get 244 * overwritten). 245 * 246 * Note that vp is used here, and not use_vp. 247 * Also, only AT_SIZE is needed. 248 */ 249 250 tmp_xvattr.xva_vattr.va_mask = AT_SIZE; 251 252 if ((error = VOP_GETATTR(vp, (vattr_t *)&tmp_xvattr, 253 flags, cr, ct)) != 0) 254 return (error); 255 256 ret_attr->sa_vattr.va_size = 257 tmp_xvattr.xva_vattr.va_size; 258 259 } 260 261 if (ret_attr->sa_vattr.va_type == VDIR) { 262 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_DIRECTORY; 263 } 264 265 return (error); 266 } 267 268 /* 269 * Support for file systems without VFSFT_XVATTR 270 */ 271 272 smb_sa_to_va_mask(ret_attr->sa_mask, 273 &ret_attr->sa_vattr.va_mask); 274 275 error = VOP_GETATTR(use_vp, &ret_attr->sa_vattr, flags, cr, ct); 276 277 if (error != 0) 278 return (error); 279 280 /* 281 * "Fake" DOS attributes and create time, filesystem doesn't support 282 * them. 283 */ 284 285 ret_attr->sa_dosattr = 0; 286 ret_attr->sa_crtime = ret_attr->sa_vattr.va_ctime; 287 288 if (unnamed_vp && (ret_attr->sa_mask & SMB_AT_SIZE)) { 289 /* 290 * Retrieve stream size attribute into temporary structure, 291 * in case the underlying file system returns attributes 292 * other than the size (we do not want to have ret_attr's 293 * other fields get overwritten). 294 * 295 * Note that vp is used here, and not use_vp. 296 * Also, only AT_SIZE is needed. 297 */ 298 299 tmp_attr.sa_vattr.va_mask = AT_SIZE; 300 error = VOP_GETATTR(vp, &tmp_attr.sa_vattr, flags, cr, ct); 301 302 if (error != 0) 303 return (error); 304 305 306 ret_attr->sa_vattr.va_size = tmp_attr.sa_vattr.va_size; 307 } 308 309 if (ret_attr->sa_vattr.va_type == VDIR) { 310 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_DIRECTORY; 311 } 312 313 return (error); 314 } 315 316 /* 317 * smb_vop_setattr() 318 * 319 * smb_fsop_setattr()/smb_vop_setattr() should always be called from the CIFS 320 * service to set attributes due to special processing for streams files. 321 * 322 * When smb_vop_setattr() is called on a named stream file, all indicated 323 * attributes except the size are set on the unnamed stream file. The size 324 * (if indicated) is set on the named stream file. 325 */ 326 327 int 328 smb_vop_setattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *set_attr, 329 int flags, cred_t *cr, boolean_t no_xvattr, caller_context_t *ct) 330 { 331 int error = 0; 332 int at_size = 0; 333 vnode_t *use_vp; 334 xvattr_t tmp_xvattr; 335 xoptattr_t *xoap = NULL; 336 uint_t xva_mask; 337 338 if (unnamed_vp) { 339 use_vp = unnamed_vp; 340 if (set_attr->sa_mask & SMB_AT_SIZE) { 341 at_size = 1; 342 set_attr->sa_mask &= ~SMB_AT_SIZE; 343 } 344 } else { 345 use_vp = vp; 346 } 347 348 /* 349 * The caller should not be setting sa_vattr.va_mask, 350 * but rather sa_mask. 351 */ 352 353 set_attr->sa_vattr.va_mask = 0; 354 355 if ((no_xvattr == B_FALSE) && 356 vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) { 357 /* 358 * Initialize xvattr, including bzero 359 */ 360 xva_init(&tmp_xvattr); 361 xoap = xva_getxoptattr(&tmp_xvattr); 362 363 ASSERT(xoap); 364 365 /* 366 * Copy caller-specified classic attributes to tmp_xvattr. 367 * First save tmp_xvattr's mask (set in xva_init()), which 368 * contains AT_XVATTR. This is |'d in later if needed. 369 */ 370 371 xva_mask = tmp_xvattr.xva_vattr.va_mask; 372 tmp_xvattr.xva_vattr = set_attr->sa_vattr; 373 374 smb_sa_to_va_mask(set_attr->sa_mask, 375 &tmp_xvattr.xva_vattr.va_mask); 376 377 /* 378 * Do not set ctime (only the file system can do it) 379 */ 380 381 tmp_xvattr.xva_vattr.va_mask &= ~AT_CTIME; 382 383 if (set_attr->sa_mask & SMB_AT_DOSATTR) { 384 385 /* 386 * "|" in the original xva_mask, which contains 387 * AT_XVATTR 388 */ 389 390 tmp_xvattr.xva_vattr.va_mask |= xva_mask; 391 392 XVA_SET_REQ(&tmp_xvattr, XAT_ARCHIVE); 393 XVA_SET_REQ(&tmp_xvattr, XAT_SYSTEM); 394 XVA_SET_REQ(&tmp_xvattr, XAT_READONLY); 395 XVA_SET_REQ(&tmp_xvattr, XAT_HIDDEN); 396 397 /* 398 * set_attr->sa_dosattr: If a given bit is not set, 399 * that indicates that the corresponding field needs 400 * to be updated with a "0" value. This is done 401 * implicitly as the xoap->xoa_* fields were bzero'd. 402 */ 403 404 if (set_attr->sa_dosattr & FILE_ATTRIBUTE_ARCHIVE) 405 xoap->xoa_archive = 1; 406 407 if (set_attr->sa_dosattr & FILE_ATTRIBUTE_SYSTEM) 408 xoap->xoa_system = 1; 409 410 if (set_attr->sa_dosattr & FILE_ATTRIBUTE_READONLY) 411 xoap->xoa_readonly = 1; 412 413 if (set_attr->sa_dosattr & FILE_ATTRIBUTE_HIDDEN) 414 xoap->xoa_hidden = 1; 415 } 416 417 if (set_attr->sa_mask & SMB_AT_CRTIME) { 418 /* 419 * "|" in the original xva_mask, which contains 420 * AT_XVATTR 421 */ 422 423 tmp_xvattr.xva_vattr.va_mask |= xva_mask; 424 XVA_SET_REQ(&tmp_xvattr, XAT_CREATETIME); 425 xoap->xoa_createtime = set_attr->sa_crtime; 426 } 427 428 if ((error = VOP_SETATTR(use_vp, (vattr_t *)&tmp_xvattr, flags, 429 cr, ct)) != 0) 430 return (error); 431 432 /* 433 * If the size of the stream needs to be set, set it on 434 * the stream file directly. (All other indicated attributes 435 * are set on the stream's unnamed stream, above.) 436 */ 437 438 if (at_size) { 439 /* 440 * set_attr->sa_vattr.va_size already contains the 441 * size as set by the caller 442 * 443 * Note that vp is used here, and not use_vp. 444 * Also, only AT_SIZE is needed. 445 */ 446 447 set_attr->sa_vattr.va_mask = AT_SIZE; 448 error = VOP_SETATTR(vp, &set_attr->sa_vattr, flags, 449 cr, ct); 450 } 451 return (error); 452 } 453 /* 454 * Support for file systems without VFSFT_XVATTR or no_xvattr == B_TRUE 455 */ 456 smb_sa_to_va_mask(set_attr->sa_mask, &set_attr->sa_vattr.va_mask); 457 /* 458 * set_attr->sa_vattr already contains new values 459 * as set by the caller 460 */ 461 462 error = VOP_SETATTR(use_vp, &set_attr->sa_vattr, flags, cr, ct); 463 464 if (error != 0) 465 return (error); 466 467 if (at_size) { 468 /* 469 * set_attr->sa_vattr.va_size already contains the 470 * size as set by the caller 471 * 472 * Note that vp is used here, and not use_vp. 473 * Also, only AT_SIZE is needed. 474 */ 475 476 set_attr->sa_vattr.va_mask = AT_SIZE; 477 error = VOP_SETATTR(vp, &set_attr->sa_vattr, flags, cr, ct); 478 } 479 return (error); 480 } 481 482 /* 483 * smb_vop_access 484 * 485 * This is a wrapper round VOP_ACCESS. VOP_ACCESS checks the given mode 486 * against file's ACL or Unix permissions. CIFS on the other hand needs to 487 * know if the requested operation can succeed for the given object, this 488 * requires more checks in case of DELETE bit since permissions on the parent 489 * directory are important as well. Based on Windows rules if parent's ACL 490 * grant FILE_DELETE_CHILD a file can be delete regardless of the file's 491 * permissions. 492 */ 493 int 494 smb_vop_access(vnode_t *vp, int mode, int flags, vnode_t *dir_vp, cred_t *cr) 495 { 496 int error = 0; 497 498 if (mode == 0) 499 return (0); 500 501 if ((flags == V_ACE_MASK) && (mode & ACE_DELETE)) { 502 if (dir_vp) { 503 error = VOP_ACCESS(dir_vp, ACE_DELETE_CHILD, flags, 504 cr, NULL); 505 506 if (error == 0) 507 mode &= ~ACE_DELETE; 508 } 509 } 510 511 if (mode) { 512 error = VOP_ACCESS(vp, mode, flags, cr, NULL); 513 } 514 515 return (error); 516 } 517 518 /* 519 * smb_vop_lookup 520 * 521 * dvp: directory vnode (in) 522 * name: name of file to be looked up (in) 523 * vpp: looked-up vnode (out) 524 * od_name: on-disk name of file (out). 525 * This parameter is optional. If a pointer is passed in, it 526 * must be allocated with MAXNAMELEN bytes 527 * rootvp: vnode of the tree root (in) 528 * This parameter is always passed in non-NULL except at the time 529 * of share set up. 530 */ 531 532 int 533 smb_vop_lookup(vnode_t *dvp, char *name, vnode_t **vpp, char *od_name, 534 int flags, vnode_t *rootvp, cred_t *cr, caller_context_t *ct) 535 { 536 int error = 0; 537 int option_flags = 0; 538 pathname_t rpn; 539 540 if (*name == '\0') 541 return (EINVAL); 542 543 ASSERT(vpp); 544 *vpp = NULL; 545 546 if ((name[0] == '.') && (name[1] == '.') && (name[2] == 0)) { 547 if (rootvp && (dvp == rootvp)) { 548 VN_HOLD(dvp); 549 *vpp = dvp; 550 return (0); 551 } 552 553 if (dvp->v_flag & VROOT) { 554 vfs_t *vfsp; 555 vnode_t *cvp = dvp; 556 557 /* 558 * Set dvp and check for races with forced unmount 559 * (see lookuppnvp()) 560 */ 561 562 vfsp = cvp->v_vfsp; 563 vfs_rlock_wait(vfsp); 564 if (((dvp = cvp->v_vfsp->vfs_vnodecovered) == NULL) || 565 (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) { 566 vfs_unlock(vfsp); 567 return (EIO); 568 } 569 vfs_unlock(vfsp); 570 } 571 } 572 573 574 575 if (flags & SMB_IGNORE_CASE) 576 option_flags = FIGNORECASE; 577 578 pn_alloc(&rpn); 579 580 error = VOP_LOOKUP(dvp, name, vpp, NULL, option_flags, NULL, cr, 581 ct, NULL, &rpn); 582 583 if ((error == 0) && od_name) { 584 bzero(od_name, MAXNAMELEN); 585 if (option_flags == FIGNORECASE) 586 (void) strlcpy(od_name, rpn.pn_buf, MAXNAMELEN); 587 else 588 (void) strlcpy(od_name, name, MAXNAMELEN); 589 } 590 591 pn_free(&rpn); 592 return (error); 593 } 594 595 int 596 smb_vop_create(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp, 597 int flags, cred_t *cr, caller_context_t *ct, vsecattr_t *vsap) 598 { 599 int error; 600 int option_flags = 0; 601 602 if (flags & SMB_IGNORE_CASE) 603 option_flags = FIGNORECASE; 604 605 smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask); 606 607 error = VOP_CREATE(dvp, name, &attr->sa_vattr, EXCL, 608 attr->sa_vattr.va_mode, vpp, cr, option_flags, ct, vsap); 609 610 return (error); 611 } 612 613 int 614 smb_vop_remove(vnode_t *dvp, char *name, int flags, cred_t *cr, 615 caller_context_t *ct) 616 { 617 int error; 618 int option_flags = 0; 619 620 if (flags & SMB_IGNORE_CASE) 621 option_flags = FIGNORECASE; 622 623 error = VOP_REMOVE(dvp, name, cr, ct, option_flags); 624 625 return (error); 626 } 627 628 /* 629 * smb_vop_rename() 630 * 631 * The rename is for files in the same tree (identical TID) only. 632 */ 633 634 int 635 smb_vop_rename(vnode_t *from_dvp, char *from_name, vnode_t *to_dvp, 636 char *to_name, int flags, cred_t *cr, caller_context_t *ct) 637 { 638 int error; 639 int option_flags = 0; 640 641 642 if (flags & SMB_IGNORE_CASE) 643 option_flags = FIGNORECASE; 644 645 error = VOP_RENAME(from_dvp, from_name, to_dvp, to_name, cr, 646 ct, option_flags); 647 648 return (error); 649 } 650 651 int 652 smb_vop_mkdir(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp, 653 int flags, cred_t *cr, caller_context_t *ct, vsecattr_t *vsap) 654 { 655 int error; 656 int option_flags = 0; 657 658 659 660 if (flags & SMB_IGNORE_CASE) 661 option_flags = FIGNORECASE; 662 663 smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask); 664 665 error = VOP_MKDIR(dvp, name, &attr->sa_vattr, vpp, cr, ct, 666 option_flags, vsap); 667 668 return (error); 669 } 670 671 /* 672 * smb_vop_rmdir() 673 * 674 * Only simple rmdir supported, consistent with NT semantics 675 * (can only remove an empty directory). 676 * 677 */ 678 679 int 680 smb_vop_rmdir(vnode_t *dvp, char *name, int flags, cred_t *cr, 681 caller_context_t *ct) 682 { 683 int error; 684 int option_flags = 0; 685 686 if (flags & SMB_IGNORE_CASE) 687 option_flags = FIGNORECASE; 688 689 /* 690 * Comments adapted from rfs_rmdir(). 691 * 692 * VOP_RMDIR now takes a new third argument (the current 693 * directory of the process). That's because rmdir 694 * wants to return EINVAL if one tries to remove ".". 695 * Of course, SMB servers do not know what their 696 * clients' current directories are. We fake it by 697 * supplying a vnode known to exist and illegal to 698 * remove. 699 */ 700 701 error = VOP_RMDIR(dvp, name, rootdir, cr, ct, option_flags); 702 return (error); 703 } 704 705 int 706 smb_vop_commit(vnode_t *vp, cred_t *cr, caller_context_t *ct) 707 { 708 return (VOP_FSYNC(vp, 1, cr, ct)); 709 } 710 711 /* 712 * smb_vop_readdir() 713 * 714 * Upon return, the "name" field will contain either the on-disk name or, if 715 * it needs mangling or has a case-insensitive collision, the mangled 716 * "shortname." 717 * 718 * vpp is an optional parameter. If non-NULL, it will contain a pointer to 719 * the vnode for the name that is looked up (the vnode will be returned held). 720 * 721 * od_name is an optional parameter (NULL can be passed if the on-disk name 722 * is not needed by the caller). 723 */ 724 725 int 726 smb_vop_readdir(vnode_t *dvp, uint32_t *cookiep, char *name, int *namelen, 727 ino64_t *inop, vnode_t **vpp, char *od_name, int flags, cred_t *cr, 728 caller_context_t *ct) 729 { 730 int num_bytes; 731 int error = 0; 732 char *dirbuf = NULL; 733 734 ASSERT(dvp); 735 ASSERT(cookiep); 736 ASSERT(name); 737 ASSERT(namelen); 738 ASSERT(inop); 739 ASSERT(cr); 740 ASSERT(ct); 741 742 if (dvp->v_type != VDIR) { 743 *namelen = 0; 744 return (ENOTDIR); 745 } 746 747 if (vpp) 748 *vpp = NULL; 749 750 dirbuf = kmem_zalloc(SMB_MINLEN_RDDIR_BUF, KM_SLEEP); 751 num_bytes = SMB_MINLEN_RDDIR_BUF; 752 753 /* 754 * The goal is to retrieve the first valid entry from *cookiep 755 * forward. smb_vop_readdir_readpage() collects an 756 * SMB_MINLEN_RDDIR_BUF-size "page" of directory entry information. 757 * smb_vop_readdir_entry() attempts to find the first valid entry 758 * in that page. 759 */ 760 761 while ((error = smb_vop_readdir_readpage(dvp, dirbuf, *cookiep, 762 &num_bytes, cr, ct, flags)) == 0) { 763 764 if (num_bytes <= 0) 765 break; 766 767 name[0] = '\0'; 768 769 error = smb_vop_readdir_entry(dvp, cookiep, name, namelen, 770 inop, vpp, od_name, flags, cr, ct, dirbuf, 771 num_bytes); 772 773 if (error) 774 break; 775 776 if (*name) 777 break; 778 779 bzero(dirbuf, SMB_MINLEN_RDDIR_BUF); 780 num_bytes = SMB_MINLEN_RDDIR_BUF; 781 } 782 783 784 if (error) { 785 kmem_free(dirbuf, SMB_MINLEN_RDDIR_BUF); 786 *namelen = 0; 787 return (error); 788 } 789 790 if (num_bytes == 0) { /* EOF */ 791 kmem_free(dirbuf, SMB_MINLEN_RDDIR_BUF); 792 *cookiep = SMB_EOF; 793 *namelen = 0; 794 return (0); 795 } 796 797 kmem_free(dirbuf, SMB_MINLEN_RDDIR_BUF); 798 return (0); 799 } 800 801 /* 802 * smb_vop_readdir_readpage() 803 * 804 * Collects an SMB_MINLEN_RDDIR_BUF "page" of directory entries. (The 805 * directory entries are returned in an fs-independent format by the 806 * underlying file system. That is, the "page" of information returned is 807 * not literally stored on-disk in the format returned.) 808 * 809 * Much of the following is borrowed from getdents64() 810 * 811 * MAXGETDENTS_SIZE is defined in getdents.c 812 */ 813 814 #define MAXGETDENTS_SIZE (64 * 1024) 815 816 static int 817 smb_vop_readdir_readpage(vnode_t *vp, void *buf, uint32_t offset, int *count, 818 cred_t *cr, caller_context_t *ct, int flags) 819 { 820 int error = 0; 821 int rdirent_flags = 0; 822 int sink; 823 struct uio auio; 824 struct iovec aiov; 825 826 if (vp->v_type != VDIR) 827 return (ENOTDIR); 828 829 /* entflags not working for streams so don't try to use them */ 830 if (!(flags & SMB_STREAM_RDDIR) && 831 (vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS))) { 832 /* 833 * Setting V_RDDIR_ENTFLAGS will cause the buffer to 834 * be filled with edirent_t structures (instead of 835 * dirent64_t structures). 836 */ 837 rdirent_flags = V_RDDIR_ENTFLAGS; 838 839 if (*count < sizeof (edirent_t)) 840 return (EINVAL); 841 } else { 842 if (*count < sizeof (dirent64_t)) 843 return (EINVAL); 844 } 845 846 if (*count > MAXGETDENTS_SIZE) 847 *count = MAXGETDENTS_SIZE; 848 849 aiov.iov_base = buf; 850 aiov.iov_len = *count; 851 auio.uio_iov = &aiov; 852 auio.uio_iovcnt = 1; 853 auio.uio_loffset = (uint64_t)offset; 854 auio.uio_segflg = UIO_SYSSPACE; 855 auio.uio_resid = *count; 856 auio.uio_fmode = 0; 857 858 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 859 error = VOP_READDIR(vp, &auio, cr, &sink, ct, rdirent_flags); 860 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 861 862 if (error) { 863 if (error == ENOENT) { 864 /* Fake EOF if offset is bad due to dropping of lock */ 865 *count = 0; 866 return (0); 867 } else { 868 return (error); 869 } 870 } 871 872 /* 873 * Windows cannot handle an offset > SMB_EOF. 874 * Pretend we are at EOF. 875 */ 876 877 if (auio.uio_loffset > SMB_EOF) { 878 *count = 0; 879 return (0); 880 } 881 882 *count = *count - auio.uio_resid; 883 return (0); 884 } 885 886 /* 887 * smb_vop_readdir_entry() 888 * 889 * This function retrieves the first valid entry from the 890 * SMB_MINLEN_RDDIR_BUF-sized buffer returned by smb_vop_readdir_readpage() 891 * to smb_vop_readdir(). 892 * 893 * Both dirent64_t and edirent_t structures need to be handled. The former is 894 * needed for file systems that do not support VFSFT_DIRENTFLAGS. The latter 895 * is required for proper handling of case collisions on file systems that 896 * support case-insensitivity. edirent_t structures are also used for 897 * case-sensitive file systems if VFSFT_DIRENTFLAGS is supported. 898 */ 899 900 static int 901 smb_vop_readdir_entry(vnode_t *dvp, uint32_t *cookiep, char *name, int *namelen, 902 ino64_t *inop, vnode_t **vpp, char *od_name, int flags, cred_t *cr, 903 caller_context_t *ct, char *dirbuf, int num_bytes) 904 { 905 uint32_t next_cookie; 906 int ebufsize; 907 int error = 0; 908 int len; 909 int rc; 910 char shortname[MANGLE_NAMELEN]; 911 char name83[MANGLE_NAMELEN]; 912 char *ebuf = NULL; 913 edirent_t *edp; 914 dirent64_t *dp = NULL; 915 vnode_t *vp = NULL; 916 917 ASSERT(dirbuf); 918 919 /* 920 * Use edirent_t structure for both 921 * entflags not working for streams so don't try to use them 922 */ 923 if (!(flags & SMB_STREAM_RDDIR) && 924 (vfs_has_feature(dvp->v_vfsp, VFSFT_DIRENTFLAGS))) { 925 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 926 edp = (edirent_t *)dirbuf; 927 } else { 928 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 929 dp = (dirent64_t *)dirbuf; 930 ebufsize = EDIRENT_RECLEN(MAXNAMELEN); 931 ebuf = kmem_zalloc(ebufsize, KM_SLEEP); 932 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 933 edp = (edirent_t *)ebuf; 934 } 935 936 while (edp) { 937 if (dp) 938 DP_TO_EDP(dp, edp); 939 940 next_cookie = (uint32_t)edp->ed_off; 941 if (edp->ed_ino == 0) { 942 *cookiep = next_cookie; 943 944 if (dp) { 945 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 946 DP_ADVANCE(dp, dirbuf, num_bytes); 947 if (dp == NULL) 948 edp = NULL; 949 } else { 950 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 951 EDP_ADVANCE(edp, dirbuf, num_bytes); 952 } 953 continue; 954 } 955 956 len = strlen(edp->ed_name); 957 958 if (*namelen < len) { 959 *namelen = 0; 960 961 if (ebuf) 962 kmem_free(ebuf, ebufsize); 963 964 return (EOVERFLOW); 965 } 966 967 /* 968 * Do not pass SMB_IGNORE_CASE to smb_vop_lookup 969 */ 970 971 error = smb_vop_lookup(dvp, edp->ed_name, vpp ? vpp : &vp, 972 od_name, 0, NULL, cr, ct); 973 974 if (error) { 975 if (error == ENOENT) { 976 *cookiep = (uint32_t)next_cookie; 977 978 if (dp) { 979 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 980 DP_ADVANCE(dp, dirbuf, num_bytes); 981 if (dp == NULL) 982 edp = NULL; 983 } else { 984 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 985 EDP_ADVANCE(edp, dirbuf, num_bytes); 986 } 987 continue; 988 } 989 990 991 *namelen = 0; 992 993 if (ebuf) 994 kmem_free(ebuf, ebufsize); 995 996 return (error); 997 } 998 999 if ((flags & SMB_IGNORE_CASE) && ED_CASE_CONFLICTS(edp)) { 1000 rc = smb_mangle_name(edp->ed_ino, edp->ed_name, 1001 shortname, name83, 1); 1002 1003 if (rc == 1) { /* success */ 1004 (void) strlcpy(name, shortname, *namelen + 1); 1005 *namelen = strlen(shortname); 1006 } else { 1007 (void) strlcpy(name, edp->ed_name, 1008 *namelen + 1); 1009 name[*namelen] = '\0'; 1010 } 1011 1012 } else { 1013 (void) strlcpy(name, edp->ed_name, *namelen + 1); 1014 *namelen = len; 1015 } 1016 1017 if (vpp == NULL) 1018 VN_RELE(vp); 1019 1020 if (inop) 1021 *inop = edp->ed_ino; 1022 1023 *cookiep = (uint32_t)next_cookie; 1024 break; 1025 } 1026 1027 if (ebuf) 1028 kmem_free(ebuf, ebufsize); 1029 1030 return (error); 1031 } 1032 1033 /* 1034 * smb_sa_to_va_mask 1035 * 1036 * Set va_mask by running through the SMB_AT_* #define's and 1037 * setting those bits that correspond to the SMB_AT_* bits 1038 * set in sa_mask. 1039 */ 1040 1041 void 1042 smb_sa_to_va_mask(uint_t sa_mask, uint_t *va_maskp) 1043 { 1044 int i; 1045 uint_t smask; 1046 1047 smask = (sa_mask); 1048 for (i = SMB_AT_TYPE; (i < SMB_AT_MAX) && (smask != 0); ++i) { 1049 if (smask & 1) 1050 *(va_maskp) |= smb_attrmap[i]; 1051 1052 smask >>= 1; 1053 } 1054 } 1055 1056 /* 1057 * smb_vop_getdents() 1058 * 1059 * Upon success, the smb_node corresponding to each entry returned will 1060 * have a reference taken on it. These will be released in 1061 * smb_trans2_find_get_dents(). 1062 * 1063 * If an error is returned from this routine, a list of already processed 1064 * entries will be returned. The smb_nodes corresponding to these entries 1065 * will be referenced, and will be released in smb_trans2_find_get_dents(). 1066 * 1067 * The returned dp->d_name field will contain either the on-disk name or, if 1068 * it needs mangling or has a case-insensitive collision, the mangled 1069 * "shortname." In this case, the on-disk name can be retrieved from the 1070 * smb_node's od_name (the smb_node is passed to smb_gather_dents_info()). 1071 */ 1072 1073 int /*ARGSUSED*/ 1074 smb_vop_getdents( 1075 smb_node_t *dir_snode, 1076 uint32_t *cookiep, 1077 uint64_t *verifierp, 1078 int32_t *dircountp, 1079 char *arg, 1080 char *pattern, 1081 uint32_t flags, 1082 smb_request_t *sr, 1083 cred_t *cr, 1084 caller_context_t *ct) 1085 { 1086 int error = 0; 1087 int maxentries; 1088 int num_bytes; 1089 int resid; 1090 char *dirbuf = NULL; 1091 vnode_t *dvp; 1092 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 1093 smb_dent_info_hdr_t *ihdr = (smb_dent_info_hdr_t *)arg; 1094 1095 dvp = dir_snode->vp; 1096 1097 resid = ihdr->uio.uio_resid; 1098 maxentries = resid / SMB_MAX_DENT_INFO_SIZE; 1099 1100 bzero(ihdr->iov->iov_base, resid); 1101 1102 dirbuf = kmem_alloc(SMB_MINLEN_RDDIR_BUF, KM_SLEEP); 1103 1104 while (maxentries) { 1105 1106 bzero(dirbuf, SMB_MINLEN_RDDIR_BUF); 1107 1108 num_bytes = SMB_MINLEN_RDDIR_BUF; 1109 error = smb_vop_readdir_readpage(dvp, dirbuf, *cookiep, 1110 &num_bytes, cr, ct, flags); 1111 1112 if (error || (num_bytes <= 0)) 1113 break; 1114 1115 error = smb_vop_getdents_entries(dir_snode, cookiep, dircountp, 1116 arg, flags, sr, cr, ct, dirbuf, &maxentries, num_bytes, 1117 pattern); 1118 1119 if (error) 1120 goto out; 1121 } 1122 1123 if (num_bytes < 0) { 1124 error = -1; 1125 } else if (num_bytes == 0) { 1126 *cookiep = SMB_EOF; 1127 error = 0; 1128 } else { 1129 error = 0; 1130 } 1131 1132 out: 1133 if (dirbuf) 1134 kmem_free(dirbuf, SMB_MINLEN_RDDIR_BUF); 1135 1136 return (error); 1137 } 1138 1139 /* 1140 * smb_vop_getdents_entries() 1141 * 1142 * This function retrieves names from the SMB_MINLEN_RDDIR_BUF-sized buffer 1143 * returned by smb_vop_readdir_readpage() to smb_vop_getdents(). 1144 * 1145 * Both dirent64_t and edirent_t structures need to be handled. The former is 1146 * needed for file systems that do not support VFSFT_DIRENTFLAGS. The latter 1147 * is required for properly handling case collisions on file systems that 1148 * support case-insensitivity. edirent_t is also used on case-sensitive 1149 * file systems where VFSFT_DIRENTFLAGS is available. 1150 */ 1151 1152 static int 1153 smb_vop_getdents_entries( 1154 smb_node_t *dir_snode, 1155 uint32_t *cookiep, 1156 int32_t *dircountp, 1157 char *arg, 1158 uint32_t flags, 1159 struct smb_request *sr, 1160 cred_t *cr, 1161 caller_context_t *ct, 1162 char *dirbuf, 1163 int *maxentries, 1164 int num_bytes, 1165 char *pattern) 1166 { 1167 uint32_t next_cookie; 1168 int ebufsize; 1169 char *tmp_name; 1170 int error; 1171 int rc; 1172 char shortname[MANGLE_NAMELEN]; 1173 char name83[MANGLE_NAMELEN]; 1174 char *ebuf = NULL; 1175 dirent64_t *dp = NULL; 1176 edirent_t *edp; 1177 smb_node_t *ret_snode; 1178 smb_attr_t ret_attr; 1179 vnode_t *dvp; 1180 vnode_t *fvp; 1181 1182 ASSERT(dirbuf); 1183 1184 dvp = dir_snode->vp; 1185 1186 if (vfs_has_feature(dvp->v_vfsp, VFSFT_DIRENTFLAGS)) { 1187 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 1188 edp = (edirent_t *)dirbuf; 1189 } else { 1190 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 1191 dp = (dirent64_t *)dirbuf; 1192 ebufsize = EDIRENT_RECLEN(MAXNAMELEN); 1193 ebuf = kmem_zalloc(ebufsize, KM_SLEEP); 1194 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 1195 edp = (edirent_t *)ebuf; 1196 } 1197 1198 while (edp) { 1199 if (dp) 1200 DP_TO_EDP(dp, edp); 1201 1202 if (*maxentries == 0) 1203 break; 1204 1205 next_cookie = (uint32_t)edp->ed_off; 1206 1207 if (edp->ed_ino == 0) { 1208 *cookiep = next_cookie; 1209 if (dp) { 1210 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 1211 DP_ADVANCE(dp, dirbuf, num_bytes); 1212 if (dp == NULL) 1213 edp = NULL; 1214 } else { 1215 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 1216 EDP_ADVANCE(edp, dirbuf, num_bytes); 1217 } 1218 continue; 1219 } 1220 1221 error = smb_vop_lookup(dvp, edp->ed_name, &fvp, 1222 NULL, 0, NULL, cr, ct); 1223 1224 if (error) { 1225 if (error == ENOENT) { 1226 *cookiep = next_cookie; 1227 if (dp) { 1228 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 1229 DP_ADVANCE(dp, dirbuf, 1230 num_bytes); 1231 if (dp == NULL) 1232 edp = NULL; 1233 } else { 1234 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 1235 EDP_ADVANCE(edp, dirbuf, 1236 num_bytes); 1237 } 1238 continue; 1239 } 1240 if (ebuf) 1241 kmem_free(ebuf, ebufsize); 1242 1243 return (error); 1244 } 1245 1246 ret_snode = smb_node_lookup(sr, NULL, cr, fvp, 1247 edp->ed_name, dir_snode, NULL, &ret_attr); 1248 1249 if (ret_snode == NULL) { 1250 VN_RELE(fvp); 1251 1252 if (ebuf) 1253 kmem_free(ebuf, ebufsize); 1254 1255 return (ENOMEM); 1256 } 1257 1258 if (smb_match_name(edp->ed_ino, edp->ed_name, shortname, 1259 name83, pattern, (flags & SMB_IGNORE_CASE))) { 1260 1261 tmp_name = edp->ed_name; 1262 1263 if ((flags & SMB_IGNORE_CASE) && 1264 ED_CASE_CONFLICTS(edp)) { 1265 rc = smb_mangle_name(edp->ed_ino, edp->ed_name, 1266 shortname, name83, 1); 1267 if (rc == 1) 1268 tmp_name = shortname; 1269 } else { 1270 rc = smb_mangle_name(edp->ed_ino, edp->ed_name, 1271 shortname, name83, 0); 1272 } 1273 1274 if (rc != 1) { 1275 (void) strlcpy(shortname, edp->ed_name, 1276 MANGLE_NAMELEN); 1277 (void) strlcpy(name83, edp->ed_name, 1278 MANGLE_NAMELEN); 1279 shortname[MANGLE_NAMELEN - 1] = '\0'; 1280 name83[MANGLE_NAMELEN - 1] = '\0'; 1281 } 1282 1283 error = smb_gather_dents_info(arg, edp->ed_ino, 1284 strlen(tmp_name), tmp_name, next_cookie, dircountp, 1285 &ret_attr, ret_snode, shortname, name83); 1286 1287 if (error > 0) { 1288 if (ebuf) 1289 kmem_free(ebuf, ebufsize); 1290 return (error); 1291 } 1292 1293 /* 1294 * Treat errors from smb_gather_dents_info() that are 1295 * < 0 the same as EOF. 1296 */ 1297 if (error < 0) { 1298 if (ebuf) 1299 kmem_free(ebuf, ebufsize); 1300 *maxentries = 0; 1301 return (0); 1302 } 1303 (*maxentries)--; 1304 } else { 1305 smb_node_release(ret_snode); 1306 } 1307 1308 *cookiep = next_cookie; 1309 1310 if (dp) { 1311 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 1312 DP_ADVANCE(dp, dirbuf, num_bytes); 1313 if (dp == NULL) 1314 edp = NULL; 1315 } else { 1316 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 1317 EDP_ADVANCE(edp, dirbuf, num_bytes); 1318 } 1319 } 1320 1321 if (ebuf) 1322 kmem_free(ebuf, ebufsize); 1323 1324 return (0); 1325 } 1326 1327 /* 1328 * smb_vop_stream_lookup() 1329 * 1330 * The name returned in od_name is the on-disk name of the stream with the 1331 * SMB_STREAM_PREFIX stripped off. od_name should be allocated to MAXNAMELEN 1332 * by the caller. 1333 */ 1334 1335 int 1336 smb_vop_stream_lookup(vnode_t *fvp, char *stream_name, vnode_t **vpp, 1337 char *od_name, vnode_t **xattrdirvpp, int flags, vnode_t *rootvp, 1338 cred_t *cr, caller_context_t *ct) 1339 { 1340 char *solaris_stream_name; 1341 char *name; 1342 int error; 1343 1344 if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp, 1345 LOOKUP_XATTR | CREATE_XATTR_DIR, cr, ct)) != 0) 1346 return (error); 1347 1348 /* 1349 * Prepend SMB_STREAM_PREFIX to stream name 1350 */ 1351 1352 solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 1353 (void) sprintf(solaris_stream_name, "%s%s", SMB_STREAM_PREFIX, 1354 stream_name); 1355 1356 /* 1357 * "name" will hold the on-disk name returned from smb_vop_lookup 1358 * for the stream, including the SMB_STREAM_PREFIX. 1359 */ 1360 1361 name = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 1362 1363 if ((error = smb_vop_lookup(*xattrdirvpp, solaris_stream_name, vpp, 1364 name, flags, rootvp, cr, ct)) != 0) { 1365 VN_RELE(*xattrdirvpp); 1366 } else { 1367 (void) strlcpy(od_name, &(name[SMB_STREAM_PREFIX_LEN]), 1368 MAXNAMELEN); 1369 } 1370 1371 kmem_free(solaris_stream_name, MAXNAMELEN); 1372 kmem_free(name, MAXNAMELEN); 1373 1374 return (error); 1375 } 1376 1377 int 1378 smb_vop_stream_create(vnode_t *fvp, char *stream_name, smb_attr_t *attr, 1379 vnode_t **vpp, vnode_t **xattrdirvpp, int flags, cred_t *cr, 1380 caller_context_t *ct) 1381 { 1382 char *solaris_stream_name; 1383 int error; 1384 1385 if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp, 1386 LOOKUP_XATTR | CREATE_XATTR_DIR, cr, ct)) != 0) 1387 return (error); 1388 1389 /* 1390 * Prepend SMB_STREAM_PREFIX to stream name 1391 */ 1392 1393 solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 1394 (void) sprintf(solaris_stream_name, "%s%s", SMB_STREAM_PREFIX, 1395 stream_name); 1396 1397 if ((error = smb_vop_create(*xattrdirvpp, solaris_stream_name, attr, 1398 vpp, flags, cr, ct, NULL)) != 0) 1399 VN_RELE(*xattrdirvpp); 1400 1401 kmem_free(solaris_stream_name, MAXNAMELEN); 1402 1403 return (error); 1404 } 1405 1406 int 1407 smb_vop_stream_remove(vnode_t *vp, char *stream_name, int flags, cred_t *cr, 1408 caller_context_t *ct) 1409 { 1410 char *solaris_stream_name; 1411 vnode_t *xattrdirvp; 1412 int error; 1413 1414 if ((error = smb_vop_lookup_xattrdir(vp, &xattrdirvp, LOOKUP_XATTR, cr, 1415 ct)) != 0) 1416 return (error); 1417 1418 /* 1419 * Prepend SMB_STREAM_PREFIX to stream name 1420 */ 1421 1422 solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 1423 (void) sprintf(solaris_stream_name, "%s%s", SMB_STREAM_PREFIX, 1424 stream_name); 1425 1426 /* XXX might have to use kcred */ 1427 error = smb_vop_remove(xattrdirvp, solaris_stream_name, flags, cr, ct); 1428 1429 kmem_free(solaris_stream_name, MAXNAMELEN); 1430 1431 return (error); 1432 } 1433 1434 /* 1435 * smb_vop_stream_readdir() 1436 * 1437 * Note: stream_info.size is not filled in in this routine. 1438 * It needs to be filled in by the caller due to the parameters for getattr. 1439 * 1440 * stream_info.name is set to the on-disk stream name with the SMB_STREAM_PREFIX 1441 * removed. 1442 */ 1443 1444 int 1445 smb_vop_stream_readdir(vnode_t *fvp, uint32_t *cookiep, 1446 struct fs_stream_info *stream_info, vnode_t **vpp, vnode_t **xattrdirvpp, 1447 int flags, cred_t *cr, caller_context_t *ct) 1448 { 1449 int nsize = MAXNAMELEN-1; 1450 int error = 0; 1451 ino64_t ino; 1452 char *tmp_name; 1453 vnode_t *xattrdirvp; 1454 vnode_t *vp; 1455 1456 if ((error = smb_vop_lookup_xattrdir(fvp, &xattrdirvp, LOOKUP_XATTR, 1457 cr, ct)) != 0) 1458 return (error); 1459 1460 bzero(stream_info->name, sizeof (stream_info->name)); 1461 stream_info->size = 0; 1462 1463 tmp_name = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 1464 1465 for (;;) { 1466 error = smb_vop_readdir(xattrdirvp, cookiep, tmp_name, &nsize, 1467 &ino, &vp, NULL, flags | SMB_STREAM_RDDIR, cr, ct); 1468 1469 if (error || (*cookiep == SMB_EOF)) 1470 break; 1471 1472 if (strncmp(tmp_name, SMB_STREAM_PREFIX, 1473 SMB_STREAM_PREFIX_LEN)) { 1474 VN_RELE(vp); 1475 continue; 1476 } 1477 1478 tmp_name[nsize] = '\0'; 1479 (void) strlcpy(stream_info->name, 1480 &(tmp_name[SMB_STREAM_PREFIX_LEN]), 1481 sizeof (stream_info->name)); 1482 1483 nsize -= SMB_STREAM_PREFIX_LEN; 1484 break; 1485 } 1486 1487 if ((error == 0) && nsize) { 1488 if (vpp) 1489 *vpp = vp; 1490 else 1491 VN_RELE(vp); 1492 1493 if (xattrdirvpp) 1494 *xattrdirvpp = xattrdirvp; 1495 else 1496 VN_RELE(xattrdirvp); 1497 1498 } 1499 1500 kmem_free(tmp_name, MAXNAMELEN); 1501 1502 return (error); 1503 } 1504 1505 int 1506 smb_vop_lookup_xattrdir(vnode_t *fvp, vnode_t **xattrdirvpp, int flags, 1507 cred_t *cr, caller_context_t *ct) 1508 { 1509 int error; 1510 1511 error = VOP_LOOKUP(fvp, "", xattrdirvpp, NULL, flags, NULL, cr, ct, 1512 NULL, NULL); 1513 return (error); 1514 } 1515 1516 /* 1517 * smb_vop_traverse_check() 1518 * 1519 * This function checks to see if the passed-in vnode has a file system 1520 * mounted on it. If it does, the mount point is "traversed" and the 1521 * vnode for the root of the file system is returned. 1522 */ 1523 1524 int 1525 smb_vop_traverse_check(vnode_t **vpp) 1526 { 1527 int error; 1528 1529 if (vn_mountedvfs(*vpp) == 0) 1530 return (0); 1531 1532 /* 1533 * traverse() may return a different held vnode, even in the error case. 1534 * If it returns a different vnode, it will have released the original. 1535 */ 1536 1537 error = traverse(vpp); 1538 1539 return (error); 1540 } 1541 1542 int /*ARGSUSED*/ 1543 smb_vop_statfs(vnode_t *vp, struct statvfs64 *statp, cred_t *cr) 1544 { 1545 int error; 1546 1547 error = VFS_STATVFS(vp->v_vfsp, statp); 1548 1549 return (error); 1550 } 1551 1552 /* 1553 * smb_vop_acl_read 1554 * 1555 * Reads the ACL of the specified file into 'aclp'. 1556 * acl_type is the type of ACL which the filesystem supports. 1557 * 1558 * Caller has to free the allocated memory for aclp by calling 1559 * acl_free(). 1560 */ 1561 int 1562 smb_vop_acl_read(vnode_t *vp, acl_t **aclp, int flags, acl_type_t acl_type, 1563 cred_t *cr, caller_context_t *ct) 1564 { 1565 int error; 1566 vsecattr_t vsecattr; 1567 1568 ASSERT(vp); 1569 ASSERT(aclp); 1570 1571 *aclp = NULL; 1572 bzero(&vsecattr, sizeof (vsecattr_t)); 1573 1574 switch (acl_type) { 1575 case ACLENT_T: 1576 vsecattr.vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL | 1577 VSA_DFACLCNT; 1578 break; 1579 1580 case ACE_T: 1581 vsecattr.vsa_mask = VSA_ACE | VSA_ACECNT | VSA_ACE_ACLFLAGS; 1582 break; 1583 1584 default: 1585 return (EINVAL); 1586 } 1587 1588 if (error = VOP_GETSECATTR(vp, &vsecattr, flags, cr, ct)) 1589 return (error); 1590 1591 *aclp = smb_fsacl_from_vsa(&vsecattr, acl_type); 1592 if (vp->v_type == VDIR) 1593 (*aclp)->acl_flags |= ACL_IS_DIR; 1594 1595 return (0); 1596 } 1597 1598 /* 1599 * smb_vop_acl_write 1600 * 1601 * Writes the given ACL in aclp for the specified file. 1602 */ 1603 int 1604 smb_vop_acl_write(vnode_t *vp, acl_t *aclp, int flags, cred_t *cr, 1605 caller_context_t *ct) 1606 { 1607 int error; 1608 vsecattr_t vsecattr; 1609 int aclbsize; 1610 1611 ASSERT(vp); 1612 ASSERT(aclp); 1613 1614 error = smb_fsacl_to_vsa(aclp, &vsecattr, &aclbsize); 1615 1616 if (error == 0) { 1617 (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); 1618 error = VOP_SETSECATTR(vp, &vsecattr, flags, cr, ct); 1619 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); 1620 } 1621 1622 if (aclbsize && vsecattr.vsa_aclentp) 1623 kmem_free(vsecattr.vsa_aclentp, aclbsize); 1624 1625 return (error); 1626 } 1627 1628 /* 1629 * smb_vop_acl_type 1630 * 1631 * Determines the ACL type for the given vnode. 1632 * ACLENT_T is a Posix ACL and ACE_T is a ZFS ACL. 1633 */ 1634 acl_type_t 1635 smb_vop_acl_type(vnode_t *vp) 1636 { 1637 int error; 1638 ulong_t whichacl; 1639 1640 error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &whichacl, kcred, NULL); 1641 if (error != 0) { 1642 /* 1643 * If we got an error, then the filesystem 1644 * likely does not understand the _PC_ACL_ENABLED 1645 * pathconf. In this case, we fall back to trying 1646 * POSIX-draft (aka UFS-style) ACLs. 1647 */ 1648 whichacl = _ACL_ACLENT_ENABLED; 1649 } 1650 1651 if (!(whichacl & (_ACL_ACE_ENABLED | _ACL_ACLENT_ENABLED))) { 1652 /* 1653 * If the file system supports neither ACE nor 1654 * ACLENT ACLs we will fall back to UFS-style ACLs 1655 * like we did above if there was an error upon 1656 * calling VOP_PATHCONF. 1657 * 1658 * ACE and ACLENT type ACLs are the only interfaces 1659 * supported thus far. If any other bits are set on 1660 * 'whichacl' upon return from VOP_PATHCONF, we will 1661 * ignore them. 1662 */ 1663 whichacl = _ACL_ACLENT_ENABLED; 1664 } 1665 1666 if (whichacl == _ACL_ACLENT_ENABLED) 1667 return (ACLENT_T); 1668 1669 return (ACE_T); 1670 } 1671 1672 static int zfs_perms[] = { 1673 ACE_READ_DATA, ACE_WRITE_DATA, ACE_APPEND_DATA, ACE_READ_NAMED_ATTRS, 1674 ACE_WRITE_NAMED_ATTRS, ACE_EXECUTE, ACE_DELETE_CHILD, 1675 ACE_READ_ATTRIBUTES, ACE_WRITE_ATTRIBUTES, ACE_DELETE, ACE_READ_ACL, 1676 ACE_WRITE_ACL, ACE_WRITE_OWNER, ACE_SYNCHRONIZE 1677 }; 1678 1679 static int unix_perms[] = { VREAD, VWRITE, VEXEC }; 1680 /* 1681 * smb_vop_eaccess 1682 * 1683 * Returns the effective permission of the given credential for the 1684 * specified object. 1685 * 1686 * This is just a workaround. We need VFS/FS support for this. 1687 */ 1688 void 1689 smb_vop_eaccess(vnode_t *vp, int *mode, int flags, vnode_t *dir_vp, cred_t *cr) 1690 { 1691 int error, i; 1692 int pnum; 1693 1694 *mode = 0; 1695 1696 if (flags == V_ACE_MASK) { 1697 pnum = sizeof (zfs_perms) / sizeof (int); 1698 1699 for (i = 0; i < pnum; i++) { 1700 error = smb_vop_access(vp, zfs_perms[i], flags, 1701 dir_vp, cr); 1702 if (error == 0) 1703 *mode |= zfs_perms[i]; 1704 } 1705 } else { 1706 pnum = sizeof (unix_perms) / sizeof (int); 1707 1708 for (i = 0; i < pnum; i++) { 1709 error = smb_vop_access(vp, unix_perms[i], flags, 1710 dir_vp, cr); 1711 if (error == 0) 1712 *mode |= unix_perms[i]; 1713 } 1714 } 1715 } 1716