Print this page

        

@@ -148,10 +148,16 @@
 };
 
 kmem_cache_t    *sdev_node_cache;       /* sdev_node cache */
 int             devtype;                /* fstype */
 
+/* static */
+static struct vnodeops *sdev_get_vop(struct sdev_node *);
+static void sdev_set_no_negcache(struct sdev_node *);
+static fs_operation_def_t *sdev_merge_vtab(const fs_operation_def_t []);
+static void sdev_free_vtab(fs_operation_def_t *);
+
 static void
 sdev_prof_free(struct sdev_node *dv)
 {
         ASSERT(!SDEV_IS_GLOBAL(dv));
         nvlist_free(dv->sdev_prof.dev_name);

@@ -305,11 +311,10 @@
         len = strlen(ddv->sdev_path) + strlen(nm) + 2;
         dv->sdev_path = kmem_alloc(len, KM_SLEEP);
         (void) snprintf(dv->sdev_path, len, "%s/%s", ddv->sdev_path, nm);
         /* overwritten for VLNK nodes */
         dv->sdev_symlink = NULL;
-        list_link_init(&dv->sdev_plist);
 
         vp = SDEVTOV(dv);
         vn_reinit(vp);
         vp->v_vfsp = SDEVTOV(ddv)->v_vfsp;
         if (vap)

@@ -394,11 +399,10 @@
                 dv->sdev_nlink = 1;
                 dv->sdev_symlink = i_ddi_strdup((char *)args, KM_SLEEP);
         } else {
                 dv->sdev_nlink = 1;
         }
-        sdev_plugin_nodeready(dv);
 
         if (!(SDEV_IS_GLOBAL(dv))) {
                 dv->sdev_origin = (struct sdev_node *)args;
                 dv->sdev_flags &= ~SDEV_PERSIST;
         }

@@ -491,26 +495,41 @@
         rw_exit(&dv->sdev_contents);
         sdev_nc_node_exists(dv);
         return (dv);
 }
 
-struct sdev_vop_table vtab[] = {
-        { "pts", devpts_vnodeops_tbl, &devpts_vnodeops, devpts_validate,
+/* directory dependent vop table */
+struct sdev_vop_table {
+        char *vt_name;                          /* subdirectory name */
+        const fs_operation_def_t *vt_service;   /* vnodeops table */
+        struct vnodeops *vt_vops;               /* constructed vop */
+        struct vnodeops **vt_global_vops;       /* global container for vop */
+        int (*vt_vtor)(struct sdev_node *);     /* validate sdev_node */
+        int vt_flags;
+};
+
+/*
+ * A nice improvement would be to provide a plug-in mechanism
+ * for this table instead of a const table.
+ */
+static struct sdev_vop_table vtab[] =
+{
+        { "pts", devpts_vnodeops_tbl, NULL, &devpts_vnodeops, devpts_validate,
         SDEV_DYNAMIC | SDEV_VTOR },
 
-        { "vt", devvt_vnodeops_tbl, &devvt_vnodeops, devvt_validate,
+        { "vt", devvt_vnodeops_tbl, NULL, &devvt_vnodeops, devvt_validate,
         SDEV_DYNAMIC | SDEV_VTOR },
 
-        { "zvol", devzvol_vnodeops_tbl, &devzvol_vnodeops,
+        { "zvol", devzvol_vnodeops_tbl, NULL, &devzvol_vnodeops,
         devzvol_validate, SDEV_ZONED | SDEV_DYNAMIC | SDEV_VTOR | SDEV_SUBDIR },
 
-        { "zcons", NULL, NULL, NULL, SDEV_NO_NCACHE },
+        { "zcons", NULL, NULL, NULL, NULL, SDEV_NO_NCACHE },
 
-        { "net", devnet_vnodeops_tbl, &devnet_vnodeops, devnet_validate,
-        SDEV_DYNAMIC | SDEV_VTOR | SDEV_SUBDIR },
+        { "net", devnet_vnodeops_tbl, NULL, &devnet_vnodeops, devnet_validate,
+        SDEV_DYNAMIC | SDEV_VTOR },
 
-        { "ipnet", devipnet_vnodeops_tbl, &devipnet_vnodeops,
+        { "ipnet", devipnet_vnodeops_tbl, NULL, &devipnet_vnodeops,
         devipnet_validate, SDEV_DYNAMIC | SDEV_VTOR | SDEV_NO_NCACHE },
 
         /*
          * SDEV_DYNAMIC: prevent calling out to devfsadm, since only the
          * lofi driver controls child nodes.

@@ -521,20 +540,138 @@
          * In addition, devfsadm knows not to attempt a rmdir: a zone
          * may hold a reference, which would zombify the node,
          * preventing a mkdir.
          */
 
-        { "lofi", NULL, NULL, NULL,
+        { "lofi", NULL, NULL, NULL, NULL,
             SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST },
-        { "rlofi", NULL, NULL, NULL,
+        { "rlofi", NULL, NULL, NULL, NULL,
             SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST },
 
-        { NULL, NULL, NULL, NULL, 0}
+        { NULL, NULL, NULL, NULL, NULL, 0}
 };
 
+/*
+ * We need to match off of the sdev_path, not the sdev_name. We are only allowed
+ * to exist directly under /dev.
+ */
+struct sdev_vop_table *
+sdev_match(struct sdev_node *dv)
+{
+        int vlen;
+        int i;
+        const char *path;
 
+        if (strlen(dv->sdev_path) <= 5)
+                return (NULL);
+
+        if (strncmp(dv->sdev_path, "/dev/", 5) != 0)
+                return (NULL);
+        path = dv->sdev_path + 5;
+
+        for (i = 0; vtab[i].vt_name; i++) {
+                if (strcmp(vtab[i].vt_name, path) == 0)
+                        return (&vtab[i]);
+                if (vtab[i].vt_flags & SDEV_SUBDIR) {
+                        vlen = strlen(vtab[i].vt_name);
+                        if ((strncmp(vtab[i].vt_name, path,
+                            vlen - 1) == 0) && path[vlen] == '/')
+                                return (&vtab[i]);
+                }
+
+        }
+        return (NULL);
+}
+
 /*
+ *  sets a directory's vnodeops if the directory is in the vtab;
+ */
+static struct vnodeops *
+sdev_get_vop(struct sdev_node *dv)
+{
+        struct sdev_vop_table *vtp;
+        char *path;
+
+        path = dv->sdev_path;
+        ASSERT(path);
+
+        /* gets the relative path to /dev/ */
+        path += 5;
+
+        /* gets the vtab entry it matches */
+        if ((vtp = sdev_match(dv)) != NULL) {
+                dv->sdev_flags |= vtp->vt_flags;
+                if (SDEV_IS_PERSIST(dv->sdev_dotdot) &&
+                    (SDEV_IS_PERSIST(dv) || !SDEV_IS_DYNAMIC(dv)))
+                        dv->sdev_flags |= SDEV_PERSIST;
+
+                if (vtp->vt_vops) {
+                        if (vtp->vt_global_vops)
+                                *(vtp->vt_global_vops) = vtp->vt_vops;
+
+                        return (vtp->vt_vops);
+                }
+
+                if (vtp->vt_service) {
+                        fs_operation_def_t *templ;
+                        templ = sdev_merge_vtab(vtp->vt_service);
+                        if (vn_make_ops(vtp->vt_name,
+                            (const fs_operation_def_t *)templ,
+                            &vtp->vt_vops) != 0) {
+                                cmn_err(CE_PANIC, "%s: malformed vnode ops\n",
+                                    vtp->vt_name);
+                                /*NOTREACHED*/
+                        }
+                        if (vtp->vt_global_vops) {
+                                *(vtp->vt_global_vops) = vtp->vt_vops;
+                        }
+                        sdev_free_vtab(templ);
+
+                        return (vtp->vt_vops);
+                }
+
+                return (sdev_vnodeops);
+        }
+
+        /* child inherits the persistence of the parent */
+        if (SDEV_IS_PERSIST(dv->sdev_dotdot))
+                dv->sdev_flags |= SDEV_PERSIST;
+
+        return (sdev_vnodeops);
+}
+
+static void
+sdev_set_no_negcache(struct sdev_node *dv)
+{
+        int i;
+        char *path;
+
+        ASSERT(dv->sdev_path);
+        path = dv->sdev_path + strlen("/dev/");
+
+        for (i = 0; vtab[i].vt_name; i++) {
+                if (strcmp(vtab[i].vt_name, path) == 0) {
+                        if (vtab[i].vt_flags & SDEV_NO_NCACHE)
+                                dv->sdev_flags |= SDEV_NO_NCACHE;
+                        break;
+                }
+        }
+}
+
+void *
+sdev_get_vtor(struct sdev_node *dv)
+{
+        struct sdev_vop_table *vtp;
+
+        vtp = sdev_match(dv);
+        if (vtp)
+                return ((void *)vtp->vt_vtor);
+        else
+                return (NULL);
+}
+
+/*
  * Build the base root inode
  */
 ino_t
 sdev_mkino(struct sdev_node *dv)
 {

@@ -808,15 +945,12 @@
         if (dv->sdev_path) {
                 kmem_free(dv->sdev_path, strlen(dv->sdev_path) + 1);
                 dv->sdev_path = NULL;
         }
 
-        if (!SDEV_IS_GLOBAL(dv)) {
+        if (!SDEV_IS_GLOBAL(dv))
                 sdev_prof_free(dv);
-                if (dv->sdev_vnode->v_type != VLNK && dv->sdev_origin != NULL)
-                        SDEV_RELE(dv->sdev_origin);
-        }
 
         if (SDEVTOV(dv)->v_type == VDIR) {
                 ASSERT(SDEV_FIRST_ENTRY(dv) == NULL);
                 avl_destroy(&dv->sdev_entries);
         }

@@ -2809,10 +2943,50 @@
                 VN_RELE(vp);
 
         return (error);
 }
 
+extern int sdev_vnodeops_tbl_size;
+
+/*
+ * construct a new template with overrides from vtab
+ */
+static fs_operation_def_t *
+sdev_merge_vtab(const fs_operation_def_t tab[])
+{
+        fs_operation_def_t *new;
+        const fs_operation_def_t *tab_entry;
+
+        /* make a copy of standard vnode ops table */
+        new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP);
+        bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size);
+
+        /* replace the overrides from tab */
+        for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) {
+                fs_operation_def_t *std_entry = new;
+                while (std_entry->name) {
+                        if (strcmp(tab_entry->name, std_entry->name) == 0) {
+                                std_entry->func = tab_entry->func;
+                                break;
+                        }
+                        std_entry++;
+                }
+                if (std_entry->name == NULL)
+                        cmn_err(CE_NOTE, "sdev_merge_vtab: entry %s unused.",
+                            tab_entry->name);
+        }
+
+        return (new);
+}
+
+/* free memory allocated by sdev_merge_vtab */
+static void
+sdev_free_vtab(fs_operation_def_t *new)
+{
+        kmem_free(new, sdev_vnodeops_tbl_size);
+}
+
 /*
  * a generic setattr() function
  *
  * note: flags only supports AT_UID and AT_GID.
  *       Future enhancements can be done for other types, e.g. AT_MODE