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 goto attr_out; 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 goto attr_out; 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 && od_name != NULL) { 741 bzero(od_name, MAXNAMELEN); 742 if ((option_flags & FIGNORECASE) != 0 && 743 rpn.pn_buf[0] != '\0') 744 np = rpn.pn_buf; 745 else 746 np = name; 747 if (flags & SMB_CATIA) 748 smb_vop_catia_v4tov5(np, od_name, MAXNAMELEN); 749 else 750 (void) strlcpy(od_name, np, MAXNAMELEN); 751 } 752 pn_free(&rpn); 753 754 attr_out: 755 if (error == 0 && attr != NULL) { 756 attr->sa_mask = SMB_AT_ALL; 757 (void) smb_vop_getattr(*vpp, NULL, attr, 0, 758 zone_kcred()); 759 } 760 761 return (error); 762 } 763 764 int 765 smb_vop_create(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp, 766 int flags, cred_t *cr, vsecattr_t *vsap) 767 { 768 int error; 769 int option_flags = 0; 770 xvattr_t xvattr; 771 vattr_t *vap; 772 char *np = name; 773 char namebuf[MAXNAMELEN]; 774 775 if (flags & SMB_IGNORE_CASE) 776 option_flags = FIGNORECASE; 777 778 attr->sa_vattr.va_mask = 0; 779 780 if (vfs_has_feature(dvp->v_vfsp, VFSFT_XVATTR)) { 781 smb_vop_setup_xvattr(attr, &xvattr); 782 vap = &xvattr.xva_vattr; 783 } else { 784 smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask); 785 vap = &attr->sa_vattr; 786 } 787 788 if (flags & SMB_CATIA) { 789 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf)); 790 if (strchr(np, '/') != NULL) 791 return (EILSEQ); 792 } 793 794 error = VOP_CREATE(dvp, np, vap, EXCL, attr->sa_vattr.va_mode, 795 vpp, cr, option_flags, &smb_ct, vsap); 796 797 /* 798 * One could argue that filesystems should obey the size 799 * if specified in the create attributes. Unfortunately, 800 * they only appear to let you truncate the size to zero. 801 * SMB needs to set a non-zero size, so work-around. 802 */ 803 if (error == 0 && *vpp != NULL && 804 (vap->va_mask & AT_SIZE) != 0 && 805 vap->va_size > 0) { 806 vattr_t ta = *vap; 807 ta.va_mask = AT_SIZE; 808 (void) VOP_SETATTR(*vpp, &ta, 0, cr, &smb_ct); 809 } 810 811 return (error); 812 } 813 814 int 815 smb_vop_remove(vnode_t *dvp, char *name, int flags, cred_t *cr) 816 { 817 int error; 818 int option_flags = 0; 819 char *np = name; 820 char namebuf[MAXNAMELEN]; 821 822 if (flags & SMB_IGNORE_CASE) 823 option_flags = FIGNORECASE; 824 825 if (flags & SMB_CATIA) 826 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf)); 827 828 error = VOP_REMOVE(dvp, np, cr, &smb_ct, option_flags); 829 830 return (error); 831 } 832 833 /* 834 * smb_vop_link(target-dir-vp, source-file-vp, target-name) 835 * 836 * Create a link - same tree (identical TID) only. 837 */ 838 int 839 smb_vop_link(vnode_t *to_dvp, vnode_t *from_vp, char *to_name, 840 int flags, cred_t *cr) 841 { 842 int option_flags = 0; 843 char *np, *buf; 844 int rc; 845 846 if (flags & SMB_IGNORE_CASE) 847 option_flags = FIGNORECASE; 848 849 if (flags & SMB_CATIA) { 850 buf = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 851 np = smb_vop_catia_v5tov4(to_name, buf, MAXNAMELEN); 852 if (strchr(np, '/') != NULL) { 853 kmem_free(buf, MAXNAMELEN); 854 return (EILSEQ); 855 } 856 857 rc = VOP_LINK(to_dvp, from_vp, np, cr, &smb_ct, option_flags); 858 kmem_free(buf, MAXNAMELEN); 859 return (rc); 860 } 861 862 rc = VOP_LINK(to_dvp, from_vp, to_name, cr, &smb_ct, option_flags); 863 return (rc); 864 } 865 866 /* 867 * smb_vop_rename() 868 * 869 * The rename is for files in the same tree (identical TID) only. 870 */ 871 int 872 smb_vop_rename(vnode_t *from_dvp, char *from_name, vnode_t *to_dvp, 873 char *to_name, int flags, cred_t *cr) 874 { 875 int error; 876 int option_flags = 0; 877 char *from, *to, *fbuf, *tbuf; 878 879 if (flags & SMB_IGNORE_CASE) 880 option_flags = FIGNORECASE; 881 882 if (flags & SMB_CATIA) { 883 tbuf = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 884 to = smb_vop_catia_v5tov4(to_name, tbuf, MAXNAMELEN); 885 if (strchr(to, '/') != NULL) { 886 kmem_free(tbuf, MAXNAMELEN); 887 return (EILSEQ); 888 } 889 890 fbuf = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 891 from = smb_vop_catia_v5tov4(from_name, fbuf, MAXNAMELEN); 892 893 error = VOP_RENAME(from_dvp, from, to_dvp, to, cr, 894 &smb_ct, option_flags); 895 896 kmem_free(tbuf, MAXNAMELEN); 897 kmem_free(fbuf, MAXNAMELEN); 898 return (error); 899 } 900 901 error = VOP_RENAME(from_dvp, from_name, to_dvp, to_name, cr, 902 &smb_ct, option_flags); 903 904 return (error); 905 } 906 907 int 908 smb_vop_mkdir(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp, 909 int flags, cred_t *cr, vsecattr_t *vsap) 910 { 911 int error; 912 int option_flags = 0; 913 xvattr_t xvattr; 914 vattr_t *vap; 915 char *np = name; 916 char namebuf[MAXNAMELEN]; 917 918 if (flags & SMB_IGNORE_CASE) 919 option_flags = FIGNORECASE; 920 921 attr->sa_vattr.va_mask = 0; 922 923 if (vfs_has_feature(dvp->v_vfsp, VFSFT_XVATTR)) { 924 smb_vop_setup_xvattr(attr, &xvattr); 925 vap = &xvattr.xva_vattr; 926 } else { 927 smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask); 928 vap = &attr->sa_vattr; 929 } 930 931 if (flags & SMB_CATIA) { 932 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf)); 933 if (strchr(np, '/') != NULL) 934 return (EILSEQ); 935 } 936 937 error = VOP_MKDIR(dvp, np, vap, vpp, cr, &smb_ct, option_flags, vsap); 938 939 return (error); 940 } 941 942 /* 943 * smb_vop_rmdir() 944 * 945 * Only simple rmdir supported, consistent with NT semantics 946 * (can only remove an empty directory). 947 * 948 * The third argument to VOP_RMDIR is the current directory of 949 * the process. It allows rmdir wants to EINVAL if one tries to 950 * remove ".". Since SMB servers do not know what their clients' 951 * current directories are, we fake it by supplying a vnode known 952 * to exist and illegal to remove (rootdir). 953 */ 954 int 955 smb_vop_rmdir(vnode_t *dvp, char *name, int flags, cred_t *cr) 956 { 957 int error; 958 int option_flags = 0; 959 char *np = name; 960 char namebuf[MAXNAMELEN]; 961 962 if (flags & SMB_IGNORE_CASE) 963 option_flags = FIGNORECASE; 964 965 if (flags & SMB_CATIA) 966 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf)); 967 968 error = VOP_RMDIR(dvp, np, rootdir, cr, &smb_ct, option_flags); 969 return (error); 970 } 971 972 int 973 smb_vop_commit(vnode_t *vp, cred_t *cr) 974 { 975 return (VOP_FSYNC(vp, 1, cr, &smb_ct)); 976 } 977 978 /* 979 * Some code in smb_node.c needs to know which DOS attributes 980 * we can actually store. Let's define a mask here of all the 981 * DOS attribute flags supported by the following function. 982 */ 983 const uint32_t 984 smb_vop_dosattr_settable = 985 FILE_ATTRIBUTE_ARCHIVE | 986 FILE_ATTRIBUTE_SYSTEM | 987 FILE_ATTRIBUTE_HIDDEN | 988 FILE_ATTRIBUTE_READONLY | 989 FILE_ATTRIBUTE_OFFLINE | 990 FILE_ATTRIBUTE_SPARSE_FILE; 991 992 static void 993 smb_vop_setup_xvattr(smb_attr_t *smb_attr, xvattr_t *xvattr) 994 { 995 xoptattr_t *xoap = NULL; 996 uint_t xva_mask; 997 998 /* 999 * Initialize xvattr, including bzero 1000 */ 1001 xva_init(xvattr); 1002 xoap = xva_getxoptattr(xvattr); 1003 1004 ASSERT(xoap); 1005 1006 /* 1007 * Copy caller-specified classic attributes to xvattr. 1008 * First save xvattr's mask (set in xva_init()), which 1009 * contains AT_XVATTR. This is |'d in later if needed. 1010 */ 1011 1012 xva_mask = xvattr->xva_vattr.va_mask; 1013 xvattr->xva_vattr = smb_attr->sa_vattr; 1014 1015 smb_sa_to_va_mask(smb_attr->sa_mask, &xvattr->xva_vattr.va_mask); 1016 1017 /* 1018 * Do not set ctime (only the file system can do it) 1019 */ 1020 1021 xvattr->xva_vattr.va_mask &= ~AT_CTIME; 1022 1023 if (smb_attr->sa_mask & SMB_AT_DOSATTR) { 1024 1025 /* 1026 * "|" in the original xva_mask, which contains 1027 * AT_XVATTR 1028 */ 1029 1030 xvattr->xva_vattr.va_mask |= xva_mask; 1031 1032 XVA_SET_REQ(xvattr, XAT_ARCHIVE); 1033 XVA_SET_REQ(xvattr, XAT_SYSTEM); 1034 XVA_SET_REQ(xvattr, XAT_READONLY); 1035 XVA_SET_REQ(xvattr, XAT_HIDDEN); 1036 XVA_SET_REQ(xvattr, XAT_OFFLINE); 1037 XVA_SET_REQ(xvattr, XAT_SPARSE); 1038 1039 /* 1040 * smb_attr->sa_dosattr: If a given bit is not set, 1041 * that indicates that the corresponding field needs 1042 * to be updated with a "0" value. This is done 1043 * implicitly as the xoap->xoa_* fields were bzero'd. 1044 */ 1045 1046 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_ARCHIVE) 1047 xoap->xoa_archive = 1; 1048 1049 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_SYSTEM) 1050 xoap->xoa_system = 1; 1051 1052 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_READONLY) 1053 xoap->xoa_readonly = 1; 1054 1055 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_HIDDEN) 1056 xoap->xoa_hidden = 1; 1057 1058 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_OFFLINE) 1059 xoap->xoa_offline = 1; 1060 1061 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE) 1062 xoap->xoa_sparse = 1; 1063 } 1064 1065 if (smb_attr->sa_mask & SMB_AT_CRTIME) { 1066 /* 1067 * "|" in the original xva_mask, which contains 1068 * AT_XVATTR 1069 */ 1070 1071 xvattr->xva_vattr.va_mask |= xva_mask; 1072 XVA_SET_REQ(xvattr, XAT_CREATETIME); 1073 xoap->xoa_createtime = smb_attr->sa_crtime; 1074 } 1075 } 1076 1077 /* 1078 * smb_vop_readdir() 1079 * 1080 * Collects an SMB_MINLEN_RDDIR_BUF "page" of directory entries. 1081 * The directory entries are returned in an fs-independent format by the 1082 * underlying file system. That is, the "page" of information returned is 1083 * not literally stored on-disk in the format returned. 1084 * If the file system supports extended directory entries (has features 1085 * VFSFT_DIRENTFLAGS), set V_RDDIR_ENTFLAGS to cause the buffer to be 1086 * filled with edirent_t structures, instead of dirent64_t structures. 1087 * If the file system supports access based enumeration (abe), set 1088 * V_RDDIR_ACCFILTER to filter directory entries based on user cred. 1089 */ 1090 int 1091 smb_vop_readdir(vnode_t *vp, uint32_t offset, 1092 void *buf, int *count, int *eof, uint32_t rddir_flag, cred_t *cr) 1093 { 1094 int error = 0; 1095 int flags = 0; 1096 int rdirent_size; 1097 struct uio auio; 1098 struct iovec aiov; 1099 1100 if (vp->v_type != VDIR) 1101 return (ENOTDIR); 1102 1103 if ((rddir_flag & SMB_EDIRENT) != 0 && 1104 vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS)) { 1105 flags |= V_RDDIR_ENTFLAGS; 1106 rdirent_size = sizeof (edirent_t); 1107 } else { 1108 rdirent_size = sizeof (dirent64_t); 1109 } 1110 1111 if (*count < rdirent_size) 1112 return (EINVAL); 1113 1114 if (rddir_flag & SMB_ABE) 1115 flags |= V_RDDIR_ACCFILTER; 1116 1117 aiov.iov_base = buf; 1118 aiov.iov_len = *count; 1119 auio.uio_iov = &aiov; 1120 auio.uio_iovcnt = 1; 1121 auio.uio_loffset = (uint64_t)offset; 1122 auio.uio_segflg = UIO_SYSSPACE; 1123 auio.uio_extflg = UIO_COPY_DEFAULT; 1124 auio.uio_resid = *count; 1125 auio.uio_fmode = 0; 1126 1127 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &smb_ct); 1128 error = VOP_READDIR(vp, &auio, cr, eof, &smb_ct, flags); 1129 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &smb_ct); 1130 1131 if (error == 0) 1132 *count = *count - auio.uio_resid; 1133 1134 return (error); 1135 } 1136 1137 /* 1138 * smb_sa_to_va_mask 1139 * 1140 * Set va_mask by running through the SMB_AT_* #define's and 1141 * setting those bits that correspond to the SMB_AT_* bits 1142 * set in sa_mask. 1143 */ 1144 void 1145 smb_sa_to_va_mask(uint_t sa_mask, uint_t *va_maskp) 1146 { 1147 int i; 1148 uint_t smask; 1149 1150 smask = (sa_mask); 1151 for (i = SMB_AT_TYPE; (i < SMB_AT_MAX) && (smask != 0); ++i) { 1152 if (smask & 1) 1153 *(va_maskp) |= smb_attrmap[i]; 1154 1155 smask >>= 1; 1156 } 1157 } 1158 1159 /* 1160 * Variant of smb_sa_to_va_mask for vop_getattr, 1161 * adding some bits for SMB_AT_ALLOCSZ etc. 1162 */ 1163 void 1164 smb_sa_to_va_mask_get(uint_t sa_mask, uint_t *va_maskp) 1165 { 1166 smb_sa_to_va_mask(sa_mask, va_maskp); 1167 1168 *va_maskp |= AT_TYPE; 1169 if ((sa_mask & SMB_AT_ALLOCSZ) != 0) { 1170 *va_maskp |= (AT_SIZE | AT_NBLOCKS); 1171 } 1172 } 1173 1174 /* 1175 * smb_vop_stream_lookup() 1176 * 1177 * The name returned in od_name is the on-disk name of the stream with the 1178 * SMB_STREAM_PREFIX stripped off. od_name should be allocated to MAXNAMELEN 1179 * by the caller. 1180 */ 1181 int 1182 smb_vop_stream_lookup( 1183 vnode_t *fvp, 1184 char *stream_name, 1185 vnode_t **vpp, 1186 char *od_name, 1187 vnode_t **xattrdirvpp, 1188 int flags, 1189 vnode_t *rootvp, 1190 cred_t *cr) 1191 { 1192 char *solaris_stream_name; 1193 char *name; 1194 int error, tmpflgs; 1195 1196 if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp, 1197 LOOKUP_XATTR | CREATE_XATTR_DIR, cr)) != 0) 1198 return (error); 1199 1200 /* 1201 * Prepend SMB_STREAM_PREFIX to stream name 1202 */ 1203 1204 solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 1205 (void) snprintf(solaris_stream_name, MAXNAMELEN, 1206 "%s%s", SMB_STREAM_PREFIX, stream_name); 1207 1208 /* 1209 * "name" will hold the on-disk name returned from smb_vop_lookup 1210 * for the stream, including the SMB_STREAM_PREFIX. 1211 */ 1212 1213 name = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 1214 1215 if ((error = smb_vop_lookup(*xattrdirvpp, solaris_stream_name, vpp, 1216 name, flags, &tmpflgs, rootvp, NULL, cr)) != 0) { 1217 VN_RELE(*xattrdirvpp); 1218 } else { 1219 (void) strlcpy(od_name, &(name[SMB_STREAM_PREFIX_LEN]), 1220 MAXNAMELEN); 1221 } 1222 1223 kmem_free(solaris_stream_name, MAXNAMELEN); 1224 kmem_free(name, MAXNAMELEN); 1225 1226 return (error); 1227 } 1228 1229 int 1230 smb_vop_stream_create(vnode_t *fvp, char *stream_name, smb_attr_t *attr, 1231 vnode_t **vpp, vnode_t **xattrdirvpp, int flags, cred_t *cr) 1232 { 1233 char *solaris_stream_name; 1234 int error; 1235 1236 if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp, 1237 LOOKUP_XATTR | CREATE_XATTR_DIR, cr)) != 0) 1238 return (error); 1239 1240 /* 1241 * Prepend SMB_STREAM_PREFIX to stream name 1242 */ 1243 1244 solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 1245 (void) snprintf(solaris_stream_name, MAXNAMELEN, 1246 "%s%s", SMB_STREAM_PREFIX, stream_name); 1247 1248 if ((error = smb_vop_create(*xattrdirvpp, solaris_stream_name, attr, 1249 vpp, flags, cr, NULL)) != 0) 1250 VN_RELE(*xattrdirvpp); 1251 1252 kmem_free(solaris_stream_name, MAXNAMELEN); 1253 1254 return (error); 1255 } 1256 1257 int 1258 smb_vop_stream_remove(vnode_t *vp, char *stream_name, int flags, cred_t *cr) 1259 { 1260 char *solaris_stream_name; 1261 vnode_t *xattrdirvp; 1262 int error; 1263 1264 error = smb_vop_lookup_xattrdir(vp, &xattrdirvp, LOOKUP_XATTR, cr); 1265 if (error != 0) 1266 return (error); 1267 1268 /* 1269 * Prepend SMB_STREAM_PREFIX to stream name 1270 */ 1271 1272 solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 1273 (void) snprintf(solaris_stream_name, MAXNAMELEN, 1274 "%s%s", SMB_STREAM_PREFIX, stream_name); 1275 1276 /* XXX might have to use kcred */ 1277 error = smb_vop_remove(xattrdirvp, solaris_stream_name, flags, cr); 1278 1279 kmem_free(solaris_stream_name, MAXNAMELEN); 1280 VN_RELE(xattrdirvp); 1281 1282 return (error); 1283 } 1284 1285 int 1286 smb_vop_lookup_xattrdir(vnode_t *fvp, vnode_t **xattrdirvpp, int flags, 1287 cred_t *cr) 1288 { 1289 int error; 1290 1291 error = VOP_LOOKUP(fvp, "", xattrdirvpp, NULL, flags, NULL, cr, 1292 &smb_ct, NULL, NULL); 1293 return (error); 1294 } 1295 1296 /* 1297 * smb_vop_traverse_check() 1298 * 1299 * This function checks to see if the passed-in vnode has a file system 1300 * mounted on it. If it does, the mount point is "traversed" and the 1301 * vnode for the root of the file system is returned. 1302 */ 1303 int 1304 smb_vop_traverse_check(vnode_t **vpp) 1305 { 1306 int error; 1307 1308 if (vn_mountedvfs(*vpp) == 0) 1309 return (0); 1310 1311 /* 1312 * traverse() may return a different held vnode, even in the error case. 1313 * If it returns a different vnode, it will have released the original. 1314 */ 1315 1316 error = traverse(vpp); 1317 1318 return (error); 1319 } 1320 1321 int /*ARGSUSED*/ 1322 smb_vop_statfs(vnode_t *vp, struct statvfs64 *statp, cred_t *cr) 1323 { 1324 int error; 1325 1326 error = VFS_STATVFS(vp->v_vfsp, statp); 1327 1328 return (error); 1329 } 1330 1331 /* 1332 * smb_vop_acl_read 1333 * 1334 * Reads the ACL of the specified file into 'aclp'. 1335 * acl_type is the type of ACL which the filesystem supports. 1336 * 1337 * Caller has to free the allocated memory for aclp by calling 1338 * acl_free(). 1339 */ 1340 int 1341 smb_vop_acl_read(vnode_t *vp, acl_t **aclp, int flags, acl_type_t acl_type, 1342 cred_t *cr) 1343 { 1344 int error; 1345 vsecattr_t vsecattr; 1346 1347 ASSERT(vp); 1348 ASSERT(aclp); 1349 1350 *aclp = NULL; 1351 bzero(&vsecattr, sizeof (vsecattr_t)); 1352 1353 switch (acl_type) { 1354 case ACLENT_T: 1355 vsecattr.vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL | 1356 VSA_DFACLCNT; 1357 break; 1358 1359 case ACE_T: 1360 vsecattr.vsa_mask = VSA_ACE | VSA_ACECNT | VSA_ACE_ACLFLAGS; 1361 break; 1362 1363 default: 1364 return (EINVAL); 1365 } 1366 1367 if ((error = VOP_GETSECATTR(vp, &vsecattr, flags, cr, &smb_ct)) != 0) 1368 return (error); 1369 1370 *aclp = smb_fsacl_from_vsa(&vsecattr, acl_type); 1371 if (vp->v_type == VDIR) 1372 (*aclp)->acl_flags |= ACL_IS_DIR; 1373 1374 return (0); 1375 } 1376 1377 /* 1378 * smb_vop_acl_write 1379 * 1380 * Writes the given ACL in aclp for the specified file. 1381 */ 1382 int 1383 smb_vop_acl_write(vnode_t *vp, acl_t *aclp, int flags, cred_t *cr) 1384 { 1385 int error; 1386 vsecattr_t vsecattr; 1387 int aclbsize; 1388 1389 ASSERT(vp); 1390 ASSERT(aclp); 1391 1392 error = smb_fsacl_to_vsa(aclp, &vsecattr, &aclbsize); 1393 1394 if (error == 0) { 1395 (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, &smb_ct); 1396 error = VOP_SETSECATTR(vp, &vsecattr, flags, cr, &smb_ct); 1397 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, &smb_ct); 1398 } 1399 1400 if (aclbsize && vsecattr.vsa_aclentp) 1401 kmem_free(vsecattr.vsa_aclentp, aclbsize); 1402 1403 return (error); 1404 } 1405 1406 /* 1407 * smb_vop_acl_type 1408 * 1409 * Determines the ACL type for the given vnode. 1410 * ACLENT_T is a Posix ACL and ACE_T is a ZFS ACL. 1411 */ 1412 acl_type_t 1413 smb_vop_acl_type(vnode_t *vp) 1414 { 1415 int error; 1416 ulong_t whichacl; 1417 1418 error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &whichacl, 1419 zone_kcred(), NULL); 1420 if (error != 0) { 1421 /* 1422 * If we got an error, then the filesystem 1423 * likely does not understand the _PC_ACL_ENABLED 1424 * pathconf. In this case, we fall back to trying 1425 * POSIX-draft (aka UFS-style) ACLs. 1426 */ 1427 whichacl = _ACL_ACLENT_ENABLED; 1428 } 1429 1430 if (!(whichacl & (_ACL_ACE_ENABLED | _ACL_ACLENT_ENABLED))) { 1431 /* 1432 * If the file system supports neither ACE nor 1433 * ACLENT ACLs we will fall back to UFS-style ACLs 1434 * like we did above if there was an error upon 1435 * calling VOP_PATHCONF. 1436 * 1437 * ACE and ACLENT type ACLs are the only interfaces 1438 * supported thus far. If any other bits are set on 1439 * 'whichacl' upon return from VOP_PATHCONF, we will 1440 * ignore them. 1441 */ 1442 whichacl = _ACL_ACLENT_ENABLED; 1443 } 1444 1445 if (whichacl == _ACL_ACLENT_ENABLED) 1446 return (ACLENT_T); 1447 1448 return (ACE_T); 1449 } 1450 1451 static const int zfs_perms[] = { 1452 ACE_READ_DATA, ACE_WRITE_DATA, ACE_APPEND_DATA, ACE_READ_NAMED_ATTRS, 1453 ACE_WRITE_NAMED_ATTRS, ACE_EXECUTE, ACE_DELETE_CHILD, 1454 ACE_READ_ATTRIBUTES, ACE_WRITE_ATTRIBUTES, ACE_DELETE, ACE_READ_ACL, 1455 ACE_WRITE_ACL, ACE_WRITE_OWNER, ACE_SYNCHRONIZE 1456 }; 1457 1458 static const int unix_perms[] = { VREAD, VWRITE, VEXEC }; 1459 /* 1460 * smb_vop_eaccess 1461 * 1462 * Returns the effective permission of the given credential for the 1463 * specified object. 1464 * 1465 * This is just a workaround. We need VFS/FS support for this. 1466 */ 1467 void 1468 smb_vop_eaccess(vnode_t *vp, int *mode, int flags, vnode_t *dir_vp, cred_t *cr) 1469 { 1470 int error, i; 1471 int pnum; 1472 1473 *mode = 0; 1474 1475 if (flags == V_ACE_MASK) { 1476 pnum = sizeof (zfs_perms) / sizeof (int); 1477 1478 for (i = 0; i < pnum; i++) { 1479 error = smb_vop_access(vp, zfs_perms[i], flags, 1480 dir_vp, cr); 1481 if (error == 0) 1482 *mode |= zfs_perms[i]; 1483 } 1484 } else { 1485 pnum = sizeof (unix_perms) / sizeof (int); 1486 1487 for (i = 0; i < pnum; i++) { 1488 error = smb_vop_access(vp, unix_perms[i], flags, 1489 dir_vp, cr); 1490 if (error == 0) 1491 *mode |= unix_perms[i]; 1492 } 1493 } 1494 } 1495 1496 /* 1497 * See comments for smb_fsop_shrlock() 1498 */ 1499 int 1500 smb_vop_shrlock(vnode_t *vp, uint32_t uniq_fid, uint32_t desired_access, 1501 uint32_t share_access, cred_t *cr) 1502 { 1503 struct shrlock shr; 1504 struct shr_locowner shr_own; 1505 short new_access = 0; 1506 short deny = 0; 1507 int flag = 0; 1508 int cmd; 1509 1510 /* 1511 * share locking is not supported for non-regular 1512 * objects in NBMAND mode. 1513 */ 1514 if (nbl_need_check(vp)) { 1515 if (vp->v_type != VREG) 1516 return (0); 1517 1518 cmd = F_SHARE_NBMAND; 1519 } else { 1520 cmd = F_SHARE; 1521 } 1522 1523 if ((desired_access & FILE_DATA_ALL) == 0) { 1524 /* metadata access only */ 1525 new_access |= F_MDACC; 1526 } else { 1527 if (desired_access & (ACE_READ_DATA | ACE_EXECUTE)) { 1528 new_access |= F_RDACC; 1529 flag |= FREAD; 1530 } 1531 1532 if (desired_access & (ACE_WRITE_DATA | ACE_APPEND_DATA | 1533 ACE_ADD_FILE)) { 1534 new_access |= F_WRACC; 1535 flag |= FWRITE; 1536 } 1537 1538 if (SMB_DENY_READ(share_access)) { 1539 deny |= F_RDDNY; 1540 } 1541 1542 if (SMB_DENY_WRITE(share_access)) { 1543 deny |= F_WRDNY; 1544 } 1545 1546 if (cmd == F_SHARE_NBMAND) { 1547 if (desired_access & ACE_DELETE) 1548 new_access |= F_RMACC; 1549 1550 if (SMB_DENY_DELETE(share_access)) { 1551 deny |= F_RMDNY; 1552 } 1553 } 1554 } 1555 1556 shr.s_access = new_access; 1557 shr.s_deny = deny; 1558 shr.s_sysid = smb_ct.cc_sysid; 1559 shr.s_pid = uniq_fid; 1560 shr.s_own_len = sizeof (shr_own); 1561 shr.s_owner = (caddr_t)&shr_own; 1562 shr_own.sl_id = shr.s_sysid; 1563 shr_own.sl_pid = shr.s_pid; 1564 1565 return (VOP_SHRLOCK(vp, cmd, &shr, flag, cr, NULL)); 1566 } 1567 1568 int 1569 smb_vop_unshrlock(vnode_t *vp, uint32_t uniq_fid, cred_t *cr) 1570 { 1571 struct shrlock shr; 1572 struct shr_locowner shr_own; 1573 1574 /* 1575 * share locking is not supported for non-regular 1576 * objects in NBMAND mode. 1577 */ 1578 if (nbl_need_check(vp) && (vp->v_type != VREG)) 1579 return (0); 1580 1581 /* 1582 * For s_access and s_deny, we do not need to pass in the original 1583 * values. 1584 */ 1585 shr.s_access = 0; 1586 shr.s_deny = 0; 1587 shr.s_sysid = smb_ct.cc_sysid; 1588 shr.s_pid = uniq_fid; 1589 shr.s_own_len = sizeof (shr_own); 1590 shr.s_owner = (caddr_t)&shr_own; 1591 shr_own.sl_id = shr.s_sysid; 1592 shr_own.sl_pid = shr.s_pid; 1593 1594 return (VOP_SHRLOCK(vp, F_UNSHARE, &shr, 0, cr, NULL)); 1595 } 1596 1597 /* 1598 * Note about mandatory vs advisory locks: 1599 * 1600 * The SMB server really should always request mandatory locks, and 1601 * if the file system does not support them, the SMB server should 1602 * just tell the client it could not get the lock. If we were to 1603 * tell the SMB client "you got the lock" when what they really 1604 * got was only an advisory lock, we would be lying to the client 1605 * about their having exclusive access to the locked range, which 1606 * could easily lead to data corruption. If someone really wants 1607 * the (dangerous) behavior they can set: smb_allow_advisory_locks 1608 */ 1609 int 1610 smb_vop_frlock(vnode_t *vp, cred_t *cr, int flag, flock64_t *bf) 1611 { 1612 flk_callback_t flk_cb; 1613 int cmd = F_SETLK_NBMAND; 1614 1615 if (smb_allow_advisory_locks != 0 && !nbl_need_check(vp)) { 1616 /* 1617 * The file system does not support nbmand, and 1618 * smb_allow_advisory_locks is enabled. (danger!) 1619 */ 1620 cmd = F_SETLK; 1621 } 1622 1623 flk_init_callback(&flk_cb, smb_lock_frlock_callback, NULL); 1624 1625 return (VOP_FRLOCK(vp, cmd, bf, flag, 0, &flk_cb, cr, &smb_ct)); 1626 } 1627 1628 static callb_cpr_t * 1629 /* ARGSUSED */ 1630 smb_lock_frlock_callback(flk_cb_when_t when, void *error) 1631 { 1632 return (0); 1633 } 1634 1635 /* 1636 * smb_vop_catia_init_v4_lookup 1637 * Initialize mapping between wide characters in the range from 1638 * 0x00A4 to 0x00FF and their UNIX (v4) equivalent (wide character). 1639 * Indexed by the decimal value of the wide character (164-255) 1640 * with an offset of -164. 1641 */ 1642 static void 1643 smb_vop_catia_init_v4_lookup() 1644 { 1645 int i, idx, offset = SMB_CATIA_V4_LOOKUP_LOW; 1646 1647 for (i = 0; i < SMB_CATIA_V4_LOOKUP_MAX; i++) 1648 smb_catia_v4_lookup[i] = (smb_wchar_t)(i + offset); 1649 1650 for (i = 0; i < SMB_CATIA_NUM_MAPS; i++) { 1651 idx = (int)catia_maps[i].winchar - offset; 1652 smb_catia_v4_lookup[idx] = (smb_wchar_t)catia_maps[i].unixchar; 1653 } 1654 } 1655 1656 /* 1657 * smb_vop_catia_init_v5_lookup 1658 * Initialize mapping between UNIX ASCII (v4) characters and equivalent 1659 * or translated wide characters. 1660 * Indexed by the decimal value of the ASCII character (0-127). 1661 */ 1662 static void 1663 smb_vop_catia_init_v5_lookup() 1664 { 1665 int i, idx; 1666 1667 for (i = 0; i < SMB_CATIA_V5_LOOKUP_MAX; i++) 1668 smb_catia_v5_lookup[i] = (smb_wchar_t)i; 1669 1670 for (i = 0; i < SMB_CATIA_NUM_MAPS; i++) { 1671 idx = (int)catia_maps[i].unixchar; 1672 smb_catia_v5_lookup[idx] = catia_maps[i].winchar; 1673 } 1674 } 1675 1676 static void 1677 smb_vop_catia_init() 1678 { 1679 smb_vop_catia_init_v4_lookup(); 1680 smb_vop_catia_init_v5_lookup(); 1681 } 1682 1683 /* 1684 * smb_vop_catia_v5tov4 1685 * (windows (v5) to unix (v4)) 1686 * 1687 * Traverse each character in the given source filename and convert the 1688 * multibyte that is equivalent to any special Windows character listed 1689 * in the catia_maps table to the Unix ASCII character if any is 1690 * encountered in the filename. The translated name is returned in buf. 1691 * 1692 * If an error occurs the conversion terminates and name is returned, 1693 * otherwise buf is returned. 1694 */ 1695 char * 1696 smb_vop_catia_v5tov4(char *name, char *buf, int buflen) 1697 { 1698 int v4_idx, numbytes, inc; 1699 int space_left = buflen - 1; /* one byte reserved for null */ 1700 uint32_t wc; 1701 char mbstring[MTS_MB_CHAR_MAX]; 1702 char *p, *src = name, *dst = buf; 1703 1704 ASSERT(name); 1705 ASSERT(buf); 1706 1707 if (!buf || !name) 1708 return (name); 1709 1710 bzero(buf, buflen); 1711 1712 while (*src) { 1713 if ((numbytes = smb_mbtowc(&wc, src, MTS_MB_CHAR_MAX)) < 0) 1714 return (name); 1715 1716 if (wc < SMB_CATIA_V4_LOOKUP_LOW || 1717 wc > SMB_CATIA_V4_LOOKUP_UPPER) { 1718 inc = numbytes; 1719 p = src; 1720 } else { 1721 /* Lookup required. */ 1722 v4_idx = (int)wc - SMB_CATIA_V4_LOOKUP_LOW; 1723 inc = smb_wctomb(mbstring, smb_catia_v4_lookup[v4_idx]); 1724 p = mbstring; 1725 } 1726 1727 if (space_left < inc) 1728 return (name); 1729 1730 (void) strncpy(dst, p, inc); 1731 dst += inc; 1732 space_left -= inc; 1733 src += numbytes; 1734 } 1735 1736 return (buf); 1737 } 1738 1739 /* 1740 * smb_vop_catia_v4tov5 1741 * (unix (v4) to windows (v5)) 1742 * 1743 * Traverse each character in the given filename 'srcbuf' and convert 1744 * the special Unix character that is listed in the catia_maps table to 1745 * the UTF-8 encoding of the corresponding Windows character if any is 1746 * encountered in the filename. 1747 * 1748 * The translated name is returned in buf. 1749 * If an error occurs the conversion terminates and the original name 1750 * is returned in buf. 1751 */ 1752 void 1753 smb_vop_catia_v4tov5(char *name, char *buf, int buflen) 1754 { 1755 int v5_idx, numbytes; 1756 int space_left = buflen - 1; /* one byte reserved for null */ 1757 uint32_t wc; 1758 char mbstring[MTS_MB_CHAR_MAX]; 1759 char *src = name, *dst = buf; 1760 1761 ASSERT(name); 1762 ASSERT(buf); 1763 1764 if (!buf || !name) 1765 return; 1766 1767 (void) bzero(buf, buflen); 1768 while (*src) { 1769 if (smb_isascii(*src)) { 1770 /* Lookup required */ 1771 v5_idx = (int)*src++; 1772 numbytes = smb_wctomb(mbstring, 1773 smb_catia_v5_lookup[v5_idx]); 1774 if (space_left < numbytes) 1775 break; 1776 (void) strncpy(dst, mbstring, numbytes); 1777 } else { 1778 if ((numbytes = smb_mbtowc(&wc, src, 1779 MTS_MB_CHAR_MAX)) < 0) 1780 break; 1781 if (space_left < numbytes) 1782 break; 1783 (void) strncpy(dst, src, numbytes); 1784 src += numbytes; 1785 } 1786 1787 dst += numbytes; 1788 space_left -= numbytes; 1789 } 1790 1791 if (*src) 1792 (void) strlcpy(buf, name, buflen); 1793 } 1794