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