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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2013-2021 Tintri by DDN, Inc. All rights reserved. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/stat.h> 28 #include <sys/uio.h> 29 #include <sys/statvfs.h> 30 #include <sys/vnode.h> 31 #include <sys/thread.h> 32 #include <sys/pathname.h> 33 #include <sys/cred.h> 34 #include <sys/extdirent.h> 35 #include <sys/nbmlock.h> 36 #include <sys/share.h> 37 #include <sys/fcntl.h> 38 #include <sys/priv_const.h> 39 #include <sys/policy.h> 40 #include <nfs/lm.h> 41 42 #include <smbsrv/smb_kproto.h> 43 #include <smbsrv/string.h> 44 #include <smbsrv/smb_vops.h> 45 #include <smbsrv/smb_fsops.h> 46 47 /* 48 * CATIA support 49 * 50 * CATIA V4 is a UNIX product and uses characters in filenames that 51 * are considered invalid by Windows. CATIA V5 is available on both 52 * UNIX and Windows. Thus, as CATIA customers migrate from V4 to V5, 53 * some V4 files could become inaccessible to windows clients if the 54 * filename contains the characters that are considered illegal in 55 * Windows. In order to address this issue an optional character 56 * translation is applied to filenames at the smb_vop interface. 57 * 58 * Character Translation Table 59 * ---------------------------------- 60 * Unix-char (v4) | Windows-char (v5) 61 * ---------------------------------- 62 * * | 0x00a4 Currency Sign 63 * | | 0x00a6 Broken Bar 64 * " | 0x00a8 Diaeresis 65 * < | 0x00ab Left-Pointing Double Angle Quotation Mark 66 * > | 0x00bb Right-Pointing Double Angle Quotation Mark 67 * ? | 0x00bf Inverted Question mark 68 * : | 0x00f7 Division Sign 69 * / | 0x00f8 Latin Small Letter o with stroke 70 * \ | 0x00ff Latin Small Letter Y with Diaeresis 71 * 72 * 73 * Two lookup tables are used to perform the character translation: 74 * 75 * smb_catia_v5_lookup - provides the mapping between UNIX ASCII (v4) 76 * characters and equivalent or translated wide characters. 77 * It is indexed by the decimal value of the ASCII character (0-127). 78 * 79 * smb_catia_v4_lookup - provides the mapping between wide characters 80 * in the range from 0x00A4 to 0x00FF and their UNIX (v4) equivalent 81 * (in wide character format). It is indexed by the decimal value of 82 * the wide character (164-255) with an offset of -164. 83 * If this translation produces a filename containing a '/' create, mkdir 84 * or rename (to the '/' name) operations will not be permitted. It is 85 * not valid to create a filename with a '/' in it. However, if such a 86 * file already exists other operations (e.g, lookup, delete, rename) 87 * are permitted on it. 88 */ 89 90 /* number of characters mapped */ 91 #define SMB_CATIA_NUM_MAPS 9 92 93 /* Windows Characters used in special character mapping */ 94 #define SMB_CATIA_WIN_CURRENCY 0x00a4 95 #define SMB_CATIA_WIN_BROKEN_BAR 0x00a6 96 #define SMB_CATIA_WIN_DIAERESIS 0x00a8 97 #define SMB_CATIA_WIN_LEFT_ANGLE 0x00ab 98 #define SMB_CATIA_WIN_RIGHT_ANGLE 0x00bb 99 #define SMB_CATIA_WIN_INVERTED_QUESTION 0x00bf 100 #define SMB_CATIA_WIN_DIVISION 0x00f7 101 #define SMB_CATIA_WIN_LATIN_O 0x00f8 102 #define SMB_CATIA_WIN_LATIN_Y 0x00ff 103 104 #define SMB_CATIA_V4_LOOKUP_LOW SMB_CATIA_WIN_CURRENCY 105 #define SMB_CATIA_V4_LOOKUP_UPPER SMB_CATIA_WIN_LATIN_Y 106 #define SMB_CATIA_V4_LOOKUP_MAX \ 107 (SMB_CATIA_V4_LOOKUP_UPPER - SMB_CATIA_V4_LOOKUP_LOW + 1) 108 #define SMB_CATIA_V5_LOOKUP_MAX 0x0080 109 110 typedef struct smb_catia_map 111 { 112 unsigned char unixchar; /* v4 */ 113 smb_wchar_t winchar; /* v5 */ 114 } smb_catia_map_t; 115 116 smb_catia_map_t const catia_maps[SMB_CATIA_NUM_MAPS] = 117 { 118 {'"', SMB_CATIA_WIN_DIAERESIS}, 119 {'*', SMB_CATIA_WIN_CURRENCY}, 120 {':', SMB_CATIA_WIN_DIVISION}, 121 {'<', SMB_CATIA_WIN_LEFT_ANGLE}, 122 {'>', SMB_CATIA_WIN_RIGHT_ANGLE}, 123 {'?', SMB_CATIA_WIN_INVERTED_QUESTION}, 124 {'\\', SMB_CATIA_WIN_LATIN_Y}, 125 {'/', SMB_CATIA_WIN_LATIN_O}, 126 {'|', SMB_CATIA_WIN_BROKEN_BAR} 127 }; 128 129 static smb_wchar_t smb_catia_v5_lookup[SMB_CATIA_V5_LOOKUP_MAX]; 130 static smb_wchar_t smb_catia_v4_lookup[SMB_CATIA_V4_LOOKUP_MAX]; 131 132 static void smb_vop_setup_xvattr(smb_attr_t *smb_attr, xvattr_t *xvattr); 133 static void smb_sa_to_va_mask(uint_t sa_mask, uint_t *va_maskp); 134 static void smb_sa_to_va_mask_get(uint_t sa_mask, uint_t *va_maskp); 135 static callb_cpr_t *smb_lock_frlock_callback(flk_cb_when_t, void *); 136 static void smb_vop_catia_init(); 137 138 extern sysid_t lm_alloc_sysidt(); 139 140 #define SMB_AT_MAX 16 141 static const uint_t smb_attrmap[SMB_AT_MAX] = { 142 0, 143 AT_TYPE, 144 AT_MODE, 145 AT_UID, 146 AT_GID, 147 AT_FSID, 148 AT_NODEID, 149 AT_NLINK, 150 AT_SIZE, 151 AT_ATIME, 152 AT_MTIME, 153 AT_CTIME, 154 AT_RDEV, 155 AT_BLKSIZE, 156 AT_NBLOCKS, 157 AT_SEQ 158 }; 159 160 static boolean_t smb_vop_initialized = B_FALSE; 161 caller_context_t smb_ct; 162 163 /* 164 * smb_vop_init 165 * 166 * This function is not multi-thread safe. The caller must make sure only one 167 * thread makes the call. 168 */ 169 int 170 smb_vop_init(void) 171 { 172 if (smb_vop_initialized) 173 return (0); 174 /* 175 * The caller_context will be used primarily for range locking. 176 * Since the CIFS server is mapping its locks to POSIX locks, 177 * only one pid is used for operations originating from the 178 * CIFS server (to represent CIFS in the VOP_FRLOCK routines). 179 * 180 * XXX: Should smb_ct be per-zone? 181 */ 182 smb_ct.cc_sysid = lm_alloc_sysidt(); 183 if (smb_ct.cc_sysid == LM_NOSYSID) 184 return (ENOMEM); 185 186 smb_ct.cc_caller_id = fs_new_caller_id(); 187 smb_ct.cc_pid = IGN_PID; 188 smb_ct.cc_flags = 0; 189 smb_vop_catia_init(); 190 191 smb_vop_initialized = B_TRUE; 192 return (0); 193 } 194 195 /* 196 * smb_vop_fini 197 * 198 * This function is not multi-thread safe. The caller must make sure only one 199 * thread makes the call. 200 */ 201 void 202 smb_vop_fini(void) 203 { 204 if (!smb_vop_initialized) 205 return; 206 207 lm_free_sysidt(smb_ct.cc_sysid); 208 smb_ct.cc_pid = IGN_PID; 209 smb_ct.cc_sysid = LM_NOSYSID; 210 smb_vop_initialized = B_FALSE; 211 } 212 213 /* 214 * The smb_ct will be used primarily for range locking. 215 * Since the CIFS server is mapping its locks to POSIX locks, 216 * only one pid is used for operations originating from the 217 * CIFS server (to represent CIFS in the VOP_FRLOCK routines). 218 */ 219 int 220 smb_vop_open(vnode_t **vpp, int mode, cred_t *cred) 221 { 222 return (VOP_OPEN(vpp, mode, cred, &smb_ct)); 223 } 224 225 void 226 smb_vop_close(vnode_t *vp, int mode, cred_t *cred) 227 { 228 (void) VOP_CLOSE(vp, mode, 1, (offset_t)0, cred, &smb_ct); 229 } 230 231 int 232 smb_vop_other_opens(vnode_t *vp, int mode) 233 { 234 return (((mode & FWRITE) && vn_has_other_opens(vp, V_WRITE)) || 235 (((mode & FWRITE) == 0) && vn_is_opened(vp, V_WRITE)) || 236 ((mode & FREAD) && vn_has_other_opens(vp, V_READ)) || 237 (((mode & FREAD) == 0) && vn_is_opened(vp, V_READ)) || 238 vn_is_mapped(vp, V_RDORWR)); 239 } 240 241 /* 242 * The smb_vop_* functions have minimal knowledge of CIFS semantics and 243 * serve as an interface to the VFS layer. 244 * 245 * Only smb_fsop_* layer functions should call smb_vop_* layer functions. 246 * (Higher-level CIFS service code should never skip the smb_fsop_* layer 247 * to call smb_vop_* layer functions directly.) 248 */ 249 250 /* 251 * XXX - Extended attributes support in the file system assumed. 252 * This is needed for full NT Streams functionality. 253 */ 254 255 int 256 smb_vop_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr) 257 { 258 int error; 259 260 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &smb_ct); 261 error = VOP_READ(vp, uiop, ioflag, cr, &smb_ct); 262 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &smb_ct); 263 return (error); 264 } 265 266 int 267 smb_vop_write(vnode_t *vp, uio_t *uiop, int ioflag, uint32_t *lcount, 268 cred_t *cr) 269 { 270 int error; 271 272 *lcount = uiop->uio_resid; 273 274 uiop->uio_llimit = MAXOFFSET_T; 275 276 (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, &smb_ct); 277 error = VOP_WRITE(vp, uiop, ioflag, cr, &smb_ct); 278 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, &smb_ct); 279 280 *lcount -= uiop->uio_resid; 281 282 return (error); 283 } 284 285 int 286 smb_vop_ioctl(vnode_t *vp, int cmd, void *arg, cred_t *cr) 287 { 288 int error, rval = 0; 289 uint_t flags = 0; 290 291 #ifdef FKIOCTL 292 flags |= FKIOCTL; 293 #endif 294 error = VOP_IOCTL(vp, cmd, (intptr_t)arg, (int)flags, cr, 295 &rval, &smb_ct); 296 if (error != 0) 297 rval = error; 298 299 return (rval); 300 } 301 302 /* 303 * Support for zero-copy read/write 304 * Request buffers and return them. 305 */ 306 int 307 smb_vop_reqzcbuf(vnode_t *vp, int ioflag, xuio_t *xuio, cred_t *cr) 308 { 309 int error; 310 311 error = VOP_REQZCBUF(vp, ioflag, xuio, cr, &smb_ct); 312 return (error); 313 } 314 315 int 316 smb_vop_retzcbuf(vnode_t *vp, xuio_t *xuio, cred_t *cr) 317 { 318 int error; 319 320 error = VOP_RETZCBUF(vp, xuio, cr, &smb_ct); 321 return (error); 322 } 323 324 325 /* 326 * smb_vop_getattr() 327 * 328 * smb_fsop_getattr()/smb_vop_getattr() should always be called from the CIFS 329 * service (instead of calling VOP_GETATTR directly) to retrieve attributes 330 * due to special processing needed for streams files. 331 * 332 * All attributes are retrieved. 333 * 334 * When vp denotes a named stream, then unnamed_vp should be passed in (denoting 335 * the corresponding unnamed stream). 336 * A named stream's attributes (as far as CIFS is concerned) are those of the 337 * unnamed stream (minus the size attribute, and the type), plus the size of 338 * the named stream, and a type value of VREG. 339 * Although the file system may store other attributes with the named stream, 340 * these should not be used by CIFS for any purpose. 341 * 342 * File systems without VFSFT_XVATTR do not support DOS attributes or create 343 * time (crtime). In this case the mtime is used as the crtime. 344 * Likewise if VOP_GETATTR doesn't return any system attributes the dosattr 345 * is 0 and the mtime is used as the crtime. 346 */ 347 int 348 smb_vop_getattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *ret_attr, 349 int flags, cred_t *cr) 350 { 351 int error; 352 vnode_t *use_vp; 353 xvattr_t tmp_xvattr; 354 xoptattr_t *xoap = NULL; 355 356 if (unnamed_vp) 357 use_vp = unnamed_vp; 358 else 359 use_vp = vp; 360 361 if (vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) { 362 xva_init(&tmp_xvattr); 363 xoap = xva_getxoptattr(&tmp_xvattr); 364 ASSERT(xoap); 365 366 smb_sa_to_va_mask_get(ret_attr->sa_mask, 367 &tmp_xvattr.xva_vattr.va_mask); 368 369 XVA_SET_REQ(&tmp_xvattr, XAT_READONLY); 370 XVA_SET_REQ(&tmp_xvattr, XAT_HIDDEN); 371 XVA_SET_REQ(&tmp_xvattr, XAT_SYSTEM); 372 XVA_SET_REQ(&tmp_xvattr, XAT_ARCHIVE); 373 XVA_SET_REQ(&tmp_xvattr, XAT_CREATETIME); 374 XVA_SET_REQ(&tmp_xvattr, XAT_REPARSE); 375 XVA_SET_REQ(&tmp_xvattr, XAT_OFFLINE); 376 XVA_SET_REQ(&tmp_xvattr, XAT_SPARSE); 377 378 error = VOP_GETATTR(use_vp, &tmp_xvattr.xva_vattr, flags, 379 cr, &smb_ct); 380 if (error != 0) 381 return (error); 382 383 ret_attr->sa_vattr = tmp_xvattr.xva_vattr; 384 ret_attr->sa_dosattr = 0; 385 386 if (tmp_xvattr.xva_vattr.va_mask & AT_XVATTR) { 387 xoap = xva_getxoptattr(&tmp_xvattr); 388 ASSERT(xoap); 389 390 if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_READONLY)) && 391 (xoap->xoa_readonly)) { 392 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_READONLY; 393 } 394 395 if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_HIDDEN)) && 396 (xoap->xoa_hidden)) { 397 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_HIDDEN; 398 } 399 400 if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_SYSTEM)) && 401 (xoap->xoa_system)) { 402 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_SYSTEM; 403 } 404 405 if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_ARCHIVE)) && 406 (xoap->xoa_archive)) { 407 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_ARCHIVE; 408 } 409 410 if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_REPARSE)) && 411 (xoap->xoa_reparse)) { 412 ret_attr->sa_dosattr |= 413 FILE_ATTRIBUTE_REPARSE_POINT; 414 } 415 416 if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_OFFLINE)) && 417 (xoap->xoa_offline)) { 418 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_OFFLINE; 419 } 420 421 if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_SPARSE)) && 422 (xoap->xoa_sparse)) { 423 ret_attr->sa_dosattr |= 424 FILE_ATTRIBUTE_SPARSE_FILE; 425 } 426 427 ret_attr->sa_crtime = xoap->xoa_createtime; 428 } else { 429 ret_attr->sa_crtime = ret_attr->sa_vattr.va_mtime; 430 } 431 } else { 432 /* 433 * Support for file systems without VFSFT_XVATTR 434 */ 435 smb_sa_to_va_mask_get(ret_attr->sa_mask, 436 &ret_attr->sa_vattr.va_mask); 437 438 error = VOP_GETATTR(use_vp, &ret_attr->sa_vattr, 439 flags, cr, &smb_ct); 440 if (error != 0) 441 return (error); 442 443 ret_attr->sa_dosattr = 0; 444 ret_attr->sa_crtime = ret_attr->sa_vattr.va_mtime; 445 } 446 447 if (unnamed_vp) { 448 /* 449 * vp is a named stream under "unnamed_vp" 450 * Need to get the size from vp (not use_vp) 451 */ 452 smb_attr_t tmp_attr; 453 ret_attr->sa_vattr.va_type = VREG; 454 455 if (ret_attr->sa_mask & 456 (SMB_AT_SIZE | SMB_AT_NBLOCKS | SMB_AT_ALLOCSZ)) { 457 tmp_attr.sa_vattr.va_mask = AT_SIZE | AT_NBLOCKS; 458 459 error = VOP_GETATTR(vp, &tmp_attr.sa_vattr, 460 flags, cr, &smb_ct); 461 if (error != 0) 462 return (error); 463 464 ret_attr->sa_vattr.va_size = tmp_attr.sa_vattr.va_size; 465 ret_attr->sa_vattr.va_nblocks = 466 tmp_attr.sa_vattr.va_nblocks; 467 } 468 } 469 470 /* 471 * Override a few things so they're as SMB expects. 472 * SMB allocsz is always zero for directories. 473 * For plain files, allocsz is the larger of: 474 * size, allocsize (See smb_node_getattr) 475 */ 476 if (ret_attr->sa_vattr.va_type == VDIR) { 477 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_DIRECTORY; 478 /* SMB expectes directories to have... */ 479 ret_attr->sa_vattr.va_nlink = 1; 480 ret_attr->sa_vattr.va_size = 0; 481 ret_attr->sa_allocsz = 0; 482 } else { 483 if (ret_attr->sa_dosattr == 0) 484 ret_attr->sa_dosattr = FILE_ATTRIBUTE_NORMAL; 485 if ((ret_attr->sa_mask & SMB_AT_ALLOCSZ) != 0) { 486 /* 487 * ZFS includes meta-data in va_nblocks. 488 * Special case zero to keep tests happy. 489 */ 490 if (ret_attr->sa_vattr.va_size == 0 && 491 ret_attr->sa_vattr.va_nblocks == 1) 492 ret_attr->sa_allocsz = 0; 493 else 494 ret_attr->sa_allocsz = 495 ret_attr->sa_vattr.va_nblocks * DEV_BSIZE; 496 if (ret_attr->sa_allocsz < ret_attr->sa_vattr.va_size) { 497 ret_attr->sa_allocsz = P2ROUNDUP( 498 ret_attr->sa_vattr.va_size, DEV_BSIZE); 499 } 500 } 501 } 502 503 return (error); 504 } 505 506 /* 507 * smb_vop_setattr() 508 * 509 * smb_fsop_setattr()/smb_vop_setattr() should always be used instead of 510 * VOP_SETATTR() when calling from the CIFS service, due to special processing 511 * for streams files. 512 * 513 * Streams have a size but otherwise do not have separate attributes from 514 * the (unnamed stream) file, i.e., the security and ownership of the file 515 * applies to the stream. In contrast, extended attribute files, which are 516 * used to implement streams, are independent objects with their own 517 * attributes. 518 * 519 * For compatibility with streams, we set the size on the extended attribute 520 * file and apply other attributes to the (unnamed stream) file. The one 521 * exception is that the UID and GID can be set on the stream by passing a 522 * NULL unnamed_vp, which allows callers to synchronize stream ownership 523 * with the (unnamed stream) file. 524 */ 525 int 526 smb_vop_setattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *attr, 527 int flags, cred_t *cr) 528 { 529 int error = 0; 530 int at_size = 0; 531 vnode_t *use_vp; 532 xvattr_t xvattr; 533 vattr_t *vap; 534 535 if (attr->sa_mask & SMB_AT_DOSATTR) { 536 attr->sa_dosattr &= 537 (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_READONLY | 538 FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | 539 FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_SPARSE_FILE); 540 } 541 542 if (unnamed_vp) { 543 use_vp = unnamed_vp; 544 if (attr->sa_mask & SMB_AT_SIZE) { 545 at_size = 1; 546 attr->sa_mask &= ~SMB_AT_SIZE; 547 } 548 } else { 549 use_vp = vp; 550 } 551 552 /* 553 * The caller should not be setting sa_vattr.va_mask, 554 * but rather sa_mask. 555 */ 556 557 attr->sa_vattr.va_mask = 0; 558 559 if (vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) { 560 smb_vop_setup_xvattr(attr, &xvattr); 561 vap = &xvattr.xva_vattr; 562 } else { 563 smb_sa_to_va_mask(attr->sa_mask, 564 &attr->sa_vattr.va_mask); 565 vap = &attr->sa_vattr; 566 } 567 568 if ((error = VOP_SETATTR(use_vp, vap, flags, cr, &smb_ct)) != 0) 569 return (error); 570 571 if (at_size) { 572 attr->sa_vattr.va_mask = AT_SIZE; 573 error = VOP_SETATTR(vp, &attr->sa_vattr, flags, 574 zone_kcred(), &smb_ct); 575 } 576 577 return (error); 578 } 579 580 int 581 smb_vop_space(vnode_t *vp, int cmd, flock64_t *bfp, int flags, 582 offset_t offset, cred_t *cr) 583 { 584 int error; 585 586 error = VOP_SPACE(vp, cmd, bfp, flags, offset, cr, &smb_ct); 587 588 return (error); 589 } 590 591 /* 592 * smb_vop_access 593 * 594 * This is a wrapper round VOP_ACCESS. VOP_ACCESS checks the given mode 595 * against file's ACL or Unix permissions. CIFS on the other hand needs to 596 * know if the requested operation can succeed for the given object, this 597 * requires more checks in case of DELETE bit since permissions on the parent 598 * directory are important as well. Based on Windows rules if parent's ACL 599 * grant FILE_DELETE_CHILD a file can be delete regardless of the file's 600 * permissions. 601 */ 602 int 603 smb_vop_access(vnode_t *vp, int mode, int flags, vnode_t *dir_vp, cred_t *cr) 604 { 605 int error = 0; 606 607 if (mode == 0) 608 return (0); 609 610 error = VOP_ACCESS(vp, mode, flags, cr, NULL); 611 612 if (error == 0) 613 return (0); 614 615 if ((mode & (ACE_DELETE|ACE_READ_ATTRIBUTES)) == 0 || 616 flags != V_ACE_MASK || dir_vp == NULL) 617 return (error); 618 619 if ((mode & ACE_DELETE) != 0) { 620 error = VOP_ACCESS(dir_vp, ACE_DELETE_CHILD, flags, 621 cr, NULL); 622 623 if (error == 0) 624 mode &= ~ACE_DELETE; 625 } 626 if ((mode & ACE_READ_ATTRIBUTES) != 0) { 627 error = VOP_ACCESS(dir_vp, ACE_LIST_DIRECTORY, flags, 628 cr, NULL); 629 630 if (error == 0) 631 mode &= ~ACE_READ_ATTRIBUTES; 632 } 633 634 if (mode != 0) 635 error = VOP_ACCESS(vp, mode, flags, cr, NULL); 636 637 return (error); 638 } 639 640 /* 641 * smb_vop_lookup 642 * 643 * dvp: directory vnode (in) 644 * name: name of file to be looked up (in) 645 * vpp: looked-up vnode (out) 646 * od_name: on-disk name of file (out). 647 * This parameter is optional. If a pointer is passed in, it 648 * must be allocated with MAXNAMELEN bytes 649 * rootvp: vnode of the tree root (in) 650 * This parameter is always passed in non-NULL except at the time 651 * of share set up. 652 * direntflags: dirent flags returned from VOP_LOOKUP 653 */ 654 int 655 smb_vop_lookup( 656 vnode_t *dvp, 657 char *name, 658 vnode_t **vpp, 659 char *od_name, 660 int flags, 661 int *direntflags, 662 vnode_t *rootvp, 663 smb_attr_t *attr, 664 cred_t *cr) 665 { 666 int error = 0; 667 int option_flags = 0; 668 pathname_t rpn; 669 char *np = name; 670 char namebuf[MAXNAMELEN]; 671 672 if (*name == '\0') { 673 /* 674 * This happens creating named streams at the share root. 675 */ 676 VN_HOLD(dvp); 677 *vpp = dvp; 678 return (0); 679 } 680 681 ASSERT(vpp); 682 *vpp = NULL; 683 *direntflags = 0; 684 685 if ((name[0] == '.') && (name[1] == '.') && (name[2] == 0)) { 686 if (rootvp && (dvp == rootvp)) { 687 VN_HOLD(dvp); 688 *vpp = dvp; 689 return (0); 690 } 691 692 if (dvp->v_flag & VROOT) { 693 vfs_t *vfsp; 694 vnode_t *cvp = dvp; 695 696 /* 697 * Set dvp and check for races with forced unmount 698 * (see lookuppnvp()) 699 */ 700 701 vfsp = cvp->v_vfsp; 702 vfs_rlock_wait(vfsp); 703 if (((dvp = cvp->v_vfsp->vfs_vnodecovered) == NULL) || 704 (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) { 705 vfs_unlock(vfsp); 706 return (EIO); 707 } 708 vfs_unlock(vfsp); 709 } 710 } 711 712 if (flags & SMB_IGNORE_CASE) 713 option_flags = FIGNORECASE; 714 715 if (flags & SMB_CATIA) 716 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf)); 717 718 #ifdef _KERNEL 719 /* 720 * The SMB server enables BYPASS_TRAVERSE_CHECKING by default. 721 * This grants PRIV_FILE_DAC_SEARCH to all users. 722 * If the user has this privilege, we'll always succeed ACE_EXECUTE 723 * checks on directories, so skip the (potentially expensive) 724 * ACL check. 725 */ 726 if (PRIV_POLICY_ONLY(cr, PRIV_FILE_DAC_SEARCH, B_FALSE)) 727 option_flags |= ATTR_NOACLCHECK; 728 #endif 729 pn_alloc(&rpn); 730 731 /* 732 * Easier to not have junk in rpn, as not every FS type 733 * will necessarily fill that in for us. 734 */ 735 bzero(rpn.pn_buf, rpn.pn_bufsize); 736 737 error = VOP_LOOKUP(dvp, np, vpp, NULL, option_flags, NULL, cr, 738 &smb_ct, direntflags, &rpn); 739 740 if (error == 0) { 741 if (od_name) { 742 bzero(od_name, MAXNAMELEN); 743 if ((option_flags & FIGNORECASE) != 0 && 744 rpn.pn_buf[0] != '\0') 745 np = rpn.pn_buf; 746 else 747 np = name; 748 if (flags & SMB_CATIA) 749 smb_vop_catia_v4tov5(np, od_name, MAXNAMELEN); 750 else 751 (void) strlcpy(od_name, np, MAXNAMELEN); 752 } 753 754 if (attr != NULL) { 755 attr->sa_mask = SMB_AT_ALL; 756 (void) smb_vop_getattr(*vpp, NULL, attr, 0, 757 zone_kcred()); 758 } 759 } 760 761 pn_free(&rpn); 762 return (error); 763 } 764 765 int 766 smb_vop_create(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp, 767 int flags, cred_t *cr, vsecattr_t *vsap) 768 { 769 int error; 770 int option_flags = 0; 771 xvattr_t xvattr; 772 vattr_t *vap; 773 char *np = name; 774 char namebuf[MAXNAMELEN]; 775 776 if (flags & SMB_IGNORE_CASE) 777 option_flags = FIGNORECASE; 778 779 attr->sa_vattr.va_mask = 0; 780 781 if (vfs_has_feature(dvp->v_vfsp, VFSFT_XVATTR)) { 782 smb_vop_setup_xvattr(attr, &xvattr); 783 vap = &xvattr.xva_vattr; 784 } else { 785 smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask); 786 vap = &attr->sa_vattr; 787 } 788 789 if (flags & SMB_CATIA) { 790 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf)); 791 if (strchr(np, '/') != NULL) 792 return (EILSEQ); 793 } 794 795 error = VOP_CREATE(dvp, np, vap, EXCL, attr->sa_vattr.va_mode, 796 vpp, cr, option_flags, &smb_ct, vsap); 797 798 /* 799 * One could argue that filesystems should obey the size 800 * if specified in the create attributes. Unfortunately, 801 * they only appear to let you truncate the size to zero. 802 * SMB needs to set a non-zero size, so work-around. 803 */ 804 if (error == 0 && *vpp != NULL && 805 (vap->va_mask & AT_SIZE) != 0 && 806 vap->va_size > 0) { 807 vattr_t ta = *vap; 808 ta.va_mask = AT_SIZE; 809 (void) VOP_SETATTR(*vpp, &ta, 0, cr, &smb_ct); 810 } 811 812 return (error); 813 } 814 815 int 816 smb_vop_remove(vnode_t *dvp, char *name, int flags, cred_t *cr) 817 { 818 int error; 819 int option_flags = 0; 820 char *np = name; 821 char namebuf[MAXNAMELEN]; 822 823 if (flags & SMB_IGNORE_CASE) 824 option_flags = FIGNORECASE; 825 826 if (flags & SMB_CATIA) 827 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf)); 828 829 error = VOP_REMOVE(dvp, np, cr, &smb_ct, option_flags); 830 831 return (error); 832 } 833 834 /* 835 * smb_vop_link(target-dir-vp, source-file-vp, target-name) 836 * 837 * Create a link - same tree (identical TID) only. 838 */ 839 int 840 smb_vop_link(vnode_t *to_dvp, vnode_t *from_vp, char *to_name, 841 int flags, cred_t *cr) 842 { 843 int option_flags = 0; 844 char *np, *buf; 845 int rc; 846 847 if (flags & SMB_IGNORE_CASE) 848 option_flags = FIGNORECASE; 849 850 if (flags & SMB_CATIA) { 851 buf = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 852 np = smb_vop_catia_v5tov4(to_name, buf, MAXNAMELEN); 853 if (strchr(np, '/') != NULL) { 854 kmem_free(buf, MAXNAMELEN); 855 return (EILSEQ); 856 } 857 858 rc = VOP_LINK(to_dvp, from_vp, np, cr, &smb_ct, option_flags); 859 kmem_free(buf, MAXNAMELEN); 860 return (rc); 861 } 862 863 rc = VOP_LINK(to_dvp, from_vp, to_name, cr, &smb_ct, option_flags); 864 return (rc); 865 } 866 867 /* 868 * smb_vop_rename() 869 * 870 * The rename is for files in the same tree (identical TID) only. 871 */ 872 int 873 smb_vop_rename(vnode_t *from_dvp, char *from_name, vnode_t *to_dvp, 874 char *to_name, int flags, cred_t *cr) 875 { 876 int error; 877 int option_flags = 0; 878 char *from, *to, *fbuf, *tbuf; 879 880 if (flags & SMB_IGNORE_CASE) 881 option_flags = FIGNORECASE; 882 883 if (flags & SMB_CATIA) { 884 tbuf = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 885 to = smb_vop_catia_v5tov4(to_name, tbuf, MAXNAMELEN); 886 if (strchr(to, '/') != NULL) { 887 kmem_free(tbuf, MAXNAMELEN); 888 return (EILSEQ); 889 } 890 891 fbuf = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 892 from = smb_vop_catia_v5tov4(from_name, fbuf, MAXNAMELEN); 893 894 error = VOP_RENAME(from_dvp, from, to_dvp, to, cr, 895 &smb_ct, option_flags); 896 897 kmem_free(tbuf, MAXNAMELEN); 898 kmem_free(fbuf, MAXNAMELEN); 899 return (error); 900 } 901 902 error = VOP_RENAME(from_dvp, from_name, to_dvp, to_name, cr, 903 &smb_ct, option_flags); 904 905 return (error); 906 } 907 908 int 909 smb_vop_mkdir(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp, 910 int flags, cred_t *cr, vsecattr_t *vsap) 911 { 912 int error; 913 int option_flags = 0; 914 xvattr_t xvattr; 915 vattr_t *vap; 916 char *np = name; 917 char namebuf[MAXNAMELEN]; 918 919 if (flags & SMB_IGNORE_CASE) 920 option_flags = FIGNORECASE; 921 922 attr->sa_vattr.va_mask = 0; 923 924 if (vfs_has_feature(dvp->v_vfsp, VFSFT_XVATTR)) { 925 smb_vop_setup_xvattr(attr, &xvattr); 926 vap = &xvattr.xva_vattr; 927 } else { 928 smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask); 929 vap = &attr->sa_vattr; 930 } 931 932 if (flags & SMB_CATIA) { 933 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf)); 934 if (strchr(np, '/') != NULL) 935 return (EILSEQ); 936 } 937 938 error = VOP_MKDIR(dvp, np, vap, vpp, cr, &smb_ct, option_flags, vsap); 939 940 return (error); 941 } 942 943 /* 944 * smb_vop_rmdir() 945 * 946 * Only simple rmdir supported, consistent with NT semantics 947 * (can only remove an empty directory). 948 * 949 * The third argument to VOP_RMDIR is the current directory of 950 * the process. It allows rmdir wants to EINVAL if one tries to 951 * remove ".". Since SMB servers do not know what their clients' 952 * current directories are, we fake it by supplying a vnode known 953 * to exist and illegal to remove (rootdir). 954 */ 955 int 956 smb_vop_rmdir(vnode_t *dvp, char *name, int flags, cred_t *cr) 957 { 958 int error; 959 int option_flags = 0; 960 char *np = name; 961 char namebuf[MAXNAMELEN]; 962 963 if (flags & SMB_IGNORE_CASE) 964 option_flags = FIGNORECASE; 965 966 if (flags & SMB_CATIA) 967 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf)); 968 969 error = VOP_RMDIR(dvp, np, rootdir, cr, &smb_ct, option_flags); 970 return (error); 971 } 972 973 int 974 smb_vop_commit(vnode_t *vp, cred_t *cr) 975 { 976 return (VOP_FSYNC(vp, 1, cr, &smb_ct)); 977 } 978 979 /* 980 * Some code in smb_node.c needs to know which DOS attributes 981 * we can actually store. Let's define a mask here of all the 982 * DOS attribute flags supported by the following function. 983 */ 984 const uint32_t 985 smb_vop_dosattr_settable = 986 FILE_ATTRIBUTE_ARCHIVE | 987 FILE_ATTRIBUTE_SYSTEM | 988 FILE_ATTRIBUTE_HIDDEN | 989 FILE_ATTRIBUTE_READONLY | 990 FILE_ATTRIBUTE_OFFLINE | 991 FILE_ATTRIBUTE_SPARSE_FILE; 992 993 static void 994 smb_vop_setup_xvattr(smb_attr_t *smb_attr, xvattr_t *xvattr) 995 { 996 xoptattr_t *xoap = NULL; 997 uint_t xva_mask; 998 999 /* 1000 * Initialize xvattr, including bzero 1001 */ 1002 xva_init(xvattr); 1003 xoap = xva_getxoptattr(xvattr); 1004 1005 ASSERT(xoap); 1006 1007 /* 1008 * Copy caller-specified classic attributes to xvattr. 1009 * First save xvattr's mask (set in xva_init()), which 1010 * contains AT_XVATTR. This is |'d in later if needed. 1011 */ 1012 1013 xva_mask = xvattr->xva_vattr.va_mask; 1014 xvattr->xva_vattr = smb_attr->sa_vattr; 1015 1016 smb_sa_to_va_mask(smb_attr->sa_mask, &xvattr->xva_vattr.va_mask); 1017 1018 /* 1019 * Do not set ctime (only the file system can do it) 1020 */ 1021 1022 xvattr->xva_vattr.va_mask &= ~AT_CTIME; 1023 1024 if (smb_attr->sa_mask & SMB_AT_DOSATTR) { 1025 1026 /* 1027 * "|" in the original xva_mask, which contains 1028 * AT_XVATTR 1029 */ 1030 1031 xvattr->xva_vattr.va_mask |= xva_mask; 1032 1033 XVA_SET_REQ(xvattr, XAT_ARCHIVE); 1034 XVA_SET_REQ(xvattr, XAT_SYSTEM); 1035 XVA_SET_REQ(xvattr, XAT_READONLY); 1036 XVA_SET_REQ(xvattr, XAT_HIDDEN); 1037 XVA_SET_REQ(xvattr, XAT_OFFLINE); 1038 XVA_SET_REQ(xvattr, XAT_SPARSE); 1039 1040 /* 1041 * smb_attr->sa_dosattr: If a given bit is not set, 1042 * that indicates that the corresponding field needs 1043 * to be updated with a "0" value. This is done 1044 * implicitly as the xoap->xoa_* fields were bzero'd. 1045 */ 1046 1047 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_ARCHIVE) 1048 xoap->xoa_archive = 1; 1049 1050 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_SYSTEM) 1051 xoap->xoa_system = 1; 1052 1053 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_READONLY) 1054 xoap->xoa_readonly = 1; 1055 1056 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_HIDDEN) 1057 xoap->xoa_hidden = 1; 1058 1059 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_OFFLINE) 1060 xoap->xoa_offline = 1; 1061 1062 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE) 1063 xoap->xoa_sparse = 1; 1064 } 1065 1066 if (smb_attr->sa_mask & SMB_AT_CRTIME) { 1067 /* 1068 * "|" in the original xva_mask, which contains 1069 * AT_XVATTR 1070 */ 1071 1072 xvattr->xva_vattr.va_mask |= xva_mask; 1073 XVA_SET_REQ(xvattr, XAT_CREATETIME); 1074 xoap->xoa_createtime = smb_attr->sa_crtime; 1075 } 1076 } 1077 1078 /* 1079 * smb_vop_readdir() 1080 * 1081 * Collects an SMB_MINLEN_RDDIR_BUF "page" of directory entries. 1082 * The directory entries are returned in an fs-independent format by the 1083 * underlying file system. That is, the "page" of information returned is 1084 * not literally stored on-disk in the format returned. 1085 * If the file system supports extended directory entries (has features 1086 * VFSFT_DIRENTFLAGS), set V_RDDIR_ENTFLAGS to cause the buffer to be 1087 * filled with edirent_t structures, instead of dirent64_t structures. 1088 * If the file system supports access based enumeration (abe), set 1089 * V_RDDIR_ACCFILTER to filter directory entries based on user cred. 1090 */ 1091 int 1092 smb_vop_readdir(vnode_t *vp, uint32_t offset, 1093 void *buf, int *count, int *eof, uint32_t rddir_flag, cred_t *cr) 1094 { 1095 int error = 0; 1096 int flags = 0; 1097 int rdirent_size; 1098 struct uio auio; 1099 struct iovec aiov; 1100 1101 if (vp->v_type != VDIR) 1102 return (ENOTDIR); 1103 1104 if ((rddir_flag & SMB_EDIRENT) != 0 && 1105 vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS)) { 1106 flags |= V_RDDIR_ENTFLAGS; 1107 rdirent_size = sizeof (edirent_t); 1108 } else { 1109 rdirent_size = sizeof (dirent64_t); 1110 } 1111 1112 if (*count < rdirent_size) 1113 return (EINVAL); 1114 1115 if (rddir_flag & SMB_ABE) 1116 flags |= V_RDDIR_ACCFILTER; 1117 1118 aiov.iov_base = buf; 1119 aiov.iov_len = *count; 1120 auio.uio_iov = &aiov; 1121 auio.uio_iovcnt = 1; 1122 auio.uio_loffset = (uint64_t)offset; 1123 auio.uio_segflg = UIO_SYSSPACE; 1124 auio.uio_extflg = UIO_COPY_DEFAULT; 1125 auio.uio_resid = *count; 1126 auio.uio_fmode = 0; 1127 1128 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &smb_ct); 1129 error = VOP_READDIR(vp, &auio, cr, eof, &smb_ct, flags); 1130 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &smb_ct); 1131 1132 if (error == 0) 1133 *count = *count - auio.uio_resid; 1134 1135 return (error); 1136 } 1137 1138 /* 1139 * smb_sa_to_va_mask 1140 * 1141 * Set va_mask by running through the SMB_AT_* #define's and 1142 * setting those bits that correspond to the SMB_AT_* bits 1143 * set in sa_mask. 1144 */ 1145 void 1146 smb_sa_to_va_mask(uint_t sa_mask, uint_t *va_maskp) 1147 { 1148 int i; 1149 uint_t smask; 1150 1151 smask = (sa_mask); 1152 for (i = SMB_AT_TYPE; (i < SMB_AT_MAX) && (smask != 0); ++i) { 1153 if (smask & 1) 1154 *(va_maskp) |= smb_attrmap[i]; 1155 1156 smask >>= 1; 1157 } 1158 } 1159 1160 /* 1161 * Variant of smb_sa_to_va_mask for vop_getattr, 1162 * adding some bits for SMB_AT_ALLOCSZ etc. 1163 */ 1164 void 1165 smb_sa_to_va_mask_get(uint_t sa_mask, uint_t *va_maskp) 1166 { 1167 smb_sa_to_va_mask(sa_mask, va_maskp); 1168 1169 *va_maskp |= AT_TYPE; 1170 if ((sa_mask & SMB_AT_ALLOCSZ) != 0) { 1171 *va_maskp |= (AT_SIZE | AT_NBLOCKS); 1172 } 1173 } 1174 1175 /* 1176 * smb_vop_stream_lookup() 1177 * 1178 * The name returned in od_name is the on-disk name of the stream with the 1179 * SMB_STREAM_PREFIX stripped off. od_name should be allocated to MAXNAMELEN 1180 * by the caller. 1181 */ 1182 int 1183 smb_vop_stream_lookup( 1184 vnode_t *fvp, 1185 char *stream_name, 1186 vnode_t **vpp, 1187 char *od_name, 1188 vnode_t **xattrdirvpp, 1189 int flags, 1190 vnode_t *rootvp, 1191 cred_t *cr) 1192 { 1193 char *solaris_stream_name; 1194 char *name; 1195 int error, tmpflgs; 1196 1197 if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp, 1198 LOOKUP_XATTR | CREATE_XATTR_DIR, cr)) != 0) 1199 return (error); 1200 1201 /* 1202 * Prepend SMB_STREAM_PREFIX to stream name 1203 */ 1204 1205 solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 1206 (void) snprintf(solaris_stream_name, MAXNAMELEN, 1207 "%s%s", SMB_STREAM_PREFIX, stream_name); 1208 1209 /* 1210 * "name" will hold the on-disk name returned from smb_vop_lookup 1211 * for the stream, including the SMB_STREAM_PREFIX. 1212 */ 1213 1214 name = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 1215 1216 if ((error = smb_vop_lookup(*xattrdirvpp, solaris_stream_name, vpp, 1217 name, flags, &tmpflgs, rootvp, NULL, cr)) != 0) { 1218 VN_RELE(*xattrdirvpp); 1219 } else { 1220 (void) strlcpy(od_name, &(name[SMB_STREAM_PREFIX_LEN]), 1221 MAXNAMELEN); 1222 } 1223 1224 kmem_free(solaris_stream_name, MAXNAMELEN); 1225 kmem_free(name, MAXNAMELEN); 1226 1227 return (error); 1228 } 1229 1230 int 1231 smb_vop_stream_create(vnode_t *fvp, char *stream_name, smb_attr_t *attr, 1232 vnode_t **vpp, vnode_t **xattrdirvpp, int flags, cred_t *cr) 1233 { 1234 char *solaris_stream_name; 1235 int error; 1236 1237 if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp, 1238 LOOKUP_XATTR | CREATE_XATTR_DIR, cr)) != 0) 1239 return (error); 1240 1241 /* 1242 * Prepend SMB_STREAM_PREFIX to stream name 1243 */ 1244 1245 solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 1246 (void) snprintf(solaris_stream_name, MAXNAMELEN, 1247 "%s%s", SMB_STREAM_PREFIX, stream_name); 1248 1249 if ((error = smb_vop_create(*xattrdirvpp, solaris_stream_name, attr, 1250 vpp, flags, cr, NULL)) != 0) 1251 VN_RELE(*xattrdirvpp); 1252 1253 kmem_free(solaris_stream_name, MAXNAMELEN); 1254 1255 return (error); 1256 } 1257 1258 int 1259 smb_vop_stream_remove(vnode_t *vp, char *stream_name, int flags, cred_t *cr) 1260 { 1261 char *solaris_stream_name; 1262 vnode_t *xattrdirvp; 1263 int error; 1264 1265 error = smb_vop_lookup_xattrdir(vp, &xattrdirvp, LOOKUP_XATTR, cr); 1266 if (error != 0) 1267 return (error); 1268 1269 /* 1270 * Prepend SMB_STREAM_PREFIX to stream name 1271 */ 1272 1273 solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 1274 (void) snprintf(solaris_stream_name, MAXNAMELEN, 1275 "%s%s", SMB_STREAM_PREFIX, stream_name); 1276 1277 /* XXX might have to use kcred */ 1278 error = smb_vop_remove(xattrdirvp, solaris_stream_name, flags, cr); 1279 1280 kmem_free(solaris_stream_name, MAXNAMELEN); 1281 VN_RELE(xattrdirvp); 1282 1283 return (error); 1284 } 1285 1286 int 1287 smb_vop_lookup_xattrdir(vnode_t *fvp, vnode_t **xattrdirvpp, int flags, 1288 cred_t *cr) 1289 { 1290 int error; 1291 1292 error = VOP_LOOKUP(fvp, "", xattrdirvpp, NULL, flags, NULL, cr, 1293 &smb_ct, NULL, NULL); 1294 return (error); 1295 } 1296 1297 /* 1298 * smb_vop_traverse_check() 1299 * 1300 * This function checks to see if the passed-in vnode has a file system 1301 * mounted on it. If it does, the mount point is "traversed" and the 1302 * vnode for the root of the file system is returned. 1303 */ 1304 int 1305 smb_vop_traverse_check(vnode_t **vpp) 1306 { 1307 int error; 1308 1309 if (vn_mountedvfs(*vpp) == 0) 1310 return (0); 1311 1312 /* 1313 * traverse() may return a different held vnode, even in the error case. 1314 * If it returns a different vnode, it will have released the original. 1315 */ 1316 1317 error = traverse(vpp); 1318 1319 return (error); 1320 } 1321 1322 int /*ARGSUSED*/ 1323 smb_vop_statfs(vnode_t *vp, struct statvfs64 *statp, cred_t *cr) 1324 { 1325 int error; 1326 1327 error = VFS_STATVFS(vp->v_vfsp, statp); 1328 1329 return (error); 1330 } 1331 1332 /* 1333 * smb_vop_acl_read 1334 * 1335 * Reads the ACL of the specified file into 'aclp'. 1336 * acl_type is the type of ACL which the filesystem supports. 1337 * 1338 * Caller has to free the allocated memory for aclp by calling 1339 * acl_free(). 1340 */ 1341 int 1342 smb_vop_acl_read(vnode_t *vp, acl_t **aclp, int flags, acl_type_t acl_type, 1343 cred_t *cr) 1344 { 1345 int error; 1346 vsecattr_t vsecattr; 1347 1348 ASSERT(vp); 1349 ASSERT(aclp); 1350 1351 *aclp = NULL; 1352 bzero(&vsecattr, sizeof (vsecattr_t)); 1353 1354 switch (acl_type) { 1355 case ACLENT_T: 1356 vsecattr.vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL | 1357 VSA_DFACLCNT; 1358 break; 1359 1360 case ACE_T: 1361 vsecattr.vsa_mask = VSA_ACE | VSA_ACECNT | VSA_ACE_ACLFLAGS; 1362 break; 1363 1364 default: 1365 return (EINVAL); 1366 } 1367 1368 if ((error = VOP_GETSECATTR(vp, &vsecattr, flags, cr, &smb_ct)) != 0) 1369 return (error); 1370 1371 *aclp = smb_fsacl_from_vsa(&vsecattr, acl_type); 1372 if (vp->v_type == VDIR) 1373 (*aclp)->acl_flags |= ACL_IS_DIR; 1374 1375 return (0); 1376 } 1377 1378 /* 1379 * smb_vop_acl_write 1380 * 1381 * Writes the given ACL in aclp for the specified file. 1382 */ 1383 int 1384 smb_vop_acl_write(vnode_t *vp, acl_t *aclp, int flags, cred_t *cr) 1385 { 1386 int error; 1387 vsecattr_t vsecattr; 1388 int aclbsize; 1389 1390 ASSERT(vp); 1391 ASSERT(aclp); 1392 1393 error = smb_fsacl_to_vsa(aclp, &vsecattr, &aclbsize); 1394 1395 if (error == 0) { 1396 (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, &smb_ct); 1397 error = VOP_SETSECATTR(vp, &vsecattr, flags, cr, &smb_ct); 1398 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, &smb_ct); 1399 } 1400 1401 if (aclbsize && vsecattr.vsa_aclentp) 1402 kmem_free(vsecattr.vsa_aclentp, aclbsize); 1403 1404 return (error); 1405 } 1406 1407 /* 1408 * smb_vop_acl_type 1409 * 1410 * Determines the ACL type for the given vnode. 1411 * ACLENT_T is a Posix ACL and ACE_T is a ZFS ACL. 1412 */ 1413 acl_type_t 1414 smb_vop_acl_type(vnode_t *vp) 1415 { 1416 int error; 1417 ulong_t whichacl; 1418 1419 error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &whichacl, 1420 zone_kcred(), NULL); 1421 if (error != 0) { 1422 /* 1423 * If we got an error, then the filesystem 1424 * likely does not understand the _PC_ACL_ENABLED 1425 * pathconf. In this case, we fall back to trying 1426 * POSIX-draft (aka UFS-style) ACLs. 1427 */ 1428 whichacl = _ACL_ACLENT_ENABLED; 1429 } 1430 1431 if (!(whichacl & (_ACL_ACE_ENABLED | _ACL_ACLENT_ENABLED))) { 1432 /* 1433 * If the file system supports neither ACE nor 1434 * ACLENT ACLs we will fall back to UFS-style ACLs 1435 * like we did above if there was an error upon 1436 * calling VOP_PATHCONF. 1437 * 1438 * ACE and ACLENT type ACLs are the only interfaces 1439 * supported thus far. If any other bits are set on 1440 * 'whichacl' upon return from VOP_PATHCONF, we will 1441 * ignore them. 1442 */ 1443 whichacl = _ACL_ACLENT_ENABLED; 1444 } 1445 1446 if (whichacl == _ACL_ACLENT_ENABLED) 1447 return (ACLENT_T); 1448 1449 return (ACE_T); 1450 } 1451 1452 static const int zfs_perms[] = { 1453 ACE_READ_DATA, ACE_WRITE_DATA, ACE_APPEND_DATA, ACE_READ_NAMED_ATTRS, 1454 ACE_WRITE_NAMED_ATTRS, ACE_EXECUTE, ACE_DELETE_CHILD, 1455 ACE_READ_ATTRIBUTES, ACE_WRITE_ATTRIBUTES, ACE_DELETE, ACE_READ_ACL, 1456 ACE_WRITE_ACL, ACE_WRITE_OWNER, ACE_SYNCHRONIZE 1457 }; 1458 1459 static const int unix_perms[] = { VREAD, VWRITE, VEXEC }; 1460 /* 1461 * smb_vop_eaccess 1462 * 1463 * Returns the effective permission of the given credential for the 1464 * specified object. 1465 * 1466 * This is just a workaround. We need VFS/FS support for this. 1467 */ 1468 void 1469 smb_vop_eaccess(vnode_t *vp, int *mode, int flags, vnode_t *dir_vp, cred_t *cr) 1470 { 1471 int error, i; 1472 int pnum; 1473 1474 *mode = 0; 1475 1476 if (flags == V_ACE_MASK) { 1477 pnum = sizeof (zfs_perms) / sizeof (int); 1478 1479 for (i = 0; i < pnum; i++) { 1480 error = smb_vop_access(vp, zfs_perms[i], flags, 1481 dir_vp, cr); 1482 if (error == 0) 1483 *mode |= zfs_perms[i]; 1484 } 1485 } else { 1486 pnum = sizeof (unix_perms) / sizeof (int); 1487 1488 for (i = 0; i < pnum; i++) { 1489 error = smb_vop_access(vp, unix_perms[i], flags, 1490 dir_vp, cr); 1491 if (error == 0) 1492 *mode |= unix_perms[i]; 1493 } 1494 } 1495 } 1496 1497 /* 1498 * See comments for smb_fsop_shrlock() 1499 */ 1500 int 1501 smb_vop_shrlock(vnode_t *vp, uint32_t uniq_fid, uint32_t desired_access, 1502 uint32_t share_access, cred_t *cr) 1503 { 1504 struct shrlock shr; 1505 struct shr_locowner shr_own; 1506 short new_access = 0; 1507 short deny = 0; 1508 int flag = 0; 1509 int cmd; 1510 1511 /* 1512 * share locking is not supported for non-regular 1513 * objects in NBMAND mode. 1514 */ 1515 if (nbl_need_check(vp)) { 1516 if (vp->v_type != VREG) 1517 return (0); 1518 1519 cmd = F_SHARE_NBMAND; 1520 } else { 1521 cmd = F_SHARE; 1522 } 1523 1524 if ((desired_access & FILE_DATA_ALL) == 0) { 1525 /* metadata access only */ 1526 new_access |= F_MDACC; 1527 } else { 1528 if (desired_access & (ACE_READ_DATA | ACE_EXECUTE)) { 1529 new_access |= F_RDACC; 1530 flag |= FREAD; 1531 } 1532 1533 if (desired_access & (ACE_WRITE_DATA | ACE_APPEND_DATA | 1534 ACE_ADD_FILE)) { 1535 new_access |= F_WRACC; 1536 flag |= FWRITE; 1537 } 1538 1539 if (SMB_DENY_READ(share_access)) { 1540 deny |= F_RDDNY; 1541 } 1542 1543 if (SMB_DENY_WRITE(share_access)) { 1544 deny |= F_WRDNY; 1545 } 1546 1547 if (cmd == F_SHARE_NBMAND) { 1548 if (desired_access & ACE_DELETE) 1549 new_access |= F_RMACC; 1550 1551 if (SMB_DENY_DELETE(share_access)) { 1552 deny |= F_RMDNY; 1553 } 1554 } 1555 } 1556 1557 shr.s_access = new_access; 1558 shr.s_deny = deny; 1559 shr.s_sysid = smb_ct.cc_sysid; 1560 shr.s_pid = uniq_fid; 1561 shr.s_own_len = sizeof (shr_own); 1562 shr.s_owner = (caddr_t)&shr_own; 1563 shr_own.sl_id = shr.s_sysid; 1564 shr_own.sl_pid = shr.s_pid; 1565 1566 return (VOP_SHRLOCK(vp, cmd, &shr, flag, cr, NULL)); 1567 } 1568 1569 int 1570 smb_vop_unshrlock(vnode_t *vp, uint32_t uniq_fid, cred_t *cr) 1571 { 1572 struct shrlock shr; 1573 struct shr_locowner shr_own; 1574 1575 /* 1576 * share locking is not supported for non-regular 1577 * objects in NBMAND mode. 1578 */ 1579 if (nbl_need_check(vp) && (vp->v_type != VREG)) 1580 return (0); 1581 1582 /* 1583 * For s_access and s_deny, we do not need to pass in the original 1584 * values. 1585 */ 1586 shr.s_access = 0; 1587 shr.s_deny = 0; 1588 shr.s_sysid = smb_ct.cc_sysid; 1589 shr.s_pid = uniq_fid; 1590 shr.s_own_len = sizeof (shr_own); 1591 shr.s_owner = (caddr_t)&shr_own; 1592 shr_own.sl_id = shr.s_sysid; 1593 shr_own.sl_pid = shr.s_pid; 1594 1595 return (VOP_SHRLOCK(vp, F_UNSHARE, &shr, 0, cr, NULL)); 1596 } 1597 1598 /* 1599 * Note about mandatory vs advisory locks: 1600 * 1601 * The SMB server really should always request mandatory locks, and 1602 * if the file system does not support them, the SMB server should 1603 * just tell the client it could not get the lock. If we were to 1604 * tell the SMB client "you got the lock" when what they really 1605 * got was only an advisory lock, we would be lying to the client 1606 * about their having exclusive access to the locked range, which 1607 * could easily lead to data corruption. If someone really wants 1608 * the (dangerous) behavior they can set: smb_allow_advisory_locks 1609 */ 1610 int 1611 smb_vop_frlock(vnode_t *vp, cred_t *cr, int flag, flock64_t *bf) 1612 { 1613 flk_callback_t flk_cb; 1614 int cmd = F_SETLK_NBMAND; 1615 1616 if (smb_allow_advisory_locks != 0 && !nbl_need_check(vp)) { 1617 /* 1618 * The file system does not support nbmand, and 1619 * smb_allow_advisory_locks is enabled. (danger!) 1620 */ 1621 cmd = F_SETLK; 1622 } 1623 1624 flk_init_callback(&flk_cb, smb_lock_frlock_callback, NULL); 1625 1626 return (VOP_FRLOCK(vp, cmd, bf, flag, 0, &flk_cb, cr, &smb_ct)); 1627 } 1628 1629 static callb_cpr_t * 1630 /* ARGSUSED */ 1631 smb_lock_frlock_callback(flk_cb_when_t when, void *error) 1632 { 1633 return (0); 1634 } 1635 1636 /* 1637 * smb_vop_catia_init_v4_lookup 1638 * Initialize mapping between wide characters in the range from 1639 * 0x00A4 to 0x00FF and their UNIX (v4) equivalent (wide character). 1640 * Indexed by the decimal value of the wide character (164-255) 1641 * with an offset of -164. 1642 */ 1643 static void 1644 smb_vop_catia_init_v4_lookup() 1645 { 1646 int i, idx, offset = SMB_CATIA_V4_LOOKUP_LOW; 1647 1648 for (i = 0; i < SMB_CATIA_V4_LOOKUP_MAX; i++) 1649 smb_catia_v4_lookup[i] = (smb_wchar_t)(i + offset); 1650 1651 for (i = 0; i < SMB_CATIA_NUM_MAPS; i++) { 1652 idx = (int)catia_maps[i].winchar - offset; 1653 smb_catia_v4_lookup[idx] = (smb_wchar_t)catia_maps[i].unixchar; 1654 } 1655 } 1656 1657 /* 1658 * smb_vop_catia_init_v5_lookup 1659 * Initialize mapping between UNIX ASCII (v4) characters and equivalent 1660 * or translated wide characters. 1661 * Indexed by the decimal value of the ASCII character (0-127). 1662 */ 1663 static void 1664 smb_vop_catia_init_v5_lookup() 1665 { 1666 int i, idx; 1667 1668 for (i = 0; i < SMB_CATIA_V5_LOOKUP_MAX; i++) 1669 smb_catia_v5_lookup[i] = (smb_wchar_t)i; 1670 1671 for (i = 0; i < SMB_CATIA_NUM_MAPS; i++) { 1672 idx = (int)catia_maps[i].unixchar; 1673 smb_catia_v5_lookup[idx] = catia_maps[i].winchar; 1674 } 1675 } 1676 1677 static void 1678 smb_vop_catia_init() 1679 { 1680 smb_vop_catia_init_v4_lookup(); 1681 smb_vop_catia_init_v5_lookup(); 1682 } 1683 1684 /* 1685 * smb_vop_catia_v5tov4 1686 * (windows (v5) to unix (v4)) 1687 * 1688 * Traverse each character in the given source filename and convert the 1689 * multibyte that is equivalent to any special Windows character listed 1690 * in the catia_maps table to the Unix ASCII character if any is 1691 * encountered in the filename. The translated name is returned in buf. 1692 * 1693 * If an error occurs the conversion terminates and name is returned, 1694 * otherwise buf is returned. 1695 */ 1696 char * 1697 smb_vop_catia_v5tov4(char *name, char *buf, int buflen) 1698 { 1699 int v4_idx, numbytes, inc; 1700 int space_left = buflen - 1; /* one byte reserved for null */ 1701 uint32_t wc; 1702 char mbstring[MTS_MB_CHAR_MAX]; 1703 char *p, *src = name, *dst = buf; 1704 1705 ASSERT(name); 1706 ASSERT(buf); 1707 1708 if (!buf || !name) 1709 return (name); 1710 1711 bzero(buf, buflen); 1712 1713 while (*src) { 1714 if ((numbytes = smb_mbtowc(&wc, src, MTS_MB_CHAR_MAX)) < 0) 1715 return (name); 1716 1717 if (wc < SMB_CATIA_V4_LOOKUP_LOW || 1718 wc > SMB_CATIA_V4_LOOKUP_UPPER) { 1719 inc = numbytes; 1720 p = src; 1721 } else { 1722 /* Lookup required. */ 1723 v4_idx = (int)wc - SMB_CATIA_V4_LOOKUP_LOW; 1724 inc = smb_wctomb(mbstring, smb_catia_v4_lookup[v4_idx]); 1725 p = mbstring; 1726 } 1727 1728 if (space_left < inc) 1729 return (name); 1730 1731 (void) strncpy(dst, p, inc); 1732 dst += inc; 1733 space_left -= inc; 1734 src += numbytes; 1735 } 1736 1737 return (buf); 1738 } 1739 1740 /* 1741 * smb_vop_catia_v4tov5 1742 * (unix (v4) to windows (v5)) 1743 * 1744 * Traverse each character in the given filename 'srcbuf' and convert 1745 * the special Unix character that is listed in the catia_maps table to 1746 * the UTF-8 encoding of the corresponding Windows character if any is 1747 * encountered in the filename. 1748 * 1749 * The translated name is returned in buf. 1750 * If an error occurs the conversion terminates and the original name 1751 * is returned in buf. 1752 */ 1753 void 1754 smb_vop_catia_v4tov5(char *name, char *buf, int buflen) 1755 { 1756 int v5_idx, numbytes; 1757 int space_left = buflen - 1; /* one byte reserved for null */ 1758 uint32_t wc; 1759 char mbstring[MTS_MB_CHAR_MAX]; 1760 char *src = name, *dst = buf; 1761 1762 ASSERT(name); 1763 ASSERT(buf); 1764 1765 if (!buf || !name) 1766 return; 1767 1768 (void) bzero(buf, buflen); 1769 while (*src) { 1770 if (smb_isascii(*src)) { 1771 /* Lookup required */ 1772 v5_idx = (int)*src++; 1773 numbytes = smb_wctomb(mbstring, 1774 smb_catia_v5_lookup[v5_idx]); 1775 if (space_left < numbytes) 1776 break; 1777 (void) strncpy(dst, mbstring, numbytes); 1778 } else { 1779 if ((numbytes = smb_mbtowc(&wc, src, 1780 MTS_MB_CHAR_MAX)) < 0) 1781 break; 1782 if (space_left < numbytes) 1783 break; 1784 (void) strncpy(dst, src, numbytes); 1785 src += numbytes; 1786 } 1787 1788 dst += numbytes; 1789 space_left -= numbytes; 1790 } 1791 1792 if (*src) 1793 (void) strlcpy(buf, name, buflen); 1794 } 1795