1facf4a8dSllai /*
2facf4a8dSllai * CDDL HEADER START
3facf4a8dSllai *
4facf4a8dSllai * The contents of this file are subject to the terms of the
5facf4a8dSllai * Common Development and Distribution License (the "License").
6facf4a8dSllai * You may not use this file except in compliance with the License.
7facf4a8dSllai *
8facf4a8dSllai * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9facf4a8dSllai * or http://www.opensolaris.org/os/licensing.
10facf4a8dSllai * See the License for the specific language governing permissions
11facf4a8dSllai * and limitations under the License.
12facf4a8dSllai *
13facf4a8dSllai * When distributing Covered Code, include this CDDL HEADER in each
14facf4a8dSllai * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15facf4a8dSllai * If applicable, add the following below this CDDL HEADER, with the
16facf4a8dSllai * fields enclosed by brackets "[]" replaced with your own identifying
17facf4a8dSllai * information: Portions Copyright [yyyy] [name of copyright owner]
18facf4a8dSllai *
19facf4a8dSllai * CDDL HEADER END
20facf4a8dSllai */
21facf4a8dSllai
22facf4a8dSllai /*
230fbb751dSJohn Levon * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
2445b17475SAlex Wilson * Copyright 2016, Joyent Inc.
25facf4a8dSllai */
26facf4a8dSllai
27facf4a8dSllai /*
28facf4a8dSllai * This file implements /dev filesystem operations for non-global
29facf4a8dSllai * instances. Three major entry points:
30facf4a8dSllai * devname_profile_update()
31facf4a8dSllai * Update matching rules determining which names to export
32facf4a8dSllai * prof_readdir()
33facf4a8dSllai * Return the list of exported names
34facf4a8dSllai * prof_lookup()
35facf4a8dSllai * Implements lookup
36facf4a8dSllai */
37facf4a8dSllai
38facf4a8dSllai #include <sys/types.h>
39facf4a8dSllai #include <sys/param.h>
40facf4a8dSllai #include <sys/sysmacros.h>
41facf4a8dSllai #include <sys/vnode.h>
42facf4a8dSllai #include <sys/uio.h>
43facf4a8dSllai #include <sys/dirent.h>
44facf4a8dSllai #include <sys/pathname.h>
45facf4a8dSllai #include <sys/fs/dv_node.h>
46facf4a8dSllai #include <sys/fs/sdev_impl.h>
47facf4a8dSllai #include <sys/sunndi.h>
48facf4a8dSllai #include <sys/modctl.h>
49facf4a8dSllai
50facf4a8dSllai enum {
51facf4a8dSllai PROFILE_TYPE_INCLUDE,
52facf4a8dSllai PROFILE_TYPE_EXCLUDE,
53facf4a8dSllai PROFILE_TYPE_MAP,
54facf4a8dSllai PROFILE_TYPE_SYMLINK
55facf4a8dSllai };
56facf4a8dSllai
57facf4a8dSllai enum {
58facf4a8dSllai WALK_DIR_CONTINUE = 0,
59facf4a8dSllai WALK_DIR_TERMINATE
60facf4a8dSllai };
61facf4a8dSllai
62facf4a8dSllai static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n";
63facf4a8dSllai
64facf4a8dSllai static void process_rule(struct sdev_node *, struct sdev_node *,
65facf4a8dSllai char *, char *, int);
66facf4a8dSllai static void walk_dir(struct vnode *, void *, int (*)(char *, void *));
67facf4a8dSllai
68facf4a8dSllai static void
prof_getattr(struct sdev_node * dir,char * name,struct vnode * gdv,struct vattr * vap,struct vnode ** avpp,int * no_fs_perm)69facf4a8dSllai prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv,
70facf4a8dSllai struct vattr *vap, struct vnode **avpp, int *no_fs_perm)
71facf4a8dSllai {
72facf4a8dSllai struct vnode *advp;
73facf4a8dSllai
74facf4a8dSllai /* get attribute from shadow, if present; else get default */
75facf4a8dSllai advp = dir->sdev_attrvp;
76da6c28aaSamw if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred,
77da6c28aaSamw NULL, NULL, NULL) == 0) {
78da6c28aaSamw (void) VOP_GETATTR(*avpp, vap, 0, kcred, NULL);
79facf4a8dSllai } else if (gdv == NULL || gdv->v_type == VDIR) {
80facf4a8dSllai /* always create shadow directory */
81facf4a8dSllai *vap = sdev_vattr_dir;
82da6c28aaSamw if (advp && VOP_MKDIR(advp, name, &sdev_vattr_dir,
83da6c28aaSamw avpp, kcred, NULL, 0, NULL) != 0) {
84facf4a8dSllai *avpp = NULLVP;
85facf4a8dSllai sdcmn_err10(("prof_getattr: failed to create "
86facf4a8dSllai "shadow directory %s/%s\n", dir->sdev_path, name));
87facf4a8dSllai }
88facf4a8dSllai } else {
89facf4a8dSllai /*
90facf4a8dSllai * get default permission from devfs
91facf4a8dSllai * Before calling devfs_get_defattr, we need to get
92facf4a8dSllai * the realvp (the dv_node). If realvp is not a dv_node,
93facf4a8dSllai * devfs_get_defattr() will return a system-wide default
94facf4a8dSllai * attr for device nodes.
95facf4a8dSllai */
96facf4a8dSllai struct vnode *rvp;
97da6c28aaSamw if (VOP_REALVP(gdv, &rvp, NULL) != 0)
98facf4a8dSllai rvp = gdv;
99facf4a8dSllai devfs_get_defattr(rvp, vap, no_fs_perm);
100facf4a8dSllai *avpp = NULLVP;
101facf4a8dSllai }
102facf4a8dSllai
103facf4a8dSllai /* ignore dev_t and vtype from backing store */
104facf4a8dSllai if (gdv) {
105facf4a8dSllai vap->va_type = gdv->v_type;
106facf4a8dSllai vap->va_rdev = gdv->v_rdev;
107facf4a8dSllai }
108facf4a8dSllai }
109facf4a8dSllai
110facf4a8dSllai static void
apply_glob_pattern(struct sdev_node * pdir,struct sdev_node * cdir)111facf4a8dSllai apply_glob_pattern(struct sdev_node *pdir, struct sdev_node *cdir)
112facf4a8dSllai {
113facf4a8dSllai char *name;
114facf4a8dSllai nvpair_t *nvp = NULL;
115facf4a8dSllai nvlist_t *nvl;
116facf4a8dSllai struct vnode *vp = SDEVTOV(cdir);
117facf4a8dSllai int rv = 0;
118facf4a8dSllai
119facf4a8dSllai if (vp->v_type != VDIR)
120facf4a8dSllai return;
121facf4a8dSllai name = cdir->sdev_name;
122facf4a8dSllai nvl = pdir->sdev_prof.dev_glob_incdir;
123facf4a8dSllai while (nvp = nvlist_next_nvpair(nvl, nvp)) {
124facf4a8dSllai char *pathleft;
125facf4a8dSllai char *expr = nvpair_name(nvp);
126facf4a8dSllai if (!gmatch(name, expr))
127facf4a8dSllai continue;
128facf4a8dSllai rv = nvpair_value_string(nvp, &pathleft);
129facf4a8dSllai if (rv != 0) {
130facf4a8dSllai cmn_err(CE_WARN, sdev_nvp_val_err,
131facf4a8dSllai rv, nvpair_name(nvp));
132facf4a8dSllai break;
133facf4a8dSllai }
134facf4a8dSllai process_rule(cdir, cdir->sdev_origin,
135facf4a8dSllai pathleft, NULL, PROFILE_TYPE_INCLUDE);
136facf4a8dSllai }
137facf4a8dSllai }
138facf4a8dSllai
139facf4a8dSllai /*
140facf4a8dSllai * Some commonality here with sdev_mknode(), could be simplified.
141facf4a8dSllai * NOTE: prof_mknode returns with *newdv held once, if success.
142facf4a8dSllai */
143facf4a8dSllai static int
prof_mknode(struct sdev_node * dir,char * name,struct sdev_node ** newdv,vattr_t * vap,vnode_t * avp,void * arg,cred_t * cred)144facf4a8dSllai prof_mknode(struct sdev_node *dir, char *name, struct sdev_node **newdv,
145facf4a8dSllai vattr_t *vap, vnode_t *avp, void *arg, cred_t *cred)
146facf4a8dSllai {
147facf4a8dSllai struct sdev_node *dv;
148facf4a8dSllai int rv;
149facf4a8dSllai
150facf4a8dSllai ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
151facf4a8dSllai
152facf4a8dSllai /* check cache first */
153facf4a8dSllai if (dv = sdev_cache_lookup(dir, name)) {
154facf4a8dSllai *newdv = dv;
155facf4a8dSllai return (0);
156facf4a8dSllai }
157facf4a8dSllai
158facf4a8dSllai /* allocate node and insert into cache */
159facf4a8dSllai rv = sdev_nodeinit(dir, name, &dv, NULL);
160facf4a8dSllai if (rv != 0) {
161facf4a8dSllai *newdv = NULL;
162facf4a8dSllai return (rv);
163facf4a8dSllai }
164facf4a8dSllai
1659e5aa9d8SRobert Mustacchi sdev_cache_update(dir, &dv, name, SDEV_CACHE_ADD);
166facf4a8dSllai *newdv = dv;
167facf4a8dSllai
168facf4a8dSllai /* put it in ready state */
169facf4a8dSllai rv = sdev_nodeready(*newdv, vap, avp, arg, cred);
170facf4a8dSllai
171facf4a8dSllai /* handle glob pattern in the middle of a path */
172facf4a8dSllai if (rv == 0) {
173facf4a8dSllai if (SDEVTOV(*newdv)->v_type == VDIR)
174facf4a8dSllai sdcmn_err10(("sdev_origin for %s set to 0x%p\n",
175facf4a8dSllai name, arg));
176facf4a8dSllai apply_glob_pattern(dir, *newdv);
1779e5aa9d8SRobert Mustacchi } else {
1789e5aa9d8SRobert Mustacchi sdev_cache_update(dir, &dv, name, SDEV_CACHE_DELETE);
1799e5aa9d8SRobert Mustacchi SDEV_RELE(dv);
180facf4a8dSllai }
181facf4a8dSllai return (rv);
182facf4a8dSllai }
183facf4a8dSllai
184facf4a8dSllai /*
185facf4a8dSllai * Create a directory node in a non-global dev instance.
186facf4a8dSllai * Always create shadow vnode. Set sdev_origin to the corresponding
187facf4a8dSllai * global directory sdev_node if it exists. This facilitates the
188facf4a8dSllai * lookup operation.
189facf4a8dSllai */
190facf4a8dSllai static int
prof_make_dir(char * name,struct sdev_node ** gdirp,struct sdev_node ** dirp)191facf4a8dSllai prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp)
192facf4a8dSllai {
193facf4a8dSllai struct sdev_node *dir = *dirp;
194facf4a8dSllai struct sdev_node *gdir = *gdirp;
195facf4a8dSllai struct sdev_node *newdv;
196facf4a8dSllai struct vnode *avp, *gnewdir = NULL;
197facf4a8dSllai struct vattr vattr;
198facf4a8dSllai int error;
199facf4a8dSllai
200facf4a8dSllai /* see if name already exists */
201facf4a8dSllai rw_enter(&dir->sdev_contents, RW_READER);
202facf4a8dSllai if (newdv = sdev_cache_lookup(dir, name)) {
203facf4a8dSllai *dirp = newdv;
204facf4a8dSllai *gdirp = newdv->sdev_origin;
205facf4a8dSllai rw_exit(&dir->sdev_contents);
2069e5aa9d8SRobert Mustacchi SDEV_RELE(dir);
207facf4a8dSllai return (0);
208facf4a8dSllai }
209facf4a8dSllai rw_exit(&dir->sdev_contents);
210facf4a8dSllai
211facf4a8dSllai /* find corresponding dir node in global dev */
212facf4a8dSllai if (gdir) {
213facf4a8dSllai error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir,
214da6c28aaSamw NULL, 0, NULL, kcred, NULL, NULL, NULL);
215facf4a8dSllai if (error == 0) {
216facf4a8dSllai *gdirp = VTOSDEV(gnewdir);
217*814e7298SToomas Soome } else { /* it's ok if there no global dir */
218facf4a8dSllai *gdirp = NULL;
219facf4a8dSllai }
220facf4a8dSllai }
221facf4a8dSllai
222facf4a8dSllai /* get attribute from shadow, also create shadow dir */
223facf4a8dSllai prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL);
224facf4a8dSllai
225facf4a8dSllai /* create dev directory vnode */
226facf4a8dSllai rw_enter(&dir->sdev_contents, RW_WRITER);
227facf4a8dSllai error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp,
228facf4a8dSllai kcred);
229facf4a8dSllai rw_exit(&dir->sdev_contents);
230facf4a8dSllai if (error == 0) {
231facf4a8dSllai ASSERT(newdv);
232facf4a8dSllai *dirp = newdv;
233facf4a8dSllai }
234facf4a8dSllai SDEV_RELE(dir);
235facf4a8dSllai return (error);
236facf4a8dSllai }
237facf4a8dSllai
238facf4a8dSllai /*
239facf4a8dSllai * Look up a logical name in the global zone.
240facf4a8dSllai * Provides the ability to map the global zone's device name
241facf4a8dSllai * to an alternate name within a zone. The primary example
242facf4a8dSllai * is the virtual console device /dev/zcons/[zonename]/zconsole
243facf4a8dSllai * mapped to /[zonename]/root/dev/zconsole.
244facf4a8dSllai */
245facf4a8dSllai static void
prof_lookup_globaldev(struct sdev_node * dir,struct sdev_node * gdir,char * name,char * rename)246facf4a8dSllai prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir,
247facf4a8dSllai char *name, char *rename)
248facf4a8dSllai {
249facf4a8dSllai int error;
250facf4a8dSllai struct vnode *avp, *gdv, *gddv;
251facf4a8dSllai struct sdev_node *newdv;
252facf4a8dSllai struct vattr vattr = {0};
253facf4a8dSllai struct pathname pn;
254facf4a8dSllai
255facf4a8dSllai /* check if node already exists */
256facf4a8dSllai newdv = sdev_cache_lookup(dir, rename);
257facf4a8dSllai if (newdv) {
258facf4a8dSllai ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
259facf4a8dSllai SDEV_SIMPLE_RELE(newdv);
260facf4a8dSllai return;
261facf4a8dSllai }
262facf4a8dSllai
263facf4a8dSllai /* sanity check arguments */
264facf4a8dSllai if (!gdir || pn_get(name, UIO_SYSSPACE, &pn))
265facf4a8dSllai return;
266facf4a8dSllai
267facf4a8dSllai /* perform a relative lookup of the global /dev instance */
268facf4a8dSllai gddv = SDEVTOV(gdir);
269facf4a8dSllai VN_HOLD(gddv);
270facf4a8dSllai error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv,
271facf4a8dSllai rootdir, gddv, kcred);
272facf4a8dSllai pn_free(&pn);
273facf4a8dSllai if (error) {
274facf4a8dSllai sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name));
275facf4a8dSllai return;
276facf4a8dSllai }
277facf4a8dSllai ASSERT(gdv && gdv->v_type != VLNK);
278facf4a8dSllai
279facf4a8dSllai /*
280facf4a8dSllai * Found the entry in global /dev, figure out attributes
281facf4a8dSllai * by looking at backing store. Call into devfs for default.
282a83b1f2cSllai * Note, mapped device is persisted under the new name
283facf4a8dSllai */
284a83b1f2cSllai prof_getattr(dir, rename, gdv, &vattr, &avp, NULL);
285facf4a8dSllai
286facf4a8dSllai if (gdv->v_type != VDIR) {
287facf4a8dSllai VN_RELE(gdv);
288facf4a8dSllai gdir = NULL;
289facf4a8dSllai } else
290facf4a8dSllai gdir = VTOSDEV(gdv);
291facf4a8dSllai
292facf4a8dSllai if (prof_mknode(dir, rename, &newdv, &vattr, avp,
293facf4a8dSllai (void *)gdir, kcred) == 0) {
294facf4a8dSllai ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
295facf4a8dSllai SDEV_SIMPLE_RELE(newdv);
296facf4a8dSllai }
297facf4a8dSllai }
298facf4a8dSllai
299facf4a8dSllai static void
prof_make_sym(struct sdev_node * dir,char * lnm,char * tgt)300facf4a8dSllai prof_make_sym(struct sdev_node *dir, char *lnm, char *tgt)
301facf4a8dSllai {
302facf4a8dSllai struct sdev_node *newdv;
303facf4a8dSllai
304facf4a8dSllai if (prof_mknode(dir, lnm, &newdv, &sdev_vattr_lnk, NULL,
305facf4a8dSllai (void *)tgt, kcred) == 0) {
306facf4a8dSllai ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
307facf4a8dSllai SDEV_SIMPLE_RELE(newdv);
308facf4a8dSllai }
309facf4a8dSllai }
310facf4a8dSllai
311facf4a8dSllai /*
312facf4a8dSllai * Create symlinks in the current directory based on profile
313facf4a8dSllai */
314facf4a8dSllai static void
prof_make_symlinks(struct sdev_node * dir)315facf4a8dSllai prof_make_symlinks(struct sdev_node *dir)
316facf4a8dSllai {
317facf4a8dSllai char *tgt, *lnm;
318facf4a8dSllai nvpair_t *nvp = NULL;
319facf4a8dSllai nvlist_t *nvl = dir->sdev_prof.dev_symlink;
320facf4a8dSllai int rv;
321facf4a8dSllai
322facf4a8dSllai ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
323facf4a8dSllai
324facf4a8dSllai if (nvl == NULL)
325facf4a8dSllai return;
326facf4a8dSllai
327facf4a8dSllai while (nvp = nvlist_next_nvpair(nvl, nvp)) {
328facf4a8dSllai lnm = nvpair_name(nvp);
329facf4a8dSllai rv = nvpair_value_string(nvp, &tgt);
330facf4a8dSllai if (rv != 0) {
331facf4a8dSllai cmn_err(CE_WARN, sdev_nvp_val_err,
332facf4a8dSllai rv, nvpair_name(nvp));
333facf4a8dSllai break;
334facf4a8dSllai }
335facf4a8dSllai prof_make_sym(dir, lnm, tgt);
336facf4a8dSllai }
337facf4a8dSllai }
338facf4a8dSllai
339facf4a8dSllai static void
prof_make_maps(struct sdev_node * dir)340facf4a8dSllai prof_make_maps(struct sdev_node *dir)
341facf4a8dSllai {
342facf4a8dSllai nvpair_t *nvp = NULL;
343facf4a8dSllai nvlist_t *nvl = dir->sdev_prof.dev_map;
344facf4a8dSllai int rv;
345facf4a8dSllai
346facf4a8dSllai ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
347facf4a8dSllai
348facf4a8dSllai if (nvl == NULL)
349facf4a8dSllai return;
350facf4a8dSllai
351facf4a8dSllai while (nvp = nvlist_next_nvpair(nvl, nvp)) {
352facf4a8dSllai char *name;
353facf4a8dSllai char *rename = nvpair_name(nvp);
354facf4a8dSllai rv = nvpair_value_string(nvp, &name);
355facf4a8dSllai if (rv != 0) {
356facf4a8dSllai cmn_err(CE_WARN, sdev_nvp_val_err,
357facf4a8dSllai rv, nvpair_name(nvp));
358facf4a8dSllai break;
359facf4a8dSllai }
360facf4a8dSllai sdcmn_err10(("map %s -> %s\n", name, rename));
361facf4a8dSllai (void) prof_lookup_globaldev(dir, sdev_origins->sdev_root,
362facf4a8dSllai name, rename);
363facf4a8dSllai }
364facf4a8dSllai }
365facf4a8dSllai
366facf4a8dSllai struct match_arg {
367facf4a8dSllai char *expr;
368facf4a8dSllai int match;
369facf4a8dSllai };
370facf4a8dSllai
371facf4a8dSllai static int
match_name(char * name,void * arg)372facf4a8dSllai match_name(char *name, void *arg)
373facf4a8dSllai {
374facf4a8dSllai struct match_arg *margp = (struct match_arg *)arg;
375facf4a8dSllai
376facf4a8dSllai if (gmatch(name, margp->expr)) {
377facf4a8dSllai margp->match = 1;
378facf4a8dSllai return (WALK_DIR_TERMINATE);
379facf4a8dSllai }
380facf4a8dSllai return (WALK_DIR_CONTINUE);
381facf4a8dSllai }
382facf4a8dSllai
383facf4a8dSllai static int
is_nonempty_dir(char * name,char * pathleft,struct sdev_node * dir)384facf4a8dSllai is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir)
385facf4a8dSllai {
386facf4a8dSllai struct match_arg marg;
387facf4a8dSllai struct pathname pn;
388facf4a8dSllai struct vnode *gvp;
389facf4a8dSllai struct sdev_node *gdir = dir->sdev_origin;
390facf4a8dSllai
391da6c28aaSamw if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred,
392da6c28aaSamw NULL, NULL, NULL) != 0)
393facf4a8dSllai return (0);
394facf4a8dSllai
395facf4a8dSllai if (gvp->v_type != VDIR) {
396facf4a8dSllai VN_RELE(gvp);
397facf4a8dSllai return (0);
398facf4a8dSllai }
399facf4a8dSllai
400facf4a8dSllai if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) {
401facf4a8dSllai VN_RELE(gvp);
402facf4a8dSllai return (0);
403facf4a8dSllai }
404facf4a8dSllai
405facf4a8dSllai marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
406facf4a8dSllai (void) pn_getcomponent(&pn, marg.expr);
407facf4a8dSllai marg.match = 0;
408facf4a8dSllai
409facf4a8dSllai walk_dir(gvp, &marg, match_name);
410facf4a8dSllai VN_RELE(gvp);
411facf4a8dSllai kmem_free(marg.expr, MAXNAMELEN);
412facf4a8dSllai pn_free(&pn);
413facf4a8dSllai
414facf4a8dSllai return (marg.match);
415facf4a8dSllai }
416facf4a8dSllai
417facf4a8dSllai
418facf4a8dSllai /* Check if name passes matching rules */
41945b17475SAlex Wilson int
prof_name_matched(char * name,struct sdev_node * dir)420facf4a8dSllai prof_name_matched(char *name, struct sdev_node *dir)
421facf4a8dSllai {
422facf4a8dSllai int type, match = 0;
423facf4a8dSllai char *expr;
424facf4a8dSllai nvlist_t *nvl;
425facf4a8dSllai nvpair_t *nvp = NULL;
426facf4a8dSllai int rv;
427facf4a8dSllai
428facf4a8dSllai /* check against nvlist for leaf include/exclude */
429facf4a8dSllai nvl = dir->sdev_prof.dev_name;
430facf4a8dSllai while (nvp = nvlist_next_nvpair(nvl, nvp)) {
431facf4a8dSllai expr = nvpair_name(nvp);
432facf4a8dSllai rv = nvpair_value_int32(nvp, &type);
433facf4a8dSllai if (rv != 0) {
434facf4a8dSllai cmn_err(CE_WARN, sdev_nvp_val_err,
435facf4a8dSllai rv, nvpair_name(nvp));
436facf4a8dSllai break;
437facf4a8dSllai }
438facf4a8dSllai
439facf4a8dSllai if (type == PROFILE_TYPE_EXCLUDE) {
440facf4a8dSllai if (gmatch(name, expr))
441facf4a8dSllai return (0); /* excluded */
442facf4a8dSllai } else if (!match) {
443facf4a8dSllai match = gmatch(name, expr);
444facf4a8dSllai }
445facf4a8dSllai }
446facf4a8dSllai if (match) {
447facf4a8dSllai sdcmn_err10(("prof_name_matched: %s\n", name));
448facf4a8dSllai return (match);
449facf4a8dSllai }
450facf4a8dSllai
451facf4a8dSllai /* check for match against directory globbing pattern */
452facf4a8dSllai nvl = dir->sdev_prof.dev_glob_incdir;
453facf4a8dSllai while (nvp = nvlist_next_nvpair(nvl, nvp)) {
454facf4a8dSllai char *pathleft;
455facf4a8dSllai expr = nvpair_name(nvp);
456facf4a8dSllai if (gmatch(name, expr) == 0)
457facf4a8dSllai continue;
458facf4a8dSllai rv = nvpair_value_string(nvp, &pathleft);
459facf4a8dSllai if (rv != 0) {
460facf4a8dSllai cmn_err(CE_WARN, sdev_nvp_val_err,
461facf4a8dSllai rv, nvpair_name(nvp));
462facf4a8dSllai break;
463facf4a8dSllai }
464facf4a8dSllai if (is_nonempty_dir(name, pathleft, dir)) {
465facf4a8dSllai sdcmn_err10(("prof_name_matched: dir %s\n", name));
466facf4a8dSllai return (1);
467facf4a8dSllai }
468facf4a8dSllai }
469facf4a8dSllai
470facf4a8dSllai return (0);
471facf4a8dSllai }
472facf4a8dSllai
473facf4a8dSllai static void
walk_dir(struct vnode * dvp,void * arg,int (* callback)(char *,void *))474facf4a8dSllai walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *))
475facf4a8dSllai {
476facf4a8dSllai char *nm;
477facf4a8dSllai int eof, error;
478facf4a8dSllai struct iovec iov;
479facf4a8dSllai struct uio uio;
480facf4a8dSllai struct dirent64 *dp;
481facf4a8dSllai dirent64_t *dbuf;
482facf4a8dSllai size_t dbuflen, dlen;
483facf4a8dSllai
484facf4a8dSllai ASSERT(dvp);
485facf4a8dSllai
486facf4a8dSllai dlen = 4096;
487facf4a8dSllai dbuf = kmem_zalloc(dlen, KM_SLEEP);
488facf4a8dSllai
489facf4a8dSllai uio.uio_iov = &iov;
490facf4a8dSllai uio.uio_iovcnt = 1;
491facf4a8dSllai uio.uio_segflg = UIO_SYSSPACE;
492facf4a8dSllai uio.uio_fmode = 0;
493facf4a8dSllai uio.uio_extflg = UIO_COPY_CACHED;
494facf4a8dSllai uio.uio_loffset = 0;
495facf4a8dSllai uio.uio_llimit = MAXOFFSET_T;
496facf4a8dSllai
497facf4a8dSllai eof = 0;
498facf4a8dSllai error = 0;
499facf4a8dSllai while (!error && !eof) {
500facf4a8dSllai uio.uio_resid = dlen;
501facf4a8dSllai iov.iov_base = (char *)dbuf;
502facf4a8dSllai iov.iov_len = dlen;
503facf4a8dSllai (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
504da6c28aaSamw error = VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0);
505facf4a8dSllai VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
506facf4a8dSllai
507facf4a8dSllai dbuflen = dlen - uio.uio_resid;
508facf4a8dSllai if (error || dbuflen == 0)
509facf4a8dSllai break;
510facf4a8dSllai for (dp = dbuf; ((intptr_t)dp <
511facf4a8dSllai (intptr_t)dbuf + dbuflen);
512facf4a8dSllai dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
513facf4a8dSllai nm = dp->d_name;
514facf4a8dSllai
515facf4a8dSllai if (strcmp(nm, ".") == 0 ||
516facf4a8dSllai strcmp(nm, "..") == 0)
517facf4a8dSllai continue;
518facf4a8dSllai
519facf4a8dSllai if (callback(nm, arg) == WALK_DIR_TERMINATE)
520facf4a8dSllai goto end;
521facf4a8dSllai }
522facf4a8dSllai }
523facf4a8dSllai
524facf4a8dSllai end:
525facf4a8dSllai kmem_free(dbuf, dlen);
526facf4a8dSllai }
527facf4a8dSllai
5280fbb751dSJohn Levon /*
5290fbb751dSJohn Levon * Last chance for a zone to see a node. If our parent dir is
5300fbb751dSJohn Levon * SDEV_ZONED, then we look up the "zone" property for the node. If the
5310fbb751dSJohn Levon * property is found and matches the current zone name, we allow it.
5320fbb751dSJohn Levon * Note that this isn't quite correct for the global zone peeking inside
5330fbb751dSJohn Levon * a zone's /dev - for that to work, we'd have to have a per-dev-mount
5340fbb751dSJohn Levon * zone ref squirreled away.
5350fbb751dSJohn Levon */
5360fbb751dSJohn Levon static int
prof_zone_matched(char * name,struct sdev_node * dir)5370fbb751dSJohn Levon prof_zone_matched(char *name, struct sdev_node *dir)
5380fbb751dSJohn Levon {
5390fbb751dSJohn Levon vnode_t *gvn = SDEVTOV(dir->sdev_origin);
5400fbb751dSJohn Levon struct pathname pn;
5410fbb751dSJohn Levon vnode_t *vn = NULL;
5420fbb751dSJohn Levon char zonename[ZONENAME_MAX];
5430fbb751dSJohn Levon int znlen = ZONENAME_MAX;
5440fbb751dSJohn Levon int ret;
5450fbb751dSJohn Levon
5460fbb751dSJohn Levon ASSERT((dir->sdev_flags & SDEV_ZONED) != 0);
5470fbb751dSJohn Levon
5480fbb751dSJohn Levon sdcmn_err10(("sdev_node %p is zoned, looking for %s\n",
5490fbb751dSJohn Levon (void *)dir, name));
5500fbb751dSJohn Levon
5510fbb751dSJohn Levon if (pn_get(name, UIO_SYSSPACE, &pn))
5520fbb751dSJohn Levon return (0);
5530fbb751dSJohn Levon
5540fbb751dSJohn Levon VN_HOLD(gvn);
5550fbb751dSJohn Levon
5560fbb751dSJohn Levon ret = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &vn, rootdir, gvn, kcred);
5570fbb751dSJohn Levon
5580fbb751dSJohn Levon pn_free(&pn);
5590fbb751dSJohn Levon
5600fbb751dSJohn Levon if (ret != 0) {
5610fbb751dSJohn Levon sdcmn_err10(("prof_zone_matched: %s not found\n", name));
5620fbb751dSJohn Levon return (0);
5630fbb751dSJohn Levon }
5640fbb751dSJohn Levon
5650fbb751dSJohn Levon /*
5660fbb751dSJohn Levon * VBLK doesn't matter, and the property name is in fact treated
5670fbb751dSJohn Levon * as a const char *.
5680fbb751dSJohn Levon */
5690fbb751dSJohn Levon ret = e_ddi_getlongprop_buf(vn->v_rdev, VBLK, (char *)"zone",
5700fbb751dSJohn Levon DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (caddr_t)zonename, &znlen);
5710fbb751dSJohn Levon
5720fbb751dSJohn Levon VN_RELE(vn);
5730fbb751dSJohn Levon
5740fbb751dSJohn Levon if (ret == DDI_PROP_NOT_FOUND) {
5750fbb751dSJohn Levon sdcmn_err10(("vnode %p: no zone prop\n", (void *)vn));
5760fbb751dSJohn Levon return (0);
5770fbb751dSJohn Levon } else if (ret != DDI_PROP_SUCCESS) {
5780fbb751dSJohn Levon sdcmn_err10(("vnode %p: zone prop error: %d\n",
5790fbb751dSJohn Levon (void *)vn, ret));
5800fbb751dSJohn Levon return (0);
5810fbb751dSJohn Levon }
5820fbb751dSJohn Levon
5830fbb751dSJohn Levon sdcmn_err10(("vnode %p zone prop: %s\n", (void *)vn, zonename));
5840fbb751dSJohn Levon return (strcmp(zonename, curproc->p_zone->zone_name) == 0);
5850fbb751dSJohn Levon }
5860fbb751dSJohn Levon
587facf4a8dSllai static int
prof_make_name_glob(char * nm,void * arg)5880fbb751dSJohn Levon prof_make_name_glob(char *nm, void *arg)
589facf4a8dSllai {
590facf4a8dSllai struct sdev_node *ddv = (struct sdev_node *)arg;
591facf4a8dSllai
592facf4a8dSllai if (prof_name_matched(nm, ddv))
593facf4a8dSllai prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm);
5940fbb751dSJohn Levon
5950fbb751dSJohn Levon return (WALK_DIR_CONTINUE);
5960fbb751dSJohn Levon }
5970fbb751dSJohn Levon
5980fbb751dSJohn Levon static int
prof_make_name_zone(char * nm,void * arg)5990fbb751dSJohn Levon prof_make_name_zone(char *nm, void *arg)
6000fbb751dSJohn Levon {
6010fbb751dSJohn Levon struct sdev_node *ddv = (struct sdev_node *)arg;
6020fbb751dSJohn Levon
6030fbb751dSJohn Levon if (prof_zone_matched(nm, ddv))
6040fbb751dSJohn Levon prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm);
6050fbb751dSJohn Levon
606facf4a8dSllai return (WALK_DIR_CONTINUE);
607facf4a8dSllai }
608facf4a8dSllai
609facf4a8dSllai static void
prof_make_names_walk(struct sdev_node * ddv,int (* cb)(char *,void *))6100fbb751dSJohn Levon prof_make_names_walk(struct sdev_node *ddv, int (*cb)(char *, void *))
611facf4a8dSllai {
612facf4a8dSllai struct sdev_node *gdir;
613facf4a8dSllai
614facf4a8dSllai gdir = ddv->sdev_origin;
615facf4a8dSllai if (gdir == NULL)
616facf4a8dSllai return;
6170fbb751dSJohn Levon walk_dir(SDEVTOV(gdir), (void *)ddv, cb);
618facf4a8dSllai }
619facf4a8dSllai
620facf4a8dSllai static void
prof_make_names(struct sdev_node * dir)621facf4a8dSllai prof_make_names(struct sdev_node *dir)
622facf4a8dSllai {
623facf4a8dSllai char *name;
624facf4a8dSllai nvpair_t *nvp = NULL;
625facf4a8dSllai nvlist_t *nvl = dir->sdev_prof.dev_name;
626facf4a8dSllai int rv;
627facf4a8dSllai
628facf4a8dSllai ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
629facf4a8dSllai
6300fbb751dSJohn Levon if ((dir->sdev_flags & SDEV_ZONED) != 0)
6310fbb751dSJohn Levon prof_make_names_walk(dir, prof_make_name_zone);
6320fbb751dSJohn Levon
633facf4a8dSllai if (nvl == NULL)
634facf4a8dSllai return;
635facf4a8dSllai
636facf4a8dSllai if (dir->sdev_prof.has_glob) {
6370fbb751dSJohn Levon prof_make_names_walk(dir, prof_make_name_glob);
638facf4a8dSllai return;
639facf4a8dSllai }
640facf4a8dSllai
641facf4a8dSllai /* Walk nvlist and lookup corresponding device in global inst */
642facf4a8dSllai while (nvp = nvlist_next_nvpair(nvl, nvp)) {
643facf4a8dSllai int type;
644facf4a8dSllai rv = nvpair_value_int32(nvp, &type);
645facf4a8dSllai if (rv != 0) {
646facf4a8dSllai cmn_err(CE_WARN, sdev_nvp_val_err,
647facf4a8dSllai rv, nvpair_name(nvp));
648facf4a8dSllai break;
649facf4a8dSllai }
650facf4a8dSllai if (type == PROFILE_TYPE_EXCLUDE)
651facf4a8dSllai continue;
652facf4a8dSllai name = nvpair_name(nvp);
653facf4a8dSllai (void) prof_lookup_globaldev(dir, dir->sdev_origin,
654facf4a8dSllai name, name);
655facf4a8dSllai }
656facf4a8dSllai }
657facf4a8dSllai
65822253b45SAlexander Kolbasov /*
65922253b45SAlexander Kolbasov * Return True if directory cache is out of date and should be updated.
66022253b45SAlexander Kolbasov */
66122253b45SAlexander Kolbasov static boolean_t
prof_dev_needupdate(sdev_node_t * ddv)66222253b45SAlexander Kolbasov prof_dev_needupdate(sdev_node_t *ddv)
66322253b45SAlexander Kolbasov {
66422253b45SAlexander Kolbasov sdev_node_t *gdir = ddv->sdev_origin;
66522253b45SAlexander Kolbasov
66622253b45SAlexander Kolbasov /*
66722253b45SAlexander Kolbasov * Caller can have either reader or writer lock
66822253b45SAlexander Kolbasov */
66922253b45SAlexander Kolbasov ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
67022253b45SAlexander Kolbasov
67122253b45SAlexander Kolbasov /*
67222253b45SAlexander Kolbasov * We need to rebuild the directory content if
67322253b45SAlexander Kolbasov * - ddv is not in a SDEV_ZOMBIE state
67422253b45SAlexander Kolbasov * - SDEV_BUILD is set OR
67522253b45SAlexander Kolbasov * - The device tree generation number has changed OR
67622253b45SAlexander Kolbasov * - The corresponding /dev namespace has been updated
67722253b45SAlexander Kolbasov */
67822253b45SAlexander Kolbasov return ((ddv->sdev_state != SDEV_ZOMBIE) &&
67922253b45SAlexander Kolbasov (((ddv->sdev_flags & SDEV_BUILD) != 0) ||
68022253b45SAlexander Kolbasov (ddv->sdev_devtree_gen != devtree_gen) ||
68122253b45SAlexander Kolbasov ((gdir != NULL) &&
68222253b45SAlexander Kolbasov (ddv->sdev_ldir_gen != gdir->sdev_gdir_gen))));
68322253b45SAlexander Kolbasov }
68422253b45SAlexander Kolbasov
685facf4a8dSllai /*
686facf4a8dSllai * Build directory vnodes based on the profile and the global
687facf4a8dSllai * dev instance.
688facf4a8dSllai */
689facf4a8dSllai void
prof_filldir(sdev_node_t * ddv)69022253b45SAlexander Kolbasov prof_filldir(sdev_node_t *ddv)
691facf4a8dSllai {
69222253b45SAlexander Kolbasov sdev_node_t *gdir;
693facf4a8dSllai
694facf4a8dSllai ASSERT(RW_READ_HELD(&ddv->sdev_contents));
695facf4a8dSllai
69622253b45SAlexander Kolbasov if (!prof_dev_needupdate(ddv)) {
69722253b45SAlexander Kolbasov ASSERT(RW_READ_HELD(&ddv->sdev_contents));
69822253b45SAlexander Kolbasov return;
69922253b45SAlexander Kolbasov }
700facf4a8dSllai /*
70122253b45SAlexander Kolbasov * Upgrade to writer lock
702facf4a8dSllai */
70322253b45SAlexander Kolbasov if (rw_tryupgrade(&ddv->sdev_contents) == 0) {
70422253b45SAlexander Kolbasov /*
70522253b45SAlexander Kolbasov * We need to drop the read lock and re-acquire it as a
70622253b45SAlexander Kolbasov * write lock. While we do this the condition may change so we
70722253b45SAlexander Kolbasov * need to re-check condition
70822253b45SAlexander Kolbasov */
709facf4a8dSllai rw_exit(&ddv->sdev_contents);
710facf4a8dSllai rw_enter(&ddv->sdev_contents, RW_WRITER);
71122253b45SAlexander Kolbasov if (!prof_dev_needupdate(ddv)) {
71222253b45SAlexander Kolbasov /* Downgrade back to the read lock before returning */
71322253b45SAlexander Kolbasov rw_downgrade(&ddv->sdev_contents);
71422253b45SAlexander Kolbasov return;
71522253b45SAlexander Kolbasov }
716facf4a8dSllai }
71722253b45SAlexander Kolbasov /* At this point we should have a write lock */
71822253b45SAlexander Kolbasov ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
71922253b45SAlexander Kolbasov
720facf4a8dSllai sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n",
721facf4a8dSllai ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen));
72222253b45SAlexander Kolbasov
72322253b45SAlexander Kolbasov gdir = ddv->sdev_origin;
72422253b45SAlexander Kolbasov
72522253b45SAlexander Kolbasov if (gdir != NULL)
726facf4a8dSllai sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n",
727facf4a8dSllai ddv->sdev_path, ddv->sdev_ldir_gen,
728facf4a8dSllai gdir->sdev_gdir_gen));
729facf4a8dSllai
730facf4a8dSllai /* update flags and generation number so next filldir is quick */
73122253b45SAlexander Kolbasov if ((ddv->sdev_flags & SDEV_BUILD) == SDEV_BUILD) {
73222253b45SAlexander Kolbasov ddv->sdev_flags &= ~SDEV_BUILD;
73322253b45SAlexander Kolbasov }
734facf4a8dSllai ddv->sdev_devtree_gen = devtree_gen;
73522253b45SAlexander Kolbasov if (gdir != NULL)
736facf4a8dSllai ddv->sdev_ldir_gen = gdir->sdev_gdir_gen;
737facf4a8dSllai
738facf4a8dSllai prof_make_symlinks(ddv);
739facf4a8dSllai prof_make_maps(ddv);
740facf4a8dSllai prof_make_names(ddv);
741facf4a8dSllai rw_downgrade(&ddv->sdev_contents);
742facf4a8dSllai }
743facf4a8dSllai
744facf4a8dSllai /* apply include/exclude pattern to existing directory content */
745facf4a8dSllai static void
apply_dir_pattern(struct sdev_node * dir,char * expr,char * pathleft,int type)746facf4a8dSllai apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type)
747facf4a8dSllai {
748facf4a8dSllai struct sdev_node *dv;
749facf4a8dSllai
750facf4a8dSllai /* leaf pattern */
751facf4a8dSllai if (pathleft == NULL) {
752facf4a8dSllai if (type == PROFILE_TYPE_INCLUDE)
753facf4a8dSllai return; /* nothing to do for include */
754facf4a8dSllai (void) sdev_cleandir(dir, expr, SDEV_ENFORCE);
755facf4a8dSllai return;
756facf4a8dSllai }
757facf4a8dSllai
758facf4a8dSllai /* directory pattern */
759facf4a8dSllai rw_enter(&dir->sdev_contents, RW_WRITER);
760aac43a5fSjg
761aac43a5fSjg for (dv = SDEV_FIRST_ENTRY(dir); dv; dv = SDEV_NEXT_ENTRY(dir, dv)) {
762facf4a8dSllai if (gmatch(dv->sdev_name, expr) == 0 ||
763facf4a8dSllai SDEVTOV(dv)->v_type != VDIR)
764facf4a8dSllai continue;
765facf4a8dSllai process_rule(dv, dv->sdev_origin,
766facf4a8dSllai pathleft, NULL, type);
767facf4a8dSllai }
768facf4a8dSllai rw_exit(&dir->sdev_contents);
769facf4a8dSllai }
770facf4a8dSllai
771facf4a8dSllai /*
772facf4a8dSllai * Add a profile rule.
773facf4a8dSllai * tgt represents a device name matching expression,
774facf4a8dSllai * matching device names are to be either included or excluded.
775facf4a8dSllai */
776facf4a8dSllai static void
prof_add_rule(char * name,char * tgt,struct sdev_node * dir,int type)777facf4a8dSllai prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type)
778facf4a8dSllai {
779facf4a8dSllai int error;
780facf4a8dSllai nvlist_t **nvlp = NULL;
781facf4a8dSllai int rv;
782facf4a8dSllai
783facf4a8dSllai ASSERT(SDEVTOV(dir)->v_type == VDIR);
784facf4a8dSllai
785facf4a8dSllai rw_enter(&dir->sdev_contents, RW_WRITER);
786facf4a8dSllai
787facf4a8dSllai switch (type) {
788facf4a8dSllai case PROFILE_TYPE_INCLUDE:
789facf4a8dSllai if (tgt)
790facf4a8dSllai nvlp = &(dir->sdev_prof.dev_glob_incdir);
791facf4a8dSllai else
792facf4a8dSllai nvlp = &(dir->sdev_prof.dev_name);
793facf4a8dSllai break;
794facf4a8dSllai case PROFILE_TYPE_EXCLUDE:
795facf4a8dSllai if (tgt)
796facf4a8dSllai nvlp = &(dir->sdev_prof.dev_glob_excdir);
797facf4a8dSllai else
798facf4a8dSllai nvlp = &(dir->sdev_prof.dev_name);
799facf4a8dSllai break;
800facf4a8dSllai case PROFILE_TYPE_MAP:
801facf4a8dSllai nvlp = &(dir->sdev_prof.dev_map);
802facf4a8dSllai break;
803facf4a8dSllai case PROFILE_TYPE_SYMLINK:
804facf4a8dSllai nvlp = &(dir->sdev_prof.dev_symlink);
805facf4a8dSllai break;
806facf4a8dSllai };
807facf4a8dSllai
808facf4a8dSllai /* initialize nvlist */
809facf4a8dSllai if (*nvlp == NULL) {
810facf4a8dSllai error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP);
811facf4a8dSllai ASSERT(error == 0);
812facf4a8dSllai }
813facf4a8dSllai
814facf4a8dSllai if (tgt) {
815facf4a8dSllai rv = nvlist_add_string(*nvlp, name, tgt);
816facf4a8dSllai } else {
817facf4a8dSllai rv = nvlist_add_int32(*nvlp, name, type);
818facf4a8dSllai }
819facf4a8dSllai ASSERT(rv == 0);
820facf4a8dSllai /* rebuild directory content */
821facf4a8dSllai dir->sdev_flags |= SDEV_BUILD;
822facf4a8dSllai
823facf4a8dSllai if ((type == PROFILE_TYPE_INCLUDE) &&
824facf4a8dSllai (strpbrk(name, "*?[]") != NULL)) {
825facf4a8dSllai dir->sdev_prof.has_glob = 1;
826facf4a8dSllai }
827facf4a8dSllai
828facf4a8dSllai rw_exit(&dir->sdev_contents);
829facf4a8dSllai
830facf4a8dSllai /* additional details for glob pattern and exclusion */
831facf4a8dSllai switch (type) {
832facf4a8dSllai case PROFILE_TYPE_INCLUDE:
833facf4a8dSllai case PROFILE_TYPE_EXCLUDE:
834facf4a8dSllai apply_dir_pattern(dir, name, tgt, type);
835facf4a8dSllai break;
836facf4a8dSllai };
837facf4a8dSllai }
838facf4a8dSllai
839facf4a8dSllai /*
840facf4a8dSllai * Parse path components and apply requested matching rule at
841facf4a8dSllai * directory level.
842facf4a8dSllai */
843facf4a8dSllai static void
process_rule(struct sdev_node * dir,struct sdev_node * gdir,char * path,char * tgt,int type)844facf4a8dSllai process_rule(struct sdev_node *dir, struct sdev_node *gdir,
845facf4a8dSllai char *path, char *tgt, int type)
846facf4a8dSllai {
847facf4a8dSllai char *name;
848facf4a8dSllai struct pathname pn;
849facf4a8dSllai int rv = 0;
850facf4a8dSllai
851facf4a8dSllai if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) {
852facf4a8dSllai path += 5;
853facf4a8dSllai }
854facf4a8dSllai
855facf4a8dSllai if (pn_get(path, UIO_SYSSPACE, &pn) != 0)
856facf4a8dSllai return;
857facf4a8dSllai
858facf4a8dSllai name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
859facf4a8dSllai (void) pn_getcomponent(&pn, name);
860facf4a8dSllai pn_skipslash(&pn);
861facf4a8dSllai SDEV_HOLD(dir);
862facf4a8dSllai
863facf4a8dSllai while (pn_pathleft(&pn)) {
864facf4a8dSllai /* If this is pattern, just add the pattern */
865facf4a8dSllai if (strpbrk(name, "*?[]") != NULL &&
866facf4a8dSllai (type == PROFILE_TYPE_INCLUDE ||
867facf4a8dSllai type == PROFILE_TYPE_EXCLUDE)) {
868facf4a8dSllai ASSERT(tgt == NULL);
869facf4a8dSllai tgt = pn.pn_path;
870facf4a8dSllai break;
871facf4a8dSllai }
872facf4a8dSllai if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) {
873facf4a8dSllai cmn_err(CE_CONT, "process_rule: %s error %d\n",
874facf4a8dSllai path, rv);
875facf4a8dSllai break;
876facf4a8dSllai }
877facf4a8dSllai (void) pn_getcomponent(&pn, name);
878facf4a8dSllai pn_skipslash(&pn);
879facf4a8dSllai }
880facf4a8dSllai
881facf4a8dSllai /* process the leaf component */
882facf4a8dSllai if (rv == 0) {
883facf4a8dSllai prof_add_rule(name, tgt, dir, type);
884facf4a8dSllai SDEV_SIMPLE_RELE(dir);
885facf4a8dSllai }
886facf4a8dSllai
887facf4a8dSllai kmem_free(name, MAXPATHLEN);
888facf4a8dSllai pn_free(&pn);
889facf4a8dSllai }
890facf4a8dSllai
891facf4a8dSllai static int
copyin_nvlist(char * packed_usr,size_t packed_sz,nvlist_t ** nvlp)892facf4a8dSllai copyin_nvlist(char *packed_usr, size_t packed_sz, nvlist_t **nvlp)
893facf4a8dSllai {
894facf4a8dSllai int err = 0;
895facf4a8dSllai char *packed;
896facf4a8dSllai nvlist_t *profile = NULL;
897facf4a8dSllai
898facf4a8dSllai /* simple sanity check */
899facf4a8dSllai if (packed_usr == NULL || packed_sz == 0)
900*814e7298SToomas Soome return (err);
901facf4a8dSllai
902facf4a8dSllai /* copyin packed profile nvlist */
903facf4a8dSllai packed = kmem_alloc(packed_sz, KM_NOSLEEP);
904facf4a8dSllai if (packed == NULL)
905facf4a8dSllai return (ENOMEM);
906facf4a8dSllai err = copyin(packed_usr, packed, packed_sz);
907facf4a8dSllai
908facf4a8dSllai /* unpack packed profile nvlist */
909facf4a8dSllai if (err)
910facf4a8dSllai cmn_err(CE_WARN, "copyin_nvlist: copyin failed with "
911facf4a8dSllai "err %d\n", err);
912facf4a8dSllai else if (err = nvlist_unpack(packed, packed_sz, &profile, KM_NOSLEEP))
913facf4a8dSllai cmn_err(CE_WARN, "copyin_nvlist: nvlist_unpack "
914facf4a8dSllai "failed with err %d\n", err);
915facf4a8dSllai
916facf4a8dSllai kmem_free(packed, packed_sz);
917facf4a8dSllai if (err == 0)
918facf4a8dSllai *nvlp = profile;
919facf4a8dSllai return (err);
920facf4a8dSllai }
921facf4a8dSllai
922facf4a8dSllai /*
923facf4a8dSllai * Process profile passed down from libdevinfo. There are four types
924facf4a8dSllai * of matching rules:
925facf4a8dSllai * include: export a name or names matching a pattern
926facf4a8dSllai * exclude: exclude a name or names matching a pattern
927facf4a8dSllai * symlink: create a local symlink
928facf4a8dSllai * map: export a device with a name different from the global zone
929facf4a8dSllai * Note: We may consider supporting VOP_SYMLINK in non-global instances,
930facf4a8dSllai * because it does not present any security risk. For now, the fs
931facf4a8dSllai * instance is read only.
932facf4a8dSllai */
933facf4a8dSllai static void
sdev_process_profile(struct sdev_data * sdev_data,nvlist_t * profile)934facf4a8dSllai sdev_process_profile(struct sdev_data *sdev_data, nvlist_t *profile)
935facf4a8dSllai {
936facf4a8dSllai nvpair_t *nvpair;
937facf4a8dSllai char *nvname, *dname;
938facf4a8dSllai struct sdev_node *dir, *gdir;
939facf4a8dSllai char **pair; /* for symlinks and maps */
940facf4a8dSllai uint_t nelem;
941facf4a8dSllai int rv;
942facf4a8dSllai
943facf4a8dSllai gdir = sdev_origins->sdev_root; /* root of global /dev */
944facf4a8dSllai dir = sdev_data->sdev_root; /* root of current instance */
945facf4a8dSllai
946facf4a8dSllai ASSERT(profile);
947facf4a8dSllai
948facf4a8dSllai /* process nvpairs in the list */
949facf4a8dSllai nvpair = NULL;
950facf4a8dSllai while (nvpair = nvlist_next_nvpair(profile, nvpair)) {
951facf4a8dSllai nvname = nvpair_name(nvpair);
952facf4a8dSllai ASSERT(nvname != NULL);
953facf4a8dSllai
954facf4a8dSllai if (strcmp(nvname, SDEV_NVNAME_INCLUDE) == 0) {
955facf4a8dSllai rv = nvpair_value_string(nvpair, &dname);
956facf4a8dSllai if (rv != 0) {
957facf4a8dSllai cmn_err(CE_WARN, sdev_nvp_val_err,
958facf4a8dSllai rv, nvpair_name(nvpair));
959facf4a8dSllai break;
960facf4a8dSllai }
961facf4a8dSllai process_rule(dir, gdir, dname, NULL,
962facf4a8dSllai PROFILE_TYPE_INCLUDE);
963facf4a8dSllai } else if (strcmp(nvname, SDEV_NVNAME_EXCLUDE) == 0) {
964facf4a8dSllai rv = nvpair_value_string(nvpair, &dname);
965facf4a8dSllai if (rv != 0) {
966facf4a8dSllai cmn_err(CE_WARN, sdev_nvp_val_err,
967facf4a8dSllai rv, nvpair_name(nvpair));
968facf4a8dSllai break;
969facf4a8dSllai }
970facf4a8dSllai process_rule(dir, gdir, dname, NULL,
971facf4a8dSllai PROFILE_TYPE_EXCLUDE);
972facf4a8dSllai } else if (strcmp(nvname, SDEV_NVNAME_SYMLINK) == 0) {
973facf4a8dSllai rv = nvpair_value_string_array(nvpair, &pair, &nelem);
974facf4a8dSllai if (rv != 0) {
975facf4a8dSllai cmn_err(CE_WARN, sdev_nvp_val_err,
976facf4a8dSllai rv, nvpair_name(nvpair));
977facf4a8dSllai break;
978facf4a8dSllai }
979facf4a8dSllai ASSERT(nelem == 2);
980facf4a8dSllai process_rule(dir, gdir, pair[0], pair[1],
981facf4a8dSllai PROFILE_TYPE_SYMLINK);
982facf4a8dSllai } else if (strcmp(nvname, SDEV_NVNAME_MAP) == 0) {
983facf4a8dSllai rv = nvpair_value_string_array(nvpair, &pair, &nelem);
984facf4a8dSllai if (rv != 0) {
985facf4a8dSllai cmn_err(CE_WARN, sdev_nvp_val_err,
986facf4a8dSllai rv, nvpair_name(nvpair));
987facf4a8dSllai break;
988facf4a8dSllai }
989facf4a8dSllai process_rule(dir, gdir, pair[1], pair[0],
990facf4a8dSllai PROFILE_TYPE_MAP);
991facf4a8dSllai } else if (strcmp(nvname, SDEV_NVNAME_MOUNTPT) != 0) {
992facf4a8dSllai cmn_err(CE_WARN, "sdev_process_profile: invalid "
993facf4a8dSllai "nvpair %s\n", nvname);
994facf4a8dSllai }
995facf4a8dSllai }
996facf4a8dSllai }
997facf4a8dSllai
998facf4a8dSllai /*ARGSUSED*/
999facf4a8dSllai int
prof_lookup(vnode_t * dvp,char * nm,struct vnode ** vpp,struct cred * cred)1000facf4a8dSllai prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred)
1001facf4a8dSllai {
1002facf4a8dSllai struct sdev_node *ddv = VTOSDEV(dvp);
1003facf4a8dSllai struct sdev_node *dv;
1004facf4a8dSllai int nmlen;
1005facf4a8dSllai
1006facf4a8dSllai /*
1007facf4a8dSllai * Empty name or ., return node itself.
1008facf4a8dSllai */
1009facf4a8dSllai nmlen = strlen(nm);
1010facf4a8dSllai if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
1011facf4a8dSllai *vpp = SDEVTOV(ddv);
1012facf4a8dSllai VN_HOLD(*vpp);
1013facf4a8dSllai return (0);
1014facf4a8dSllai }
1015facf4a8dSllai
1016facf4a8dSllai /*
1017facf4a8dSllai * .., return the parent directory
1018facf4a8dSllai */
1019facf4a8dSllai if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
1020facf4a8dSllai *vpp = SDEVTOV(ddv->sdev_dotdot);
1021facf4a8dSllai VN_HOLD(*vpp);
1022facf4a8dSllai return (0);
1023facf4a8dSllai }
1024facf4a8dSllai
1025facf4a8dSllai rw_enter(&ddv->sdev_contents, RW_READER);
1026facf4a8dSllai dv = sdev_cache_lookup(ddv, nm);
1027facf4a8dSllai if (dv == NULL) {
1028facf4a8dSllai prof_filldir(ddv);
1029facf4a8dSllai dv = sdev_cache_lookup(ddv, nm);
1030facf4a8dSllai }
1031facf4a8dSllai rw_exit(&ddv->sdev_contents);
1032facf4a8dSllai if (dv == NULL) {
1033facf4a8dSllai sdcmn_err10(("prof_lookup: %s not found\n", nm));
1034facf4a8dSllai return (ENOENT);
1035facf4a8dSllai }
1036facf4a8dSllai
1037facf4a8dSllai return (sdev_to_vp(dv, vpp));
1038facf4a8dSllai }
1039facf4a8dSllai
1040facf4a8dSllai /*
1041facf4a8dSllai * This is invoked after a new filesystem is mounted to define the
1042facf4a8dSllai * name space. It is also invoked during normal system operation
1043facf4a8dSllai * to update the name space.
1044facf4a8dSllai *
1045facf4a8dSllai * Applications call di_prof_commit() in libdevinfo, which invokes
1046facf4a8dSllai * modctl(). modctl calls this function. The input is a packed nvlist.
1047facf4a8dSllai */
1048facf4a8dSllai int
devname_profile_update(char * packed,size_t packed_sz)1049facf4a8dSllai devname_profile_update(char *packed, size_t packed_sz)
1050facf4a8dSllai {
1051facf4a8dSllai char *mntpt;
1052facf4a8dSllai nvlist_t *nvl;
1053facf4a8dSllai nvpair_t *nvp;
1054facf4a8dSllai struct sdev_data *mntinfo;
1055facf4a8dSllai int err;
1056facf4a8dSllai int rv;
1057facf4a8dSllai
1058facf4a8dSllai nvl = NULL;
1059facf4a8dSllai if ((err = copyin_nvlist(packed, packed_sz, &nvl)) != 0)
1060facf4a8dSllai return (err);
1061facf4a8dSllai ASSERT(nvl);
1062facf4a8dSllai
1063facf4a8dSllai /* The first nvpair must be the mount point */
1064facf4a8dSllai nvp = nvlist_next_nvpair(nvl, NULL);
1065facf4a8dSllai if (strcmp(nvpair_name(nvp), SDEV_NVNAME_MOUNTPT) != 0) {
1066facf4a8dSllai cmn_err(CE_NOTE,
1067facf4a8dSllai "devname_profile_update: mount point not specified");
1068facf4a8dSllai nvlist_free(nvl);
1069facf4a8dSllai return (EINVAL);
1070facf4a8dSllai }
1071facf4a8dSllai
1072facf4a8dSllai /* find the matching filesystem instance */
1073facf4a8dSllai rv = nvpair_value_string(nvp, &mntpt);
1074facf4a8dSllai if (rv != 0) {
1075facf4a8dSllai cmn_err(CE_WARN, sdev_nvp_val_err,
1076facf4a8dSllai rv, nvpair_name(nvp));
1077facf4a8dSllai } else {
1078facf4a8dSllai mntinfo = sdev_find_mntinfo(mntpt);
1079facf4a8dSllai if (mntinfo == NULL) {
1080facf4a8dSllai cmn_err(CE_NOTE, "devname_profile_update: "
1081facf4a8dSllai " mount point %s not found", mntpt);
1082facf4a8dSllai nvlist_free(nvl);
1083facf4a8dSllai return (EINVAL);
1084facf4a8dSllai }
1085facf4a8dSllai
1086facf4a8dSllai /* now do the hardwork to process the profile */
1087facf4a8dSllai sdev_process_profile(mntinfo, nvl);
1088facf4a8dSllai
1089facf4a8dSllai sdev_mntinfo_rele(mntinfo);
1090facf4a8dSllai }
1091facf4a8dSllai
1092facf4a8dSllai nvlist_free(nvl);
1093facf4a8dSllai return (0);
1094facf4a8dSllai }
1095