1da6c28aaSamw /*
2da6c28aaSamw * CDDL HEADER START
3da6c28aaSamw *
4da6c28aaSamw * The contents of this file are subject to the terms of the
5da6c28aaSamw * Common Development and Distribution License (the "License").
6da6c28aaSamw * You may not use this file except in compliance with the License.
7da6c28aaSamw *
8da6c28aaSamw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw * See the License for the specific language governing permissions
11da6c28aaSamw * and limitations under the License.
12da6c28aaSamw *
13da6c28aaSamw * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw *
19da6c28aaSamw * CDDL HEADER END
20da6c28aaSamw */
21da6c28aaSamw /*
223fece860SMark Shellenbaum * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23da6c28aaSamw */
24da6c28aaSamw
25da6c28aaSamw #include <sys/param.h>
26da6c28aaSamw #include <sys/isa_defs.h>
27da6c28aaSamw #include <sys/types.h>
28da6c28aaSamw #include <sys/sysmacros.h>
29da6c28aaSamw #include <sys/cred.h>
30da6c28aaSamw #include <sys/systm.h>
31da6c28aaSamw #include <sys/errno.h>
32da6c28aaSamw #include <sys/fcntl.h>
33da6c28aaSamw #include <sys/pathname.h>
34da6c28aaSamw #include <sys/stat.h>
35da6c28aaSamw #include <sys/vfs.h>
36da6c28aaSamw #include <sys/acl.h>
37da6c28aaSamw #include <sys/file.h>
38da6c28aaSamw #include <sys/sunddi.h>
39da6c28aaSamw #include <sys/debug.h>
40da6c28aaSamw #include <sys/cmn_err.h>
41da6c28aaSamw #include <sys/vnode.h>
42da6c28aaSamw #include <sys/mode.h>
43da6c28aaSamw #include <sys/nvpair.h>
44da6c28aaSamw #include <sys/attr.h>
45da6c28aaSamw #include <sys/gfs.h>
46da6c28aaSamw #include <sys/mutex.h>
47da6c28aaSamw #include <fs/fs_subr.h>
48da6c28aaSamw #include <sys/kidmap.h>
49da6c28aaSamw
50da6c28aaSamw typedef struct {
5168469adeSMark Shellenbaum gfs_file_t xattr_gfs_private;
52da6c28aaSamw xattr_view_t xattr_view;
53da6c28aaSamw } xattr_file_t;
54da6c28aaSamw
5568469adeSMark Shellenbaum typedef struct {
5668469adeSMark Shellenbaum gfs_dir_t xattr_gfs_private;
5768469adeSMark Shellenbaum vnode_t *xattr_realvp; /* Only used for VOP_REALVP */
5868469adeSMark Shellenbaum } xattr_dir_t;
5968469adeSMark Shellenbaum
6068469adeSMark Shellenbaum /*
6168469adeSMark Shellenbaum * xattr_realvp is only used for VOP_REALVP, this is so we don't
6268469adeSMark Shellenbaum * keep an unnecessary hold on the *real* xattr dir unless we have
6368469adeSMark Shellenbaum * no other choice.
6468469adeSMark Shellenbaum */
6568469adeSMark Shellenbaum
66da6c28aaSamw /* ARGSUSED */
67da6c28aaSamw static int
xattr_file_open(vnode_t ** vpp,int flags,cred_t * cr,caller_context_t * ct)68da6c28aaSamw xattr_file_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
69da6c28aaSamw {
70da6c28aaSamw xattr_file_t *np = (*vpp)->v_data;
71da6c28aaSamw
72da6c28aaSamw if ((np->xattr_view == XATTR_VIEW_READONLY) && (flags & FWRITE))
73da6c28aaSamw return (EACCES);
74da6c28aaSamw
75da6c28aaSamw return (0);
76da6c28aaSamw }
77da6c28aaSamw
78da6c28aaSamw /* ARGSUSED */
79da6c28aaSamw static int
xattr_file_access(vnode_t * vp,int mode,int flags,cred_t * cr,caller_context_t * ct)80da6c28aaSamw xattr_file_access(vnode_t *vp, int mode, int flags, cred_t *cr,
81da6c28aaSamw caller_context_t *ct)
82da6c28aaSamw {
83da6c28aaSamw xattr_file_t *np = vp->v_data;
84da6c28aaSamw
85da6c28aaSamw if ((np->xattr_view == XATTR_VIEW_READONLY) && (mode & VWRITE))
86da6c28aaSamw return (EACCES);
87da6c28aaSamw
88da6c28aaSamw return (0);
89da6c28aaSamw }
90da6c28aaSamw
91da6c28aaSamw /* ARGSUSED */
92da6c28aaSamw static int
xattr_file_close(vnode_t * vp,int flags,int count,offset_t off,cred_t * cr,caller_context_t * ct)93da6c28aaSamw xattr_file_close(vnode_t *vp, int flags, int count, offset_t off,
94da6c28aaSamw cred_t *cr, caller_context_t *ct)
95da6c28aaSamw {
96da6c28aaSamw cleanlocks(vp, ddi_get_pid(), 0);
97da6c28aaSamw cleanshares(vp, ddi_get_pid());
98da6c28aaSamw return (0);
99da6c28aaSamw }
100da6c28aaSamw
101da6c28aaSamw static int
xattr_common_fid(vnode_t * vp,fid_t * fidp,caller_context_t * ct)102da6c28aaSamw xattr_common_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
103da6c28aaSamw {
104da6c28aaSamw xattr_fid_t *xfidp;
105da6c28aaSamw vnode_t *pvp, *savevp;
106da6c28aaSamw int error;
107da6c28aaSamw uint16_t orig_len;
108da6c28aaSamw
109da6c28aaSamw if (fidp->fid_len < XATTR_FIDSZ) {
110da6c28aaSamw fidp->fid_len = XATTR_FIDSZ;
111da6c28aaSamw return (ENOSPC);
112da6c28aaSamw }
113da6c28aaSamw
114da6c28aaSamw savevp = pvp = gfs_file_parent(vp);
115da6c28aaSamw mutex_enter(&savevp->v_lock);
116da6c28aaSamw if (pvp->v_flag & V_XATTRDIR) {
117da6c28aaSamw pvp = gfs_file_parent(pvp);
118da6c28aaSamw }
119da6c28aaSamw mutex_exit(&savevp->v_lock);
120da6c28aaSamw
121da6c28aaSamw xfidp = (xattr_fid_t *)fidp;
122da6c28aaSamw orig_len = fidp->fid_len;
123da6c28aaSamw fidp->fid_len = sizeof (xfidp->parent_fid);
124da6c28aaSamw
125da6c28aaSamw error = VOP_FID(pvp, fidp, ct);
126da6c28aaSamw if (error) {
127da6c28aaSamw fidp->fid_len = orig_len;
128da6c28aaSamw return (error);
129da6c28aaSamw }
130da6c28aaSamw
131da6c28aaSamw xfidp->parent_len = fidp->fid_len;
132da6c28aaSamw fidp->fid_len = XATTR_FIDSZ;
133da6c28aaSamw xfidp->dir_offset = gfs_file_inode(vp);
134da6c28aaSamw
135da6c28aaSamw return (0);
136da6c28aaSamw }
137da6c28aaSamw
138da6c28aaSamw /* ARGSUSED */
139da6c28aaSamw static int
xattr_fill_nvlist(vnode_t * vp,xattr_view_t xattr_view,nvlist_t * nvlp,cred_t * cr,caller_context_t * ct)140da6c28aaSamw xattr_fill_nvlist(vnode_t *vp, xattr_view_t xattr_view, nvlist_t *nvlp,
141da6c28aaSamw cred_t *cr, caller_context_t *ct)
142da6c28aaSamw {
143da6c28aaSamw int error;
144da6c28aaSamw f_attr_t attr;
145da6c28aaSamw uint64_t fsid;
146da6c28aaSamw xvattr_t xvattr;
147da6c28aaSamw xoptattr_t *xoap; /* Pointer to optional attributes */
148da6c28aaSamw vnode_t *ppvp;
149da6c28aaSamw const char *domain;
150da6c28aaSamw uint32_t rid;
151da6c28aaSamw
152da6c28aaSamw xva_init(&xvattr);
153da6c28aaSamw
154da6c28aaSamw if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
155da6c28aaSamw return (EINVAL);
156da6c28aaSamw
157da6c28aaSamw /*
158da6c28aaSamw * For detecting ephemeral uid/gid
159da6c28aaSamw */
160da6c28aaSamw xvattr.xva_vattr.va_mask |= (AT_UID|AT_GID);
161da6c28aaSamw
162da6c28aaSamw /*
163da6c28aaSamw * We need to access the real fs object.
164da6c28aaSamw * vp points to a GFS file; ppvp points to the real object.
165da6c28aaSamw */
166da6c28aaSamw ppvp = gfs_file_parent(gfs_file_parent(vp));
167da6c28aaSamw
168da6c28aaSamw /*
169da6c28aaSamw * Iterate through the attrs associated with this view
170da6c28aaSamw */
171da6c28aaSamw
172da6c28aaSamw for (attr = 0; attr < F_ATTR_ALL; attr++) {
173da6c28aaSamw if (xattr_view != attr_to_xattr_view(attr)) {
174da6c28aaSamw continue;
175da6c28aaSamw }
176da6c28aaSamw
177da6c28aaSamw switch (attr) {
178da6c28aaSamw case F_SYSTEM:
179da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_SYSTEM);
180da6c28aaSamw break;
181da6c28aaSamw case F_READONLY:
182da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_READONLY);
183da6c28aaSamw break;
184da6c28aaSamw case F_HIDDEN:
185da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_HIDDEN);
186da6c28aaSamw break;
187da6c28aaSamw case F_ARCHIVE:
188da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
189da6c28aaSamw break;
190da6c28aaSamw case F_IMMUTABLE:
191da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
192da6c28aaSamw break;
193da6c28aaSamw case F_APPENDONLY:
194da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
195da6c28aaSamw break;
196da6c28aaSamw case F_NOUNLINK:
197da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
198da6c28aaSamw break;
199da6c28aaSamw case F_OPAQUE:
200da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_OPAQUE);
201da6c28aaSamw break;
202da6c28aaSamw case F_NODUMP:
203da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_NODUMP);
204da6c28aaSamw break;
205da6c28aaSamw case F_AV_QUARANTINED:
206da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
207da6c28aaSamw break;
208da6c28aaSamw case F_AV_MODIFIED:
209da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
210da6c28aaSamw break;
211da6c28aaSamw case F_AV_SCANSTAMP:
212da6c28aaSamw if (ppvp->v_type == VREG)
213da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
214da6c28aaSamw break;
215da6c28aaSamw case F_CRTIME:
216da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_CREATETIME);
217da6c28aaSamw break;
218da6c28aaSamw case F_FSID:
219da6c28aaSamw fsid = (((uint64_t)vp->v_vfsp->vfs_fsid.val[0] << 32) |
220da6c28aaSamw (uint64_t)(vp->v_vfsp->vfs_fsid.val[1] &
221da6c28aaSamw 0xffffffff));
222da6c28aaSamw VERIFY(nvlist_add_uint64(nvlp, attr_to_name(attr),
223da6c28aaSamw fsid) == 0);
224da6c28aaSamw break;
2257a286c47SDai Ngo case F_REPARSE:
2267a286c47SDai Ngo XVA_SET_REQ(&xvattr, XAT_REPARSE);
2277a286c47SDai Ngo break;
22899d5e173STim Haley case F_GEN:
22999d5e173STim Haley XVA_SET_REQ(&xvattr, XAT_GEN);
23099d5e173STim Haley break;
231*fd9ee8b5Sjoyce mcintosh case F_OFFLINE:
232*fd9ee8b5Sjoyce mcintosh XVA_SET_REQ(&xvattr, XAT_OFFLINE);
233*fd9ee8b5Sjoyce mcintosh break;
234*fd9ee8b5Sjoyce mcintosh case F_SPARSE:
235*fd9ee8b5Sjoyce mcintosh XVA_SET_REQ(&xvattr, XAT_SPARSE);
236*fd9ee8b5Sjoyce mcintosh break;
237da6c28aaSamw default:
238da6c28aaSamw break;
239da6c28aaSamw }
240da6c28aaSamw }
241da6c28aaSamw
242da6c28aaSamw error = VOP_GETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
243da6c28aaSamw if (error)
244da6c28aaSamw return (error);
245da6c28aaSamw
246da6c28aaSamw /*
247da6c28aaSamw * Process all the optional attributes together here. Notice that
248da6c28aaSamw * xoap was set when the optional attribute bits were set above.
249da6c28aaSamw */
250da6c28aaSamw if ((xvattr.xva_vattr.va_mask & AT_XVATTR) && xoap) {
251da6c28aaSamw if (XVA_ISSET_RTN(&xvattr, XAT_READONLY)) {
252da6c28aaSamw VERIFY(nvlist_add_boolean_value(nvlp,
253da6c28aaSamw attr_to_name(F_READONLY),
254da6c28aaSamw xoap->xoa_readonly) == 0);
255da6c28aaSamw }
256da6c28aaSamw if (XVA_ISSET_RTN(&xvattr, XAT_HIDDEN)) {
257da6c28aaSamw VERIFY(nvlist_add_boolean_value(nvlp,
258da6c28aaSamw attr_to_name(F_HIDDEN),
259da6c28aaSamw xoap->xoa_hidden) == 0);
260da6c28aaSamw }
261da6c28aaSamw if (XVA_ISSET_RTN(&xvattr, XAT_SYSTEM)) {
262da6c28aaSamw VERIFY(nvlist_add_boolean_value(nvlp,
263da6c28aaSamw attr_to_name(F_SYSTEM),
264da6c28aaSamw xoap->xoa_system) == 0);
265da6c28aaSamw }
266da6c28aaSamw if (XVA_ISSET_RTN(&xvattr, XAT_ARCHIVE)) {
267da6c28aaSamw VERIFY(nvlist_add_boolean_value(nvlp,
268da6c28aaSamw attr_to_name(F_ARCHIVE),
269da6c28aaSamw xoap->xoa_archive) == 0);
270da6c28aaSamw }
271da6c28aaSamw if (XVA_ISSET_RTN(&xvattr, XAT_IMMUTABLE)) {
272da6c28aaSamw VERIFY(nvlist_add_boolean_value(nvlp,
273da6c28aaSamw attr_to_name(F_IMMUTABLE),
274da6c28aaSamw xoap->xoa_immutable) == 0);
275da6c28aaSamw }
276da6c28aaSamw if (XVA_ISSET_RTN(&xvattr, XAT_NOUNLINK)) {
277da6c28aaSamw VERIFY(nvlist_add_boolean_value(nvlp,
278da6c28aaSamw attr_to_name(F_NOUNLINK),
279da6c28aaSamw xoap->xoa_nounlink) == 0);
280da6c28aaSamw }
281da6c28aaSamw if (XVA_ISSET_RTN(&xvattr, XAT_APPENDONLY)) {
282da6c28aaSamw VERIFY(nvlist_add_boolean_value(nvlp,
283da6c28aaSamw attr_to_name(F_APPENDONLY),
284da6c28aaSamw xoap->xoa_appendonly) == 0);
285da6c28aaSamw }
286da6c28aaSamw if (XVA_ISSET_RTN(&xvattr, XAT_NODUMP)) {
287da6c28aaSamw VERIFY(nvlist_add_boolean_value(nvlp,
288da6c28aaSamw attr_to_name(F_NODUMP),
289da6c28aaSamw xoap->xoa_nodump) == 0);
290da6c28aaSamw }
291da6c28aaSamw if (XVA_ISSET_RTN(&xvattr, XAT_OPAQUE)) {
292da6c28aaSamw VERIFY(nvlist_add_boolean_value(nvlp,
293da6c28aaSamw attr_to_name(F_OPAQUE),
294da6c28aaSamw xoap->xoa_opaque) == 0);
295da6c28aaSamw }
296da6c28aaSamw if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED)) {
297da6c28aaSamw VERIFY(nvlist_add_boolean_value(nvlp,
298da6c28aaSamw attr_to_name(F_AV_QUARANTINED),
299da6c28aaSamw xoap->xoa_av_quarantined) == 0);
300da6c28aaSamw }
301da6c28aaSamw if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED)) {
302da6c28aaSamw VERIFY(nvlist_add_boolean_value(nvlp,
303da6c28aaSamw attr_to_name(F_AV_MODIFIED),
304da6c28aaSamw xoap->xoa_av_modified) == 0);
305da6c28aaSamw }
306da6c28aaSamw if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP)) {
307da6c28aaSamw VERIFY(nvlist_add_uint8_array(nvlp,
308da6c28aaSamw attr_to_name(F_AV_SCANSTAMP),
309da6c28aaSamw xoap->xoa_av_scanstamp,
310da6c28aaSamw sizeof (xoap->xoa_av_scanstamp)) == 0);
311da6c28aaSamw }
312da6c28aaSamw if (XVA_ISSET_RTN(&xvattr, XAT_CREATETIME)) {
313da6c28aaSamw VERIFY(nvlist_add_uint64_array(nvlp,
314da6c28aaSamw attr_to_name(F_CRTIME),
315da6c28aaSamw (uint64_t *)&(xoap->xoa_createtime),
316da6c28aaSamw sizeof (xoap->xoa_createtime) /
317da6c28aaSamw sizeof (uint64_t)) == 0);
318da6c28aaSamw }
3197a286c47SDai Ngo if (XVA_ISSET_RTN(&xvattr, XAT_REPARSE)) {
3207a286c47SDai Ngo VERIFY(nvlist_add_boolean_value(nvlp,
3217a286c47SDai Ngo attr_to_name(F_REPARSE),
3227a286c47SDai Ngo xoap->xoa_reparse) == 0);
3237a286c47SDai Ngo }
32499d5e173STim Haley if (XVA_ISSET_RTN(&xvattr, XAT_GEN)) {
32599d5e173STim Haley VERIFY(nvlist_add_uint64(nvlp,
32699d5e173STim Haley attr_to_name(F_GEN),
32799d5e173STim Haley xoap->xoa_generation) == 0);
32899d5e173STim Haley }
329*fd9ee8b5Sjoyce mcintosh if (XVA_ISSET_RTN(&xvattr, XAT_OFFLINE)) {
330*fd9ee8b5Sjoyce mcintosh VERIFY(nvlist_add_boolean_value(nvlp,
331*fd9ee8b5Sjoyce mcintosh attr_to_name(F_OFFLINE),
332*fd9ee8b5Sjoyce mcintosh xoap->xoa_offline) == 0);
333*fd9ee8b5Sjoyce mcintosh }
334*fd9ee8b5Sjoyce mcintosh if (XVA_ISSET_RTN(&xvattr, XAT_SPARSE)) {
335*fd9ee8b5Sjoyce mcintosh VERIFY(nvlist_add_boolean_value(nvlp,
336*fd9ee8b5Sjoyce mcintosh attr_to_name(F_SPARSE),
337*fd9ee8b5Sjoyce mcintosh xoap->xoa_sparse) == 0);
338*fd9ee8b5Sjoyce mcintosh }
339da6c28aaSamw }
340da6c28aaSamw /*
341da6c28aaSamw * Check for optional ownersid/groupsid
342da6c28aaSamw */
343da6c28aaSamw
344da6c28aaSamw if (xvattr.xva_vattr.va_uid > MAXUID) {
345da6c28aaSamw nvlist_t *nvl_sid;
346da6c28aaSamw
347da6c28aaSamw if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
348da6c28aaSamw return (ENOMEM);
349da6c28aaSamw
350bda89588Sjp151216 if (kidmap_getsidbyuid(crgetzone(cr), xvattr.xva_vattr.va_uid,
351da6c28aaSamw &domain, &rid) == 0) {
352da6c28aaSamw VERIFY(nvlist_add_string(nvl_sid,
353da6c28aaSamw SID_DOMAIN, domain) == 0);
354da6c28aaSamw VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0);
355da6c28aaSamw VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_OWNERSID),
356da6c28aaSamw nvl_sid) == 0);
357da6c28aaSamw }
358da6c28aaSamw nvlist_free(nvl_sid);
359da6c28aaSamw }
360da6c28aaSamw if (xvattr.xva_vattr.va_gid > MAXUID) {
361da6c28aaSamw nvlist_t *nvl_sid;
362da6c28aaSamw
363da6c28aaSamw if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
364da6c28aaSamw return (ENOMEM);
365da6c28aaSamw
366bda89588Sjp151216 if (kidmap_getsidbygid(crgetzone(cr), xvattr.xva_vattr.va_gid,
367da6c28aaSamw &domain, &rid) == 0) {
368da6c28aaSamw VERIFY(nvlist_add_string(nvl_sid,
369da6c28aaSamw SID_DOMAIN, domain) == 0);
370da6c28aaSamw VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0);
371da6c28aaSamw VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_GROUPSID),
372da6c28aaSamw nvl_sid) == 0);
373da6c28aaSamw }
374da6c28aaSamw nvlist_free(nvl_sid);
375da6c28aaSamw }
376da6c28aaSamw
377da6c28aaSamw return (0);
378da6c28aaSamw }
379da6c28aaSamw
380da6c28aaSamw /*
381da6c28aaSamw * The size of a sysattr file is the size of the nvlist that will be
382da6c28aaSamw * returned by xattr_file_read(). A call to xattr_file_write() could
383da6c28aaSamw * change the size of that nvlist. That size is not stored persistently
384da6c28aaSamw * so xattr_fill_nvlist() calls VOP_GETATTR so that it can be calculated.
385da6c28aaSamw */
386da6c28aaSamw static int
xattr_file_size(vnode_t * vp,xattr_view_t xattr_view,size_t * size,cred_t * cr,caller_context_t * ct)387da6c28aaSamw xattr_file_size(vnode_t *vp, xattr_view_t xattr_view, size_t *size,
388da6c28aaSamw cred_t *cr, caller_context_t *ct)
389da6c28aaSamw {
390da6c28aaSamw nvlist_t *nvl;
391da6c28aaSamw
392da6c28aaSamw if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)) {
393da6c28aaSamw return (ENOMEM);
394da6c28aaSamw }
395da6c28aaSamw
396da6c28aaSamw if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
397da6c28aaSamw nvlist_free(nvl);
398da6c28aaSamw return (EFAULT);
399da6c28aaSamw }
400da6c28aaSamw
401da6c28aaSamw VERIFY(nvlist_size(nvl, size, NV_ENCODE_XDR) == 0);
402da6c28aaSamw nvlist_free(nvl);
403da6c28aaSamw return (0);
404da6c28aaSamw }
405da6c28aaSamw
406da6c28aaSamw /* ARGSUSED */
407da6c28aaSamw static int
xattr_file_getattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)408da6c28aaSamw xattr_file_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
409da6c28aaSamw caller_context_t *ct)
410da6c28aaSamw {
411da6c28aaSamw xattr_file_t *np = vp->v_data;
412da6c28aaSamw timestruc_t now;
413da6c28aaSamw size_t size;
414da6c28aaSamw int error;
415da6c28aaSamw vnode_t *pvp;
416da6c28aaSamw vattr_t pvattr;
417da6c28aaSamw
418da6c28aaSamw vap->va_type = VREG;
419da6c28aaSamw vap->va_mode = MAKEIMODE(vap->va_type,
420da6c28aaSamw (np->xattr_view == XATTR_VIEW_READONLY ? 0444 : 0644));
421da6c28aaSamw vap->va_nodeid = gfs_file_inode(vp);
422da6c28aaSamw vap->va_nlink = 1;
423da6c28aaSamw pvp = gfs_file_parent(vp);
424da6c28aaSamw (void) memset(&pvattr, 0, sizeof (pvattr));
425da6c28aaSamw pvattr.va_mask = AT_CTIME|AT_MTIME;
426da6c28aaSamw error = VOP_GETATTR(pvp, &pvattr, flags, cr, ct);
427da6c28aaSamw if (error) {
428da6c28aaSamw return (error);
429da6c28aaSamw }
430da6c28aaSamw vap->va_ctime = pvattr.va_ctime;
431da6c28aaSamw vap->va_mtime = pvattr.va_mtime;
432da6c28aaSamw gethrestime(&now);
433da6c28aaSamw vap->va_atime = now;
434da6c28aaSamw vap->va_uid = 0;
435da6c28aaSamw vap->va_gid = 0;
436da6c28aaSamw vap->va_rdev = 0;
437da6c28aaSamw vap->va_blksize = DEV_BSIZE;
438da6c28aaSamw vap->va_seq = 0;
439da6c28aaSamw vap->va_fsid = vp->v_vfsp->vfs_dev;
440da6c28aaSamw error = xattr_file_size(vp, np->xattr_view, &size, cr, ct);
441da6c28aaSamw vap->va_size = size;
442da6c28aaSamw vap->va_nblocks = howmany(vap->va_size, vap->va_blksize);
443da6c28aaSamw return (error);
444da6c28aaSamw }
445da6c28aaSamw
446da6c28aaSamw /* ARGSUSED */
447da6c28aaSamw static int
xattr_file_read(vnode_t * vp,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)448da6c28aaSamw xattr_file_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
449da6c28aaSamw caller_context_t *ct)
450da6c28aaSamw {
451da6c28aaSamw xattr_file_t *np = vp->v_data;
452da6c28aaSamw xattr_view_t xattr_view = np->xattr_view;
453da6c28aaSamw char *buf;
454da6c28aaSamw size_t filesize;
455da6c28aaSamw nvlist_t *nvl;
456da6c28aaSamw int error;
457da6c28aaSamw
458da6c28aaSamw /*
459da6c28aaSamw * Validate file offset and fasttrack empty reads
460da6c28aaSamw */
461da6c28aaSamw if (uiop->uio_loffset < (offset_t)0)
462da6c28aaSamw return (EINVAL);
463da6c28aaSamw
464da6c28aaSamw if (uiop->uio_resid == 0)
465da6c28aaSamw return (0);
466da6c28aaSamw
467da6c28aaSamw if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP))
468da6c28aaSamw return (ENOMEM);
469da6c28aaSamw
470da6c28aaSamw if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
471da6c28aaSamw nvlist_free(nvl);
472da6c28aaSamw return (EFAULT);
473da6c28aaSamw }
474da6c28aaSamw
475da6c28aaSamw VERIFY(nvlist_size(nvl, &filesize, NV_ENCODE_XDR) == 0);
476da6c28aaSamw
477da6c28aaSamw if (uiop->uio_loffset >= filesize) {
478da6c28aaSamw nvlist_free(nvl);
479da6c28aaSamw return (0);
480da6c28aaSamw }
481da6c28aaSamw
482da6c28aaSamw buf = kmem_alloc(filesize, KM_SLEEP);
483da6c28aaSamw VERIFY(nvlist_pack(nvl, &buf, &filesize, NV_ENCODE_XDR,
484da6c28aaSamw KM_SLEEP) == 0);
485da6c28aaSamw
486da6c28aaSamw error = uiomove((caddr_t)buf, filesize, UIO_READ, uiop);
487da6c28aaSamw kmem_free(buf, filesize);
488da6c28aaSamw nvlist_free(nvl);
489da6c28aaSamw return (error);
490da6c28aaSamw }
491da6c28aaSamw
492da6c28aaSamw /* ARGSUSED */
493da6c28aaSamw static int
xattr_file_write(vnode_t * vp,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)494da6c28aaSamw xattr_file_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
495da6c28aaSamw caller_context_t *ct)
496da6c28aaSamw {
497da6c28aaSamw int error = 0;
498da6c28aaSamw char *buf;
499da6c28aaSamw char *domain;
500da6c28aaSamw uint32_t rid;
501da6c28aaSamw ssize_t size = uiop->uio_resid;
502da6c28aaSamw nvlist_t *nvp;
503da6c28aaSamw nvpair_t *pair = NULL;
504da6c28aaSamw vnode_t *ppvp;
505da6c28aaSamw xvattr_t xvattr;
506da6c28aaSamw xoptattr_t *xoap = NULL; /* Pointer to optional attributes */
507da6c28aaSamw
5089660e5cbSJanice Chang if (vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) == 0)
5099660e5cbSJanice Chang return (EINVAL);
5109660e5cbSJanice Chang
511da6c28aaSamw /*
512da6c28aaSamw * Validate file offset and size.
513da6c28aaSamw */
514da6c28aaSamw if (uiop->uio_loffset < (offset_t)0)
515da6c28aaSamw return (EINVAL);
516da6c28aaSamw
517da6c28aaSamw if (size == 0)
518da6c28aaSamw return (EINVAL);
519da6c28aaSamw
520da6c28aaSamw xva_init(&xvattr);
521da6c28aaSamw
522da6c28aaSamw if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
523da6c28aaSamw return (EINVAL);
524da6c28aaSamw }
525da6c28aaSamw
526da6c28aaSamw /*
527da6c28aaSamw * Copy and unpack the nvlist
528da6c28aaSamw */
529da6c28aaSamw buf = kmem_alloc(size, KM_SLEEP);
530da6c28aaSamw if (uiomove((caddr_t)buf, size, UIO_WRITE, uiop)) {
531da6c28aaSamw return (EFAULT);
532da6c28aaSamw }
533da6c28aaSamw
534da6c28aaSamw if (nvlist_unpack(buf, size, &nvp, KM_SLEEP) != 0) {
535da6c28aaSamw kmem_free(buf, size);
536da6c28aaSamw uiop->uio_resid = size;
537da6c28aaSamw return (EINVAL);
538da6c28aaSamw }
539da6c28aaSamw kmem_free(buf, size);
540da6c28aaSamw
541da6c28aaSamw /*
542da6c28aaSamw * Fasttrack empty writes (nvlist with no nvpairs)
543da6c28aaSamw */
544da6c28aaSamw if (nvlist_next_nvpair(nvp, NULL) == 0)
545da6c28aaSamw return (0);
546da6c28aaSamw
547da6c28aaSamw ppvp = gfs_file_parent(gfs_file_parent(vp));
548da6c28aaSamw
549da6c28aaSamw while (pair = nvlist_next_nvpair(nvp, pair)) {
550da6c28aaSamw data_type_t type;
551da6c28aaSamw f_attr_t attr;
552da6c28aaSamw boolean_t value;
553da6c28aaSamw uint64_t *time, *times;
554da6c28aaSamw uint_t elem, nelems;
555da6c28aaSamw nvlist_t *nvp_sid;
556da6c28aaSamw uint8_t *scanstamp;
557da6c28aaSamw
558da6c28aaSamw /*
559da6c28aaSamw * Validate the name and type of each attribute.
560da6c28aaSamw * Log any unknown names and continue. This will
561da6c28aaSamw * help if additional attributes are added later.
562da6c28aaSamw */
563da6c28aaSamw type = nvpair_type(pair);
564da6c28aaSamw if ((attr = name_to_attr(nvpair_name(pair))) == F_ATTR_INVAL) {
565da6c28aaSamw cmn_err(CE_WARN, "Unknown attribute %s",
566da6c28aaSamw nvpair_name(pair));
567da6c28aaSamw continue;
568da6c28aaSamw }
569da6c28aaSamw
570da6c28aaSamw /*
571da6c28aaSamw * Verify nvlist type matches required type and view is OK
572da6c28aaSamw */
573da6c28aaSamw
574da6c28aaSamw if (type != attr_to_data_type(attr) ||
575da6c28aaSamw (attr_to_xattr_view(attr) == XATTR_VIEW_READONLY)) {
576da6c28aaSamw nvlist_free(nvp);
577da6c28aaSamw return (EINVAL);
578da6c28aaSamw }
579da6c28aaSamw
580da6c28aaSamw /*
581da6c28aaSamw * For OWNERSID/GROUPSID make sure the target
582da6c28aaSamw * file system support ephemeral ID's
583da6c28aaSamw */
584da6c28aaSamw if ((attr == F_OWNERSID || attr == F_GROUPSID) &&
585da6c28aaSamw (!(vp->v_vfsp->vfs_flag & VFS_XID))) {
586da6c28aaSamw nvlist_free(nvp);
587da6c28aaSamw return (EINVAL);
588da6c28aaSamw }
589da6c28aaSamw
590da6c28aaSamw /*
591da6c28aaSamw * Retrieve data from nvpair
592da6c28aaSamw */
593da6c28aaSamw switch (type) {
594da6c28aaSamw case DATA_TYPE_BOOLEAN_VALUE:
595da6c28aaSamw if (nvpair_value_boolean_value(pair, &value)) {
596da6c28aaSamw nvlist_free(nvp);
597da6c28aaSamw return (EINVAL);
598da6c28aaSamw }
599da6c28aaSamw break;
600da6c28aaSamw case DATA_TYPE_UINT64_ARRAY:
601da6c28aaSamw if (nvpair_value_uint64_array(pair, ×, &nelems)) {
602da6c28aaSamw nvlist_free(nvp);
603da6c28aaSamw return (EINVAL);
604da6c28aaSamw }
605da6c28aaSamw break;
606da6c28aaSamw case DATA_TYPE_NVLIST:
607da6c28aaSamw if (nvpair_value_nvlist(pair, &nvp_sid)) {
608da6c28aaSamw nvlist_free(nvp);
609da6c28aaSamw return (EINVAL);
610da6c28aaSamw }
611da6c28aaSamw break;
612da6c28aaSamw case DATA_TYPE_UINT8_ARRAY:
613da6c28aaSamw if (nvpair_value_uint8_array(pair,
614da6c28aaSamw &scanstamp, &nelems)) {
615da6c28aaSamw nvlist_free(nvp);
616da6c28aaSamw return (EINVAL);
617da6c28aaSamw }
618da6c28aaSamw break;
619da6c28aaSamw default:
620da6c28aaSamw nvlist_free(nvp);
621da6c28aaSamw return (EINVAL);
622da6c28aaSamw }
623da6c28aaSamw
624da6c28aaSamw switch (attr) {
625da6c28aaSamw /*
626da6c28aaSamw * If we have several similar optional attributes to
627da6c28aaSamw * process then we should do it all together here so that
628da6c28aaSamw * xoap and the requested bitmap can be set in one place.
629da6c28aaSamw */
630da6c28aaSamw case F_READONLY:
631da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_READONLY);
632da6c28aaSamw xoap->xoa_readonly = value;
633da6c28aaSamw break;
634da6c28aaSamw case F_HIDDEN:
635da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_HIDDEN);
636da6c28aaSamw xoap->xoa_hidden = value;
637da6c28aaSamw break;
638da6c28aaSamw case F_SYSTEM:
639da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_SYSTEM);
640da6c28aaSamw xoap->xoa_system = value;
641da6c28aaSamw break;
642da6c28aaSamw case F_ARCHIVE:
643da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
644da6c28aaSamw xoap->xoa_archive = value;
645da6c28aaSamw break;
646da6c28aaSamw case F_IMMUTABLE:
647da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
648da6c28aaSamw xoap->xoa_immutable = value;
649da6c28aaSamw break;
650da6c28aaSamw case F_NOUNLINK:
651da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
652da6c28aaSamw xoap->xoa_nounlink = value;
653da6c28aaSamw break;
654da6c28aaSamw case F_APPENDONLY:
655da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
656da6c28aaSamw xoap->xoa_appendonly = value;
657da6c28aaSamw break;
658da6c28aaSamw case F_NODUMP:
659da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_NODUMP);
660da6c28aaSamw xoap->xoa_nodump = value;
661da6c28aaSamw break;
662da6c28aaSamw case F_AV_QUARANTINED:
663da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
664da6c28aaSamw xoap->xoa_av_quarantined = value;
665da6c28aaSamw break;
666da6c28aaSamw case F_AV_MODIFIED:
667da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
668da6c28aaSamw xoap->xoa_av_modified = value;
669da6c28aaSamw break;
670da6c28aaSamw case F_CRTIME:
671da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_CREATETIME);
672da6c28aaSamw time = (uint64_t *)&(xoap->xoa_createtime);
673da6c28aaSamw for (elem = 0; elem < nelems; elem++)
674da6c28aaSamw *time++ = times[elem];
675da6c28aaSamw break;
676da6c28aaSamw case F_OWNERSID:
677da6c28aaSamw case F_GROUPSID:
678da6c28aaSamw if (nvlist_lookup_string(nvp_sid, SID_DOMAIN,
679da6c28aaSamw &domain) || nvlist_lookup_uint32(nvp_sid, SID_RID,
680da6c28aaSamw &rid)) {
681da6c28aaSamw nvlist_free(nvp);
682da6c28aaSamw return (EINVAL);
683da6c28aaSamw }
684da6c28aaSamw
685da6c28aaSamw /*
686da6c28aaSamw * Now map domain+rid to ephemeral id's
687da6c28aaSamw *
688da6c28aaSamw * If mapping fails, then the uid/gid will
689da6c28aaSamw * be set to UID_NOBODY by Winchester.
690da6c28aaSamw */
691da6c28aaSamw
692da6c28aaSamw if (attr == F_OWNERSID) {
693bda89588Sjp151216 (void) kidmap_getuidbysid(crgetzone(cr), domain,
694bda89588Sjp151216 rid, &xvattr.xva_vattr.va_uid);
695da6c28aaSamw xvattr.xva_vattr.va_mask |= AT_UID;
696da6c28aaSamw } else {
697bda89588Sjp151216 (void) kidmap_getgidbysid(crgetzone(cr), domain,
698bda89588Sjp151216 rid, &xvattr.xva_vattr.va_gid);
699da6c28aaSamw xvattr.xva_vattr.va_mask |= AT_GID;
700da6c28aaSamw }
701da6c28aaSamw break;
702da6c28aaSamw case F_AV_SCANSTAMP:
703da6c28aaSamw if (ppvp->v_type == VREG) {
704da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
705da6c28aaSamw (void) memcpy(xoap->xoa_av_scanstamp,
706da6c28aaSamw scanstamp, nelems);
707da6c28aaSamw } else {
708da6c28aaSamw nvlist_free(nvp);
709da6c28aaSamw return (EINVAL);
710da6c28aaSamw }
711da6c28aaSamw break;
7127a286c47SDai Ngo case F_REPARSE:
7137a286c47SDai Ngo XVA_SET_REQ(&xvattr, XAT_REPARSE);
7147a286c47SDai Ngo xoap->xoa_reparse = value;
7157a286c47SDai Ngo break;
716*fd9ee8b5Sjoyce mcintosh case F_OFFLINE:
717*fd9ee8b5Sjoyce mcintosh XVA_SET_REQ(&xvattr, XAT_OFFLINE);
718*fd9ee8b5Sjoyce mcintosh xoap->xoa_offline = value;
719*fd9ee8b5Sjoyce mcintosh break;
720*fd9ee8b5Sjoyce mcintosh case F_SPARSE:
721*fd9ee8b5Sjoyce mcintosh XVA_SET_REQ(&xvattr, XAT_SPARSE);
722*fd9ee8b5Sjoyce mcintosh xoap->xoa_sparse = value;
723*fd9ee8b5Sjoyce mcintosh break;
724da6c28aaSamw default:
725da6c28aaSamw break;
726da6c28aaSamw }
727da6c28aaSamw }
728da6c28aaSamw
729da6c28aaSamw ppvp = gfs_file_parent(gfs_file_parent(vp));
730da6c28aaSamw error = VOP_SETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
731da6c28aaSamw if (error)
732da6c28aaSamw uiop->uio_resid = size;
733da6c28aaSamw
734da6c28aaSamw nvlist_free(nvp);
735da6c28aaSamw return (error);
736da6c28aaSamw }
737da6c28aaSamw
738da6c28aaSamw static int
xattr_file_pathconf(vnode_t * vp,int cmd,ulong_t * valp,cred_t * cr,caller_context_t * ct)739da6c28aaSamw xattr_file_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
740da6c28aaSamw caller_context_t *ct)
741da6c28aaSamw {
742da6c28aaSamw switch (cmd) {
743da6c28aaSamw case _PC_XATTR_EXISTS:
744da6c28aaSamw case _PC_SATTR_ENABLED:
745da6c28aaSamw case _PC_SATTR_EXISTS:
746da6c28aaSamw *valp = 0;
747da6c28aaSamw return (0);
748da6c28aaSamw default:
749da6c28aaSamw return (fs_pathconf(vp, cmd, valp, cr, ct));
750da6c28aaSamw }
751da6c28aaSamw }
752da6c28aaSamw
753da6c28aaSamw vnodeops_t *xattr_file_ops;
754da6c28aaSamw
755da6c28aaSamw static const fs_operation_def_t xattr_file_tops[] = {
756da6c28aaSamw { VOPNAME_OPEN, { .vop_open = xattr_file_open } },
757da6c28aaSamw { VOPNAME_CLOSE, { .vop_close = xattr_file_close } },
758da6c28aaSamw { VOPNAME_READ, { .vop_read = xattr_file_read } },
759da6c28aaSamw { VOPNAME_WRITE, { .vop_write = xattr_file_write } },
760da6c28aaSamw { VOPNAME_IOCTL, { .error = fs_ioctl } },
761da6c28aaSamw { VOPNAME_GETATTR, { .vop_getattr = xattr_file_getattr } },
762da6c28aaSamw { VOPNAME_ACCESS, { .vop_access = xattr_file_access } },
763da6c28aaSamw { VOPNAME_READDIR, { .error = fs_notdir } },
764da6c28aaSamw { VOPNAME_SEEK, { .vop_seek = fs_seek } },
765da6c28aaSamw { VOPNAME_INACTIVE, { .vop_inactive = gfs_vop_inactive } },
766da6c28aaSamw { VOPNAME_FID, { .vop_fid = xattr_common_fid } },
767da6c28aaSamw { VOPNAME_PATHCONF, { .vop_pathconf = xattr_file_pathconf } },
768da6c28aaSamw { VOPNAME_PUTPAGE, { .error = fs_putpage } },
769da6c28aaSamw { VOPNAME_FSYNC, { .error = fs_fsync } },
770da6c28aaSamw { NULL }
771da6c28aaSamw };
772da6c28aaSamw
773da6c28aaSamw vnode_t *
xattr_mkfile(vnode_t * pvp,xattr_view_t xattr_view)774da6c28aaSamw xattr_mkfile(vnode_t *pvp, xattr_view_t xattr_view)
775da6c28aaSamw {
776da6c28aaSamw vnode_t *vp;
777da6c28aaSamw xattr_file_t *np;
778da6c28aaSamw
779da6c28aaSamw vp = gfs_file_create(sizeof (xattr_file_t), pvp, xattr_file_ops);
780da6c28aaSamw np = vp->v_data;
781da6c28aaSamw np->xattr_view = xattr_view;
782da6c28aaSamw vp->v_flag |= V_SYSATTR;
783da6c28aaSamw return (vp);
784da6c28aaSamw }
785da6c28aaSamw
786da6c28aaSamw vnode_t *
xattr_mkfile_ro(vnode_t * pvp)787da6c28aaSamw xattr_mkfile_ro(vnode_t *pvp)
788da6c28aaSamw {
789da6c28aaSamw return (xattr_mkfile(pvp, XATTR_VIEW_READONLY));
790da6c28aaSamw }
791da6c28aaSamw
792da6c28aaSamw vnode_t *
xattr_mkfile_rw(vnode_t * pvp)793da6c28aaSamw xattr_mkfile_rw(vnode_t *pvp)
794da6c28aaSamw {
795da6c28aaSamw return (xattr_mkfile(pvp, XATTR_VIEW_READWRITE));
796da6c28aaSamw }
797da6c28aaSamw
798da6c28aaSamw vnodeops_t *xattr_dir_ops;
799da6c28aaSamw
800da6c28aaSamw static gfs_dirent_t xattr_dirents[] = {
801da6c28aaSamw { VIEW_READONLY, xattr_mkfile_ro, GFS_CACHE_VNODE, },
802da6c28aaSamw { VIEW_READWRITE, xattr_mkfile_rw, GFS_CACHE_VNODE, },
803da6c28aaSamw { NULL },
804da6c28aaSamw };
805da6c28aaSamw
806da6c28aaSamw #define XATTRDIR_NENTS ((sizeof (xattr_dirents) / sizeof (gfs_dirent_t)) - 1)
807da6c28aaSamw
808da6c28aaSamw static int
is_sattr_name(char * s)809da6c28aaSamw is_sattr_name(char *s)
810da6c28aaSamw {
811da6c28aaSamw int i;
812da6c28aaSamw
813da6c28aaSamw for (i = 0; i < XATTRDIR_NENTS; ++i) {
814da6c28aaSamw if (strcmp(s, xattr_dirents[i].gfse_name) == 0) {
815da6c28aaSamw return (1);
816da6c28aaSamw }
817da6c28aaSamw }
818da6c28aaSamw return (0);
819da6c28aaSamw }
820da6c28aaSamw
821b38f0970Sck153898 /*
822b38f0970Sck153898 * Given the name of an extended attribute file, determine if there is a
823b38f0970Sck153898 * normalization conflict with a sysattr view name.
824b38f0970Sck153898 */
825b38f0970Sck153898 int
xattr_sysattr_casechk(char * s)826b38f0970Sck153898 xattr_sysattr_casechk(char *s)
827b38f0970Sck153898 {
828b38f0970Sck153898 int i;
829b38f0970Sck153898
830b38f0970Sck153898 for (i = 0; i < XATTRDIR_NENTS; ++i) {
831b38f0970Sck153898 if (strcasecmp(s, xattr_dirents[i].gfse_name) == 0)
832b38f0970Sck153898 return (1);
833b38f0970Sck153898 }
834b38f0970Sck153898 return (0);
835b38f0970Sck153898 }
836b38f0970Sck153898
837da6c28aaSamw static int
xattr_copy(vnode_t * sdvp,char * snm,vnode_t * tdvp,char * tnm,cred_t * cr,caller_context_t * ct)838da6c28aaSamw xattr_copy(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
839da6c28aaSamw cred_t *cr, caller_context_t *ct)
840da6c28aaSamw {
841da6c28aaSamw xvattr_t xvattr;
842da6c28aaSamw vnode_t *pdvp;
843da6c28aaSamw int error;
844da6c28aaSamw
845da6c28aaSamw /*
846da6c28aaSamw * Only copy system attrs if the views are the same
847da6c28aaSamw */
848da6c28aaSamw if (strcmp(snm, tnm) != 0)
849da6c28aaSamw return (EINVAL);
850da6c28aaSamw
851da6c28aaSamw xva_init(&xvattr);
852da6c28aaSamw
853da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_SYSTEM);
854da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_READONLY);
855da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_HIDDEN);
856da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
857da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
858da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
859da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
860da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_NODUMP);
861da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
862da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
863da6c28aaSamw XVA_SET_REQ(&xvattr, XAT_CREATETIME);
8647a286c47SDai Ngo XVA_SET_REQ(&xvattr, XAT_REPARSE);
865*fd9ee8b5Sjoyce mcintosh XVA_SET_REQ(&xvattr, XAT_OFFLINE);
866*fd9ee8b5Sjoyce mcintosh XVA_SET_REQ(&xvattr, XAT_SPARSE);
867da6c28aaSamw
868da6c28aaSamw pdvp = gfs_file_parent(sdvp);
869da6c28aaSamw error = VOP_GETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
870da6c28aaSamw if (error)
871da6c28aaSamw return (error);
872da6c28aaSamw
873da6c28aaSamw pdvp = gfs_file_parent(tdvp);
874da6c28aaSamw error = VOP_SETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
875da6c28aaSamw return (error);
876da6c28aaSamw }
877da6c28aaSamw
878da6c28aaSamw static int
xattr_dir_realdir(vnode_t * dvp,vnode_t ** realdvp,int lookup_flags,cred_t * cr,caller_context_t * ct)879da6c28aaSamw xattr_dir_realdir(vnode_t *dvp, vnode_t **realdvp, int lookup_flags,
880da6c28aaSamw cred_t *cr, caller_context_t *ct)
881da6c28aaSamw {
882da6c28aaSamw vnode_t *pvp;
883da6c28aaSamw int error;
884da6c28aaSamw struct pathname pn;
885da6c28aaSamw char *startnm = "";
886da6c28aaSamw
887da6c28aaSamw *realdvp = NULL;
888da6c28aaSamw
889da6c28aaSamw pvp = gfs_file_parent(dvp);
890da6c28aaSamw
891da6c28aaSamw error = pn_get(startnm, UIO_SYSSPACE, &pn);
892da6c28aaSamw if (error) {
893da6c28aaSamw VN_RELE(pvp);
894da6c28aaSamw return (error);
895da6c28aaSamw }
896da6c28aaSamw
897da6c28aaSamw /*
898da6c28aaSamw * Set the LOOKUP_HAVE_SYSATTR_DIR flag so that we don't get into an
899da6c28aaSamw * infinite loop with fop_lookup calling back to xattr_dir_lookup.
900da6c28aaSamw */
901da6c28aaSamw lookup_flags |= LOOKUP_HAVE_SYSATTR_DIR;
902da6c28aaSamw error = VOP_LOOKUP(pvp, startnm, realdvp, &pn, lookup_flags,
903da6c28aaSamw rootvp, cr, ct, NULL, NULL);
904da6c28aaSamw pn_free(&pn);
905da6c28aaSamw
906da6c28aaSamw return (error);
907da6c28aaSamw }
908da6c28aaSamw
909da6c28aaSamw /* ARGSUSED */
910da6c28aaSamw static int
xattr_dir_open(vnode_t ** vpp,int flags,cred_t * cr,caller_context_t * ct)911da6c28aaSamw xattr_dir_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
912da6c28aaSamw {
913da6c28aaSamw if (flags & FWRITE) {
914da6c28aaSamw return (EACCES);
915da6c28aaSamw }
916da6c28aaSamw
917da6c28aaSamw return (0);
918da6c28aaSamw }
919da6c28aaSamw
920da6c28aaSamw /* ARGSUSED */
921da6c28aaSamw static int
xattr_dir_close(vnode_t * vpp,int flags,int count,offset_t off,cred_t * cr,caller_context_t * ct)922da6c28aaSamw xattr_dir_close(vnode_t *vpp, int flags, int count, offset_t off, cred_t *cr,
923da6c28aaSamw caller_context_t *ct)
924da6c28aaSamw {
925da6c28aaSamw return (0);
926da6c28aaSamw }
927da6c28aaSamw
92800ba712dSGarima Tripathi /*
92900ba712dSGarima Tripathi * Retrieve the attributes on an xattr directory. If there is a "real"
93000ba712dSGarima Tripathi * xattr directory, use that. Otherwise, get the attributes (represented
93100ba712dSGarima Tripathi * by PARENT_ATTRMASK) from the "parent" node and fill in the rest. Note
93200ba712dSGarima Tripathi * that VOP_GETATTR() could turn off bits in the va_mask.
93300ba712dSGarima Tripathi */
93400ba712dSGarima Tripathi
93500ba712dSGarima Tripathi #define PARENT_ATTRMASK (AT_UID|AT_GID|AT_RDEV|AT_CTIME|AT_MTIME)
93600ba712dSGarima Tripathi
937da6c28aaSamw /* ARGSUSED */
938da6c28aaSamw static int
xattr_dir_getattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)939da6c28aaSamw xattr_dir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
940da6c28aaSamw caller_context_t *ct)
941da6c28aaSamw {
942da6c28aaSamw timestruc_t now;
943da6c28aaSamw vnode_t *pvp;
944da6c28aaSamw int error;
945da6c28aaSamw
946da6c28aaSamw error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR, cr, ct);
947da6c28aaSamw if (error == 0) {
948da6c28aaSamw error = VOP_GETATTR(pvp, vap, 0, cr, ct);
949da6c28aaSamw VN_RELE(pvp);
950da6c28aaSamw if (error) {
951da6c28aaSamw return (error);
952da6c28aaSamw }
953da6c28aaSamw vap->va_nlink += XATTRDIR_NENTS;
954da6c28aaSamw vap->va_size += XATTRDIR_NENTS;
955da6c28aaSamw return (0);
956da6c28aaSamw }
957da6c28aaSamw
958da6c28aaSamw /*
959da6c28aaSamw * There is no real xattr directory. Cobble together
96000ba712dSGarima Tripathi * an entry using info from the parent object (if needed)
96100ba712dSGarima Tripathi * plus information common to all xattrs.
962da6c28aaSamw */
96300ba712dSGarima Tripathi if (vap->va_mask & PARENT_ATTRMASK) {
96400ba712dSGarima Tripathi vattr_t pvattr;
96500ba712dSGarima Tripathi uint_t off_bits;
96600ba712dSGarima Tripathi
967da6c28aaSamw pvp = gfs_file_parent(vp);
968da6c28aaSamw (void) memset(&pvattr, 0, sizeof (pvattr));
96900ba712dSGarima Tripathi pvattr.va_mask = PARENT_ATTRMASK;
970da6c28aaSamw error = VOP_GETATTR(pvp, &pvattr, 0, cr, ct);
971da6c28aaSamw if (error) {
972da6c28aaSamw return (error);
973da6c28aaSamw }
97400ba712dSGarima Tripathi
97500ba712dSGarima Tripathi /*
97600ba712dSGarima Tripathi * VOP_GETATTR() might have turned off some bits in
97700ba712dSGarima Tripathi * pvattr.va_mask. This means that the underlying
97800ba712dSGarima Tripathi * file system couldn't process those attributes.
97900ba712dSGarima Tripathi * We need to make sure those bits get turned off
98000ba712dSGarima Tripathi * in the vattr_t structure that gets passed back
98100ba712dSGarima Tripathi * to the caller. Figure out which bits were turned
98200ba712dSGarima Tripathi * off (if any) then set pvattr.va_mask before it
98300ba712dSGarima Tripathi * gets copied to the vattr_t that the caller sees.
98400ba712dSGarima Tripathi */
98500ba712dSGarima Tripathi off_bits = (pvattr.va_mask ^ PARENT_ATTRMASK) & PARENT_ATTRMASK;
98600ba712dSGarima Tripathi pvattr.va_mask = vap->va_mask & ~off_bits;
987da6c28aaSamw *vap = pvattr;
98800ba712dSGarima Tripathi }
98900ba712dSGarima Tripathi
990da6c28aaSamw vap->va_type = VDIR;
991da6c28aaSamw vap->va_mode = MAKEIMODE(vap->va_type, S_ISVTX | 0777);
992da6c28aaSamw vap->va_fsid = vp->v_vfsp->vfs_dev;
993da6c28aaSamw vap->va_nodeid = gfs_file_inode(vp);
994da6c28aaSamw vap->va_nlink = XATTRDIR_NENTS+2;
995da6c28aaSamw vap->va_size = vap->va_nlink;
996da6c28aaSamw gethrestime(&now);
997da6c28aaSamw vap->va_atime = now;
998da6c28aaSamw vap->va_blksize = 0;
999da6c28aaSamw vap->va_nblocks = 0;
1000da6c28aaSamw vap->va_seq = 0;
1001da6c28aaSamw return (0);
1002da6c28aaSamw }
1003da6c28aaSamw
1004da6c28aaSamw static int
xattr_dir_setattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)1005da6c28aaSamw xattr_dir_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
1006da6c28aaSamw caller_context_t *ct)
1007da6c28aaSamw {
1008da6c28aaSamw vnode_t *realvp;
1009da6c28aaSamw int error;
1010da6c28aaSamw
1011da6c28aaSamw /*
1012da6c28aaSamw * If there is a real xattr directory, do the setattr there.
1013da6c28aaSamw * Otherwise, just return success. The GFS directory is transient,
1014da6c28aaSamw * and any setattr changes can disappear anyway.
1015da6c28aaSamw */
1016da6c28aaSamw error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
1017da6c28aaSamw if (error == 0) {
1018da6c28aaSamw error = VOP_SETATTR(realvp, vap, flags, cr, ct);
1019da6c28aaSamw VN_RELE(realvp);
1020da6c28aaSamw }
1021da6c28aaSamw if (error == ENOENT) {
1022da6c28aaSamw error = 0;
1023da6c28aaSamw }
1024da6c28aaSamw return (error);
1025da6c28aaSamw }
1026da6c28aaSamw
1027da6c28aaSamw /* ARGSUSED */
1028da6c28aaSamw static int
xattr_dir_access(vnode_t * vp,int mode,int flags,cred_t * cr,caller_context_t * ct)1029da6c28aaSamw xattr_dir_access(vnode_t *vp, int mode, int flags, cred_t *cr,
1030da6c28aaSamw caller_context_t *ct)
1031da6c28aaSamw {
1032da6c28aaSamw int error;
1033da6c28aaSamw vnode_t *realvp = NULL;
1034da6c28aaSamw
1035da6c28aaSamw if (mode & VWRITE) {
1036da6c28aaSamw return (EACCES);
1037da6c28aaSamw }
1038da6c28aaSamw
1039da6c28aaSamw error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
1040da6c28aaSamw
1041da6c28aaSamw if (realvp)
1042da6c28aaSamw VN_RELE(realvp);
1043da6c28aaSamw
1044da6c28aaSamw /*
1045da6c28aaSamw * No real xattr dir isn't an error
1046da6c28aaSamw * an error of EINVAL indicates attributes on attributes
1047da6c28aaSamw * are not supported. In that case just allow access to the
1048da6c28aaSamw * transient directory.
1049da6c28aaSamw */
1050da6c28aaSamw return ((error == ENOENT || error == EINVAL) ? 0 : error);
1051da6c28aaSamw }
1052da6c28aaSamw
1053da6c28aaSamw static int
xattr_dir_create(vnode_t * dvp,char * name,vattr_t * vap,vcexcl_t excl,int mode,vnode_t ** vpp,cred_t * cr,int flag,caller_context_t * ct,vsecattr_t * vsecp)1054da6c28aaSamw xattr_dir_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl,
1055da6c28aaSamw int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
1056da6c28aaSamw vsecattr_t *vsecp)
1057da6c28aaSamw {
1058da6c28aaSamw vnode_t *pvp;
1059da6c28aaSamw int error;
1060da6c28aaSamw
1061da6c28aaSamw *vpp = NULL;
1062da6c28aaSamw
1063da6c28aaSamw /*
1064da6c28aaSamw * Don't allow creation of extended attributes with sysattr names.
1065da6c28aaSamw */
1066da6c28aaSamw if (is_sattr_name(name)) {
1067ab04eb8eStimh return (gfs_dir_lookup(dvp, name, vpp, cr, 0, NULL, NULL));
1068da6c28aaSamw }
1069da6c28aaSamw
1070da6c28aaSamw error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
1071da6c28aaSamw cr, ct);
1072da6c28aaSamw if (error == 0) {
1073da6c28aaSamw error = VOP_CREATE(pvp, name, vap, excl, mode, vpp, cr, flag,
1074da6c28aaSamw ct, vsecp);
1075da6c28aaSamw VN_RELE(pvp);
1076da6c28aaSamw }
1077da6c28aaSamw return (error);
1078da6c28aaSamw }
1079da6c28aaSamw
1080da6c28aaSamw static int
xattr_dir_remove(vnode_t * dvp,char * name,cred_t * cr,caller_context_t * ct,int flags)1081da6c28aaSamw xattr_dir_remove(vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct,
1082da6c28aaSamw int flags)
1083da6c28aaSamw {
1084da6c28aaSamw vnode_t *pvp;
1085da6c28aaSamw int error;
1086da6c28aaSamw
1087da6c28aaSamw if (is_sattr_name(name)) {
1088da6c28aaSamw return (EACCES);
1089da6c28aaSamw }
1090da6c28aaSamw
1091da6c28aaSamw error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
1092da6c28aaSamw if (error == 0) {
1093da6c28aaSamw error = VOP_REMOVE(pvp, name, cr, ct, flags);
1094da6c28aaSamw VN_RELE(pvp);
1095da6c28aaSamw }
1096da6c28aaSamw return (error);
1097da6c28aaSamw }
1098da6c28aaSamw
1099da6c28aaSamw static int
xattr_dir_link(vnode_t * tdvp,vnode_t * svp,char * name,cred_t * cr,caller_context_t * ct,int flags)1100da6c28aaSamw xattr_dir_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr,
1101da6c28aaSamw caller_context_t *ct, int flags)
1102da6c28aaSamw {
1103da6c28aaSamw vnode_t *pvp;
1104da6c28aaSamw int error;
1105da6c28aaSamw
1106da6c28aaSamw if (svp->v_flag & V_SYSATTR) {
1107da6c28aaSamw return (EINVAL);
1108da6c28aaSamw }
1109da6c28aaSamw
1110da6c28aaSamw error = xattr_dir_realdir(tdvp, &pvp, LOOKUP_XATTR, cr, ct);
1111da6c28aaSamw if (error == 0) {
1112da6c28aaSamw error = VOP_LINK(pvp, svp, name, cr, ct, flags);
1113da6c28aaSamw VN_RELE(pvp);
1114da6c28aaSamw }
1115da6c28aaSamw return (error);
1116da6c28aaSamw }
1117da6c28aaSamw
1118da6c28aaSamw static int
xattr_dir_rename(vnode_t * sdvp,char * snm,vnode_t * tdvp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)1119da6c28aaSamw xattr_dir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
1120da6c28aaSamw cred_t *cr, caller_context_t *ct, int flags)
1121da6c28aaSamw {
1122da6c28aaSamw vnode_t *spvp, *tpvp;
1123da6c28aaSamw int error;
1124da6c28aaSamw int held_tgt;
1125da6c28aaSamw
1126da6c28aaSamw if (is_sattr_name(snm) || is_sattr_name(tnm))
1127da6c28aaSamw return (xattr_copy(sdvp, snm, tdvp, tnm, cr, ct));
1128da6c28aaSamw /*
1129da6c28aaSamw * We know that sdvp is a GFS dir, or we wouldn't be here.
1130da6c28aaSamw * Get the real unnamed directory.
1131da6c28aaSamw */
1132da6c28aaSamw error = xattr_dir_realdir(sdvp, &spvp, LOOKUP_XATTR, cr, ct);
1133da6c28aaSamw if (error) {
1134da6c28aaSamw return (error);
1135da6c28aaSamw }
1136da6c28aaSamw
1137da6c28aaSamw if (sdvp == tdvp) {
1138da6c28aaSamw /*
1139da6c28aaSamw * If the source and target are the same GFS directory, the
1140da6c28aaSamw * underlying unnamed source and target dir will be the same.
1141da6c28aaSamw */
1142da6c28aaSamw tpvp = spvp;
1143da6c28aaSamw VN_HOLD(tpvp);
1144da6c28aaSamw held_tgt = 1;
1145da6c28aaSamw } else if (tdvp->v_flag & V_SYSATTR) {
1146da6c28aaSamw /*
1147da6c28aaSamw * If the target dir is a different GFS directory,
1148da6c28aaSamw * find its underlying unnamed dir.
1149da6c28aaSamw */
1150da6c28aaSamw error = xattr_dir_realdir(tdvp, &tpvp, LOOKUP_XATTR, cr, ct);
1151da6c28aaSamw if (error) {
1152da6c28aaSamw VN_RELE(spvp);
1153da6c28aaSamw return (error);
1154da6c28aaSamw }
1155da6c28aaSamw held_tgt = 1;
1156da6c28aaSamw } else {
1157da6c28aaSamw /*
1158da6c28aaSamw * Target dir is outside of GFS, pass it on through.
1159da6c28aaSamw */
1160da6c28aaSamw tpvp = tdvp;
1161da6c28aaSamw held_tgt = 0;
1162da6c28aaSamw }
1163da6c28aaSamw
1164da6c28aaSamw error = VOP_RENAME(spvp, snm, tpvp, tnm, cr, ct, flags);
1165da6c28aaSamw
1166da6c28aaSamw if (held_tgt) {
1167da6c28aaSamw VN_RELE(tpvp);
1168da6c28aaSamw }
1169da6c28aaSamw VN_RELE(spvp);
1170da6c28aaSamw
1171da6c28aaSamw return (error);
1172da6c28aaSamw }
1173da6c28aaSamw
1174b38f0970Sck153898 /*
1175b38f0970Sck153898 * readdir_xattr_casecmp: given a system attribute name, see if there
1176b38f0970Sck153898 * is a real xattr with the same normalized name.
1177b38f0970Sck153898 */
1178b38f0970Sck153898 static int
readdir_xattr_casecmp(vnode_t * dvp,char * nm,cred_t * cr,caller_context_t * ct,int * eflags)1179b38f0970Sck153898 readdir_xattr_casecmp(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
1180b38f0970Sck153898 int *eflags)
1181b38f0970Sck153898 {
1182b38f0970Sck153898 int error;
1183b38f0970Sck153898 vnode_t *vp;
1184b38f0970Sck153898 struct pathname pn;
1185b38f0970Sck153898
1186b38f0970Sck153898 *eflags = 0;
1187b38f0970Sck153898
1188b38f0970Sck153898 error = pn_get(nm, UIO_SYSSPACE, &pn);
1189b38f0970Sck153898 if (error == 0) {
1190ab04eb8eStimh error = VOP_LOOKUP(dvp, nm, &vp, &pn,
1191ab04eb8eStimh FIGNORECASE, rootvp, cr, ct, NULL, NULL);
1192b38f0970Sck153898 if (error == 0) {
1193b38f0970Sck153898 *eflags = ED_CASE_CONFLICT;
1194b38f0970Sck153898 VN_RELE(vp);
1195b38f0970Sck153898 } else if (error == ENOENT) {
1196b38f0970Sck153898 error = 0;
1197b38f0970Sck153898 }
1198b38f0970Sck153898 pn_free(&pn);
1199b38f0970Sck153898 }
1200b38f0970Sck153898
1201b38f0970Sck153898 return (error);
1202b38f0970Sck153898 }
1203b38f0970Sck153898
1204da6c28aaSamw static int
xattr_dir_readdir(vnode_t * dvp,uio_t * uiop,cred_t * cr,int * eofp,caller_context_t * ct,int flags)1205da6c28aaSamw xattr_dir_readdir(vnode_t *dvp, uio_t *uiop, cred_t *cr, int *eofp,
1206da6c28aaSamw caller_context_t *ct, int flags)
1207da6c28aaSamw {
1208da6c28aaSamw vnode_t *pvp;
1209da6c28aaSamw int error;
1210f5f5959bSck153898 int local_eof;
1211da6c28aaSamw int reset_off = 0;
1212da6c28aaSamw int has_xattrs = 0;
1213da6c28aaSamw
1214da6c28aaSamw if (eofp == NULL) {
1215da6c28aaSamw eofp = &local_eof;
1216da6c28aaSamw }
1217f5f5959bSck153898 *eofp = 0;
1218da6c28aaSamw
1219da6c28aaSamw /*
1220da6c28aaSamw * See if there is a real extended attribute directory.
1221da6c28aaSamw */
1222da6c28aaSamw error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
1223da6c28aaSamw if (error == 0) {
1224da6c28aaSamw has_xattrs = 1;
1225da6c28aaSamw }
1226da6c28aaSamw
1227da6c28aaSamw /*
1228da6c28aaSamw * Start by reading up the static entries.
1229da6c28aaSamw */
1230da6c28aaSamw if (uiop->uio_loffset == 0) {
1231b38f0970Sck153898 ino64_t pino, ino;
1232b38f0970Sck153898 offset_t off;
1233b38f0970Sck153898 gfs_dir_t *dp = dvp->v_data;
1234b38f0970Sck153898 gfs_readdir_state_t gstate;
1235b38f0970Sck153898
1236da6c28aaSamw if (has_xattrs) {
1237da6c28aaSamw /*
1238da6c28aaSamw * If there is a real xattr dir, skip . and ..
1239da6c28aaSamw * in the GFS dir. We'll pick them up below
1240da6c28aaSamw * when we call into the underlying fs.
1241da6c28aaSamw */
1242da6c28aaSamw uiop->uio_loffset = GFS_STATIC_ENTRY_OFFSET;
1243da6c28aaSamw }
1244b38f0970Sck153898 error = gfs_get_parent_ino(dvp, cr, ct, &pino, &ino);
1245b38f0970Sck153898 if (error == 0) {
1246b38f0970Sck153898 error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1,
1247b38f0970Sck153898 uiop, pino, ino, flags);
1248da6c28aaSamw }
1249b38f0970Sck153898 if (error) {
1250b38f0970Sck153898 if (has_xattrs)
1251b38f0970Sck153898 VN_RELE(pvp);
1252da6c28aaSamw return (error);
1253da6c28aaSamw }
1254b38f0970Sck153898
1255b38f0970Sck153898 while ((error = gfs_readdir_pred(&gstate, uiop, &off)) == 0 &&
1256b38f0970Sck153898 !*eofp) {
1257b38f0970Sck153898 if (off >= 0 && off < dp->gfsd_nstatic) {
1258ab04eb8eStimh int eflags;
1259b38f0970Sck153898
1260b38f0970Sck153898 /*
1261b38f0970Sck153898 * Check to see if this sysattr set name has a
1262b38f0970Sck153898 * case-insensitive conflict with a real xattr
1263b38f0970Sck153898 * name.
1264b38f0970Sck153898 */
1265ab04eb8eStimh eflags = 0;
1266b38f0970Sck153898 if ((flags & V_RDDIR_ENTFLAGS) && has_xattrs) {
1267b38f0970Sck153898 error = readdir_xattr_casecmp(pvp,
1268b38f0970Sck153898 dp->gfsd_static[off].gfse_name,
1269b38f0970Sck153898 cr, ct, &eflags);
1270b38f0970Sck153898 if (error)
1271b38f0970Sck153898 break;
1272b38f0970Sck153898 }
1273b38f0970Sck153898 ino = dp->gfsd_inode(dvp, off);
1274b38f0970Sck153898
1275b38f0970Sck153898 error = gfs_readdir_emit(&gstate, uiop, off,
1276b38f0970Sck153898 ino, dp->gfsd_static[off].gfse_name,
1277b38f0970Sck153898 eflags);
1278b38f0970Sck153898 if (error)
1279b38f0970Sck153898 break;
1280b38f0970Sck153898 } else {
1281b38f0970Sck153898 *eofp = 1;
1282b38f0970Sck153898 }
1283b38f0970Sck153898 }
1284b38f0970Sck153898
1285b38f0970Sck153898 error = gfs_readdir_fini(&gstate, error, eofp, *eofp);
1286b38f0970Sck153898 if (error) {
1287b38f0970Sck153898 if (has_xattrs)
1288b38f0970Sck153898 VN_RELE(pvp);
1289b38f0970Sck153898 return (error);
1290b38f0970Sck153898 }
1291b38f0970Sck153898
1292da6c28aaSamw /*
1293da6c28aaSamw * We must read all of the static entries in the first
1294da6c28aaSamw * call. Otherwise we won't know if uio_loffset in a
1295da6c28aaSamw * subsequent call refers to the static entries or to those
1296da6c28aaSamw * in an underlying fs.
1297da6c28aaSamw */
1298e802abbdSTim Haley if (*eofp == 0)
1299e802abbdSTim Haley return (EINVAL);
1300da6c28aaSamw reset_off = 1;
1301da6c28aaSamw }
1302da6c28aaSamw
1303da6c28aaSamw if (!has_xattrs) {
1304da6c28aaSamw *eofp = 1;
1305da6c28aaSamw return (0);
1306da6c28aaSamw }
1307da6c28aaSamw
1308da6c28aaSamw *eofp = 0;
1309da6c28aaSamw if (reset_off) {
1310da6c28aaSamw uiop->uio_loffset = 0;
1311da6c28aaSamw }
1312da6c28aaSamw (void) VOP_RWLOCK(pvp, V_WRITELOCK_FALSE, NULL);
1313da6c28aaSamw error = VOP_READDIR(pvp, uiop, cr, eofp, ct, flags);
1314da6c28aaSamw VOP_RWUNLOCK(pvp, V_WRITELOCK_FALSE, NULL);
1315da6c28aaSamw VN_RELE(pvp);
1316da6c28aaSamw
1317da6c28aaSamw return (error);
1318da6c28aaSamw }
1319da6c28aaSamw
1320da6c28aaSamw /* ARGSUSED */
1321da6c28aaSamw static void
xattr_dir_inactive(vnode_t * vp,cred_t * cr,caller_context_t * ct)1322da6c28aaSamw xattr_dir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
1323da6c28aaSamw {
1324da6c28aaSamw gfs_file_t *fp;
132568469adeSMark Shellenbaum xattr_dir_t *xattr_dir;
1326da6c28aaSamw
132768469adeSMark Shellenbaum mutex_enter(&vp->v_lock);
132868469adeSMark Shellenbaum xattr_dir = vp->v_data;
132968469adeSMark Shellenbaum if (xattr_dir->xattr_realvp) {
133068469adeSMark Shellenbaum VN_RELE(xattr_dir->xattr_realvp);
133168469adeSMark Shellenbaum xattr_dir->xattr_realvp = NULL;
133268469adeSMark Shellenbaum }
133368469adeSMark Shellenbaum mutex_exit(&vp->v_lock);
1334da6c28aaSamw fp = gfs_dir_inactive(vp);
1335da6c28aaSamw if (fp != NULL) {
1336da6c28aaSamw kmem_free(fp, fp->gfs_size);
1337da6c28aaSamw }
1338da6c28aaSamw }
1339da6c28aaSamw
1340da6c28aaSamw static int
xattr_dir_pathconf(vnode_t * vp,int cmd,ulong_t * valp,cred_t * cr,caller_context_t * ct)1341da6c28aaSamw xattr_dir_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
1342da6c28aaSamw caller_context_t *ct)
1343da6c28aaSamw {
1344da6c28aaSamw switch (cmd) {
1345da6c28aaSamw case _PC_XATTR_EXISTS:
1346da6c28aaSamw case _PC_SATTR_ENABLED:
1347da6c28aaSamw case _PC_SATTR_EXISTS:
1348da6c28aaSamw *valp = 0;
1349da6c28aaSamw return (0);
1350da6c28aaSamw default:
1351da6c28aaSamw return (fs_pathconf(vp, cmd, valp, cr, ct));
1352da6c28aaSamw }
1353da6c28aaSamw }
1354da6c28aaSamw
135568469adeSMark Shellenbaum /* ARGSUSED */
135668469adeSMark Shellenbaum static int
xattr_dir_realvp(vnode_t * vp,vnode_t ** realvp,caller_context_t * ct)135768469adeSMark Shellenbaum xattr_dir_realvp(vnode_t *vp, vnode_t **realvp, caller_context_t *ct)
135868469adeSMark Shellenbaum {
135968469adeSMark Shellenbaum xattr_dir_t *xattr_dir;
136068469adeSMark Shellenbaum
136168469adeSMark Shellenbaum mutex_enter(&vp->v_lock);
136268469adeSMark Shellenbaum xattr_dir = vp->v_data;
136368469adeSMark Shellenbaum if (xattr_dir->xattr_realvp) {
136468469adeSMark Shellenbaum *realvp = xattr_dir->xattr_realvp;
136568469adeSMark Shellenbaum mutex_exit(&vp->v_lock);
13663fece860SMark Shellenbaum return (0);
13673fece860SMark Shellenbaum } else {
13683fece860SMark Shellenbaum vnode_t *xdvp;
13693fece860SMark Shellenbaum int error;
13703fece860SMark Shellenbaum
13713fece860SMark Shellenbaum mutex_exit(&vp->v_lock);
13723fece860SMark Shellenbaum if ((error = xattr_dir_realdir(vp, &xdvp,
13733fece860SMark Shellenbaum LOOKUP_XATTR, kcred, NULL)) == 0) {
13743fece860SMark Shellenbaum /*
13753fece860SMark Shellenbaum * verify we aren't racing with another thread
13763fece860SMark Shellenbaum * to find the xattr_realvp
13773fece860SMark Shellenbaum */
13783fece860SMark Shellenbaum mutex_enter(&vp->v_lock);
13793fece860SMark Shellenbaum if (xattr_dir->xattr_realvp == NULL) {
13803fece860SMark Shellenbaum xattr_dir->xattr_realvp = xdvp;
13813fece860SMark Shellenbaum *realvp = xdvp;
13823fece860SMark Shellenbaum mutex_exit(&vp->v_lock);
13833fece860SMark Shellenbaum } else {
13843fece860SMark Shellenbaum *realvp = xattr_dir->xattr_realvp;
13853fece860SMark Shellenbaum mutex_exit(&vp->v_lock);
13863fece860SMark Shellenbaum VN_RELE(xdvp);
13873fece860SMark Shellenbaum }
13883fece860SMark Shellenbaum }
138968469adeSMark Shellenbaum return (error);
139068469adeSMark Shellenbaum }
13913fece860SMark Shellenbaum }
139268469adeSMark Shellenbaum
1393da6c28aaSamw static const fs_operation_def_t xattr_dir_tops[] = {
1394da6c28aaSamw { VOPNAME_OPEN, { .vop_open = xattr_dir_open } },
1395da6c28aaSamw { VOPNAME_CLOSE, { .vop_close = xattr_dir_close } },
1396da6c28aaSamw { VOPNAME_IOCTL, { .error = fs_inval } },
1397da6c28aaSamw { VOPNAME_GETATTR, { .vop_getattr = xattr_dir_getattr } },
1398da6c28aaSamw { VOPNAME_SETATTR, { .vop_setattr = xattr_dir_setattr } },
1399da6c28aaSamw { VOPNAME_ACCESS, { .vop_access = xattr_dir_access } },
1400da6c28aaSamw { VOPNAME_READDIR, { .vop_readdir = xattr_dir_readdir } },
1401da6c28aaSamw { VOPNAME_LOOKUP, { .vop_lookup = gfs_vop_lookup } },
1402da6c28aaSamw { VOPNAME_CREATE, { .vop_create = xattr_dir_create } },
1403da6c28aaSamw { VOPNAME_REMOVE, { .vop_remove = xattr_dir_remove } },
1404da6c28aaSamw { VOPNAME_LINK, { .vop_link = xattr_dir_link } },
1405da6c28aaSamw { VOPNAME_RENAME, { .vop_rename = xattr_dir_rename } },
1406da6c28aaSamw { VOPNAME_MKDIR, { .error = fs_inval } },
1407da6c28aaSamw { VOPNAME_SEEK, { .vop_seek = fs_seek } },
1408da6c28aaSamw { VOPNAME_INACTIVE, { .vop_inactive = xattr_dir_inactive } },
1409da6c28aaSamw { VOPNAME_FID, { .vop_fid = xattr_common_fid } },
1410da6c28aaSamw { VOPNAME_PATHCONF, { .vop_pathconf = xattr_dir_pathconf } },
141168469adeSMark Shellenbaum { VOPNAME_REALVP, { .vop_realvp = xattr_dir_realvp } },
1412da6c28aaSamw { NULL, NULL }
1413da6c28aaSamw };
1414da6c28aaSamw
1415da6c28aaSamw static gfs_opsvec_t xattr_opsvec[] = {
1416da6c28aaSamw { "xattr dir", xattr_dir_tops, &xattr_dir_ops },
1417da6c28aaSamw { "system attributes", xattr_file_tops, &xattr_file_ops },
1418da6c28aaSamw { NULL, NULL, NULL }
1419da6c28aaSamw };
1420da6c28aaSamw
1421da6c28aaSamw static int
xattr_lookup_cb(vnode_t * vp,const char * nm,vnode_t ** vpp,ino64_t * inop,cred_t * cr,int flags,int * deflags,pathname_t * rpnp)1422da6c28aaSamw xattr_lookup_cb(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop,
1423ab04eb8eStimh cred_t *cr, int flags, int *deflags, pathname_t *rpnp)
1424da6c28aaSamw {
1425da6c28aaSamw vnode_t *pvp;
1426da6c28aaSamw struct pathname pn;
1427da6c28aaSamw int error;
1428da6c28aaSamw
1429da6c28aaSamw *vpp = NULL;
1430da6c28aaSamw *inop = 0;
1431da6c28aaSamw
1432da6c28aaSamw error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
1433da6c28aaSamw cr, NULL);
1434da6c28aaSamw
1435da6c28aaSamw /*
1436da6c28aaSamw * Return ENOENT for EACCES requests during lookup. Once an
1437da6c28aaSamw * attribute create is attempted EACCES will be returned.
1438da6c28aaSamw */
1439da6c28aaSamw if (error) {
1440da6c28aaSamw if (error == EACCES)
1441da6c28aaSamw return (ENOENT);
1442da6c28aaSamw return (error);
1443da6c28aaSamw }
1444da6c28aaSamw
1445da6c28aaSamw error = pn_get((char *)nm, UIO_SYSSPACE, &pn);
1446da6c28aaSamw if (error == 0) {
1447ab04eb8eStimh error = VOP_LOOKUP(pvp, (char *)nm, vpp, &pn, flags, rootvp,
1448ab04eb8eStimh cr, NULL, deflags, rpnp);
1449da6c28aaSamw pn_free(&pn);
1450da6c28aaSamw }
1451da6c28aaSamw VN_RELE(pvp);
1452da6c28aaSamw
1453da6c28aaSamw return (error);
1454da6c28aaSamw }
1455da6c28aaSamw
1456da6c28aaSamw /* ARGSUSED */
1457da6c28aaSamw static ino64_t
xattrdir_do_ino(vnode_t * vp,int index)1458da6c28aaSamw xattrdir_do_ino(vnode_t *vp, int index)
1459da6c28aaSamw {
1460da6c28aaSamw /*
1461da6c28aaSamw * We use index 0 for the directory fid. Start
1462da6c28aaSamw * the file numbering at 1.
1463da6c28aaSamw */
1464da6c28aaSamw return ((ino64_t)index+1);
1465da6c28aaSamw }
1466da6c28aaSamw
1467da6c28aaSamw void
xattr_init(void)1468da6c28aaSamw xattr_init(void)
1469da6c28aaSamw {
1470da6c28aaSamw VERIFY(gfs_make_opsvec(xattr_opsvec) == 0);
1471da6c28aaSamw }
1472da6c28aaSamw
1473da6c28aaSamw int
xattr_dir_lookup(vnode_t * dvp,vnode_t ** vpp,int flags,cred_t * cr)1474da6c28aaSamw xattr_dir_lookup(vnode_t *dvp, vnode_t **vpp, int flags, cred_t *cr)
1475da6c28aaSamw {
1476da6c28aaSamw int error = 0;
1477da6c28aaSamw
1478da6c28aaSamw *vpp = NULL;
1479da6c28aaSamw
1480da6c28aaSamw if (dvp->v_type != VDIR && dvp->v_type != VREG)
1481da6c28aaSamw return (EINVAL);
1482da6c28aaSamw
1483da6c28aaSamw mutex_enter(&dvp->v_lock);
1484da6c28aaSamw
1485da6c28aaSamw /*
1486da6c28aaSamw * If we're already in sysattr space, don't allow creation
1487da6c28aaSamw * of another level of sysattrs.
1488da6c28aaSamw */
1489da6c28aaSamw if (dvp->v_flag & V_SYSATTR) {
1490da6c28aaSamw mutex_exit(&dvp->v_lock);
1491da6c28aaSamw return (EINVAL);
1492da6c28aaSamw }
1493da6c28aaSamw
1494da6c28aaSamw if (dvp->v_xattrdir != NULL) {
1495da6c28aaSamw *vpp = dvp->v_xattrdir;
1496da6c28aaSamw VN_HOLD(*vpp);
1497da6c28aaSamw } else {
1498da6c28aaSamw ulong_t val;
1499da6c28aaSamw int xattrs_allowed = dvp->v_vfsp->vfs_flag & VFS_XATTR;
1500da6c28aaSamw int sysattrs_allowed = 1;
1501da6c28aaSamw
1502da6c28aaSamw /*
1503da6c28aaSamw * We have to drop the lock on dvp. gfs_dir_create will
1504da6c28aaSamw * grab it for a VN_HOLD.
1505da6c28aaSamw */
1506da6c28aaSamw mutex_exit(&dvp->v_lock);
1507da6c28aaSamw
1508da6c28aaSamw /*
1509da6c28aaSamw * If dvp allows xattr creation, but not sysattr
1510da6c28aaSamw * creation, return the real xattr dir vp. We can't
1511da6c28aaSamw * use the vfs feature mask here because _PC_SATTR_ENABLED
1512da6c28aaSamw * has vnode-level granularity (e.g. .zfs).
1513da6c28aaSamw */
1514da6c28aaSamw error = VOP_PATHCONF(dvp, _PC_SATTR_ENABLED, &val, cr, NULL);
1515da6c28aaSamw if (error != 0 || val == 0)
1516da6c28aaSamw sysattrs_allowed = 0;
1517da6c28aaSamw
1518da6c28aaSamw if (!xattrs_allowed && !sysattrs_allowed)
1519da6c28aaSamw return (EINVAL);
1520da6c28aaSamw
1521da6c28aaSamw if (!sysattrs_allowed) {
1522da6c28aaSamw struct pathname pn;
1523da6c28aaSamw char *nm = "";
1524da6c28aaSamw
1525da6c28aaSamw error = pn_get(nm, UIO_SYSSPACE, &pn);
1526da6c28aaSamw if (error)
1527da6c28aaSamw return (error);
1528da6c28aaSamw error = VOP_LOOKUP(dvp, nm, vpp, &pn,
1529da6c28aaSamw flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL,
1530da6c28aaSamw NULL, NULL);
1531da6c28aaSamw pn_free(&pn);
1532da6c28aaSamw return (error);
1533da6c28aaSamw }
1534da6c28aaSamw
1535da6c28aaSamw /*
1536da6c28aaSamw * Note that we act as if we were given CREATE_XATTR_DIR,
1537da6c28aaSamw * but only for creation of the GFS directory.
1538da6c28aaSamw */
1539da6c28aaSamw *vpp = gfs_dir_create(
154068469adeSMark Shellenbaum sizeof (xattr_dir_t), dvp, xattr_dir_ops, xattr_dirents,
1541da6c28aaSamw xattrdir_do_ino, MAXNAMELEN, NULL, xattr_lookup_cb);
1542da6c28aaSamw mutex_enter(&dvp->v_lock);
1543da6c28aaSamw if (dvp->v_xattrdir != NULL) {
1544da6c28aaSamw /*
1545da6c28aaSamw * We lost the race to create the xattr dir.
1546da6c28aaSamw * Destroy this one, use the winner. We can't
1547da6c28aaSamw * just call VN_RELE(*vpp), because the vnode
1548da6c28aaSamw * is only partially initialized.
1549da6c28aaSamw */
1550da6c28aaSamw gfs_dir_t *dp = (*vpp)->v_data;
1551da6c28aaSamw
1552da6c28aaSamw ASSERT((*vpp)->v_count == 1);
1553da6c28aaSamw vn_free(*vpp);
1554da6c28aaSamw
1555da6c28aaSamw mutex_destroy(&dp->gfsd_lock);
1556da6c28aaSamw kmem_free(dp->gfsd_static,
1557da6c28aaSamw dp->gfsd_nstatic * sizeof (gfs_dirent_t));
1558da6c28aaSamw kmem_free(dp, dp->gfsd_file.gfs_size);
1559da6c28aaSamw
1560da6c28aaSamw /*
1561da6c28aaSamw * There is an implied VN_HOLD(dvp) here. We should
1562da6c28aaSamw * be doing a VN_RELE(dvp) to clean up the reference
1563da6c28aaSamw * from *vpp, and then a VN_HOLD(dvp) for the new
1564da6c28aaSamw * reference. Instead, we just leave the count alone.
1565da6c28aaSamw */
1566da6c28aaSamw
1567da6c28aaSamw *vpp = dvp->v_xattrdir;
1568da6c28aaSamw VN_HOLD(*vpp);
1569da6c28aaSamw } else {
1570da6c28aaSamw (*vpp)->v_flag |= (V_XATTRDIR|V_SYSATTR);
1571da6c28aaSamw dvp->v_xattrdir = *vpp;
1572da6c28aaSamw }
1573da6c28aaSamw }
1574da6c28aaSamw mutex_exit(&dvp->v_lock);
1575da6c28aaSamw
1576da6c28aaSamw return (error);
1577da6c28aaSamw }
1578da6c28aaSamw
1579da6c28aaSamw int
xattr_dir_vget(vfs_t * vfsp,vnode_t ** vpp,fid_t * fidp)1580da6c28aaSamw xattr_dir_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
1581da6c28aaSamw {
1582da6c28aaSamw int error;
1583da6c28aaSamw vnode_t *pvp, *dvp;
1584da6c28aaSamw xattr_fid_t *xfidp;
1585da6c28aaSamw struct pathname pn;
1586da6c28aaSamw char *nm;
1587da6c28aaSamw uint16_t orig_len;
1588da6c28aaSamw
1589da6c28aaSamw *vpp = NULL;
1590da6c28aaSamw
1591da6c28aaSamw if (fidp->fid_len < XATTR_FIDSZ)
1592da6c28aaSamw return (EINVAL);
1593da6c28aaSamw
1594da6c28aaSamw xfidp = (xattr_fid_t *)fidp;
1595da6c28aaSamw orig_len = fidp->fid_len;
1596da6c28aaSamw fidp->fid_len = xfidp->parent_len;
1597da6c28aaSamw
1598da6c28aaSamw error = VFS_VGET(vfsp, &pvp, fidp);
1599da6c28aaSamw fidp->fid_len = orig_len;
1600da6c28aaSamw if (error)
1601da6c28aaSamw return (error);
1602da6c28aaSamw
1603da6c28aaSamw /*
1604da6c28aaSamw * Start by getting the GFS sysattr directory. We might need
1605da6c28aaSamw * to recreate it during the VOP_LOOKUP.
1606da6c28aaSamw */
1607da6c28aaSamw nm = "";
1608da6c28aaSamw error = pn_get(nm, UIO_SYSSPACE, &pn);
1609da6c28aaSamw if (error) {
1610da6c28aaSamw VN_RELE(pvp);
1611da6c28aaSamw return (EINVAL);
1612da6c28aaSamw }
1613da6c28aaSamw
1614da6c28aaSamw error = VOP_LOOKUP(pvp, nm, &dvp, &pn, LOOKUP_XATTR|CREATE_XATTR_DIR,
1615da6c28aaSamw rootvp, CRED(), NULL, NULL, NULL);
1616da6c28aaSamw pn_free(&pn);
1617da6c28aaSamw VN_RELE(pvp);
1618da6c28aaSamw if (error)
1619da6c28aaSamw return (error);
1620da6c28aaSamw
1621da6c28aaSamw if (xfidp->dir_offset == 0) {
1622da6c28aaSamw /*
1623da6c28aaSamw * If we were looking for the directory, we're done.
1624da6c28aaSamw */
1625da6c28aaSamw *vpp = dvp;
1626da6c28aaSamw return (0);
1627da6c28aaSamw }
1628da6c28aaSamw
1629da6c28aaSamw if (xfidp->dir_offset > XATTRDIR_NENTS) {
1630da6c28aaSamw VN_RELE(dvp);
1631da6c28aaSamw return (EINVAL);
1632da6c28aaSamw }
1633da6c28aaSamw
1634da6c28aaSamw nm = xattr_dirents[xfidp->dir_offset - 1].gfse_name;
1635da6c28aaSamw
1636da6c28aaSamw error = pn_get(nm, UIO_SYSSPACE, &pn);
1637da6c28aaSamw if (error) {
1638da6c28aaSamw VN_RELE(dvp);
1639da6c28aaSamw return (EINVAL);
1640da6c28aaSamw }
1641da6c28aaSamw
1642da6c28aaSamw error = VOP_LOOKUP(dvp, nm, vpp, &pn, 0, rootvp, CRED(), NULL,
1643da6c28aaSamw NULL, NULL);
1644da6c28aaSamw
1645da6c28aaSamw pn_free(&pn);
1646da6c28aaSamw VN_RELE(dvp);
1647da6c28aaSamw
1648da6c28aaSamw return (error);
1649da6c28aaSamw }
1650