.notifier_call = cpuid_class_cpu_callback,
 };
 
-static char *cpuid_nodename(struct device *dev)
+static char *cpuid_devnode(struct device *dev, mode_t *mode)
 {
        return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt));
 }
                err = PTR_ERR(cpuid_class);
                goto out_chrdev;
        }
-       cpuid_class->nodename = cpuid_nodename;
+       cpuid_class->devnode = cpuid_devnode;
        for_each_online_cpu(i) {
                err = cpuid_device_create(i);
                if (err != 0)
 
 static struct miscdevice microcode_dev = {
        .minor                  = MICROCODE_MINOR,
        .name                   = "microcode",
-       .devnode                = "cpu/microcode",
+       .nodename               = "cpu/microcode",
        .fops                   = µcode_fops,
 };
 
 
        .notifier_call = msr_class_cpu_callback,
 };
 
-static char *msr_nodename(struct device *dev)
+static char *msr_devnode(struct device *dev, mode_t *mode)
 {
        return kasprintf(GFP_KERNEL, "cpu/%u/msr", MINOR(dev->devt));
 }
                err = PTR_ERR(msr_class);
                goto out_chrdev;
        }
-       msr_class->nodename = msr_nodename;
+       msr_class->devnode = msr_devnode;
        for_each_online_cpu(i) {
                err = msr_device_create(i);
                if (err != 0)
 
 
 static struct cdev bsg_cdev;
 
-static char *bsg_nodename(struct device *dev)
+static char *bsg_devnode(struct device *dev, mode_t *mode)
 {
        return kasprintf(GFP_KERNEL, "bsg/%s", dev_name(dev));
 }
                ret = PTR_ERR(bsg_class);
                goto destroy_kmemcache;
        }
-       bsg_class->nodename = bsg_nodename;
+       bsg_class->devnode = bsg_devnode;
 
        ret = alloc_chrdev_region(&devid, 0, BSG_MAX_DEVS, "bsg");
        if (ret)
 
        .name           = "block",
 };
 
-static char *block_nodename(struct device *dev)
+static char *block_devnode(struct device *dev, mode_t *mode)
 {
        struct gendisk *disk = dev_to_disk(dev);
 
-       if (disk->nodename)
-               return disk->nodename(disk);
+       if (disk->devnode)
+               return disk->devnode(disk, mode);
        return NULL;
 }
 
        .name           = "disk",
        .groups         = disk_attr_groups,
        .release        = disk_release,
-       .nodename       = block_nodename,
+       .devnode        = block_devnode,
 };
 
 #ifdef CONFIG_PROC_FS
 
        if (MAJOR(dev->devt)) {
                const char *tmp;
                const char *name;
+               mode_t mode = 0;
 
                add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt));
                add_uevent_var(env, "MINOR=%u", MINOR(dev->devt));
-               name = device_get_nodename(dev, &tmp);
+               name = device_get_devnode(dev, &mode, &tmp);
                if (name) {
                        add_uevent_var(env, "DEVNAME=%s", name);
                        kfree(tmp);
+                       if (mode)
+                               add_uevent_var(env, "DEVMODE=%#o", mode & 0777);
                }
        }
 
 }
 
 /**
- * device_get_nodename - path of device node file
+ * device_get_devnode - path of device node file
  * @dev: device
+ * @mode: returned file access mode
  * @tmp: possibly allocated string
  *
  * Return the relative path of a possible device node.
  * a name. This memory is returned in tmp and needs to be
  * freed by the caller.
  */
-const char *device_get_nodename(struct device *dev, const char **tmp)
+const char *device_get_devnode(struct device *dev,
+                              mode_t *mode, const char **tmp)
 {
        char *s;
 
        *tmp = NULL;
 
        /* the device type may provide a specific name */
-       if (dev->type && dev->type->nodename)
-               *tmp = dev->type->nodename(dev);
+       if (dev->type && dev->type->devnode)
+               *tmp = dev->type->devnode(dev, mode);
        if (*tmp)
                return *tmp;
 
        /* the class may provide a specific name */
-       if (dev->class && dev->class->nodename)
-               *tmp = dev->class->nodename(dev);
+       if (dev->class && dev->class->devnode)
+               *tmp = dev->class->devnode(dev, mode);
        if (*tmp)
                return *tmp;
 
 
  * During bootup, before any driver core device is registered,
  * devtmpfs, a tmpfs-based filesystem is created. Every driver-core
  * device which requests a device node, will add a node in this
- * filesystem. The node is named after the the name of the device,
- * or the susbsytem can provide a custom name. All devices are
- * owned by root and have a mode of 0600.
+ * filesystem.
+ * By default, all devices are named after the the name of the
+ * device, owned by root and have a default mode of 0600. Subsystems
+ * can overwrite the default setting if needed.
  */
 
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/shmem_fs.h>
 #include <linux/cred.h>
+#include <linux/sched.h>
 #include <linux/init_task.h>
 
 static struct vfsmount *dev_mnt;
        const char *tmp = NULL;
        const char *nodename;
        const struct cred *curr_cred;
-       mode_t mode;
+       mode_t mode = 0;
        struct nameidata nd;
        struct dentry *dentry;
        int err;
        if (!dev_mnt)
                return 0;
 
-       nodename = device_get_nodename(dev, &tmp);
+       nodename = device_get_devnode(dev, &mode, &tmp);
        if (!nodename)
                return -ENOMEM;
 
+       if (mode == 0)
+               mode = 0600;
        if (is_blockdev(dev))
-               mode = S_IFBLK|0600;
+               mode |= S_IFBLK;
        else
-               mode = S_IFCHR|0600;
+               mode |= S_IFCHR;
 
        curr_cred = override_creds(&init_cred);
        err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
 
        dentry = lookup_create(&nd, 0);
        if (!IS_ERR(dentry)) {
+               int umask;
+
+               umask = sys_umask(0000);
                err = vfs_mknod(nd.path.dentry->d_inode,
                                dentry, mode, dev->devt);
+               sys_umask(umask);
                /* mark as kernel created inode */
                if (!err)
                        dentry->d_inode->i_private = &dev_mnt;
        if (!dev_mnt)
                return 0;
 
-       nodename = device_get_nodename(dev, &tmp);
+       nodename = device_get_devnode(dev, NULL, &tmp);
        if (!nodename)
                return -ENOMEM;
 
 
        .owner = THIS_MODULE,
 };
 
-static char *aoe_nodename(struct device *dev)
+static char *aoe_devnode(struct device *dev, mode_t *mode)
 {
        return kasprintf(GFP_KERNEL, "etherd/%s", dev_name(dev));
 }
                unregister_chrdev(AOE_MAJOR, "aoechr");
                return PTR_ERR(aoe_class);
        }
-       aoe_class->nodename = aoe_nodename;
+       aoe_class->devnode = aoe_devnode;
 
        for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
                device_create(aoe_class, NULL,
 
        .media_changed =        pkt_media_changed,
 };
 
-static char *pktcdvd_nodename(struct gendisk *gd)
+static char *pktcdvd_devnode(struct gendisk *gd, mode_t *mode)
 {
        return kasprintf(GFP_KERNEL, "pktcdvd/%s", gd->disk_name);
 }
        disk->fops = &pktcdvd_ops;
        disk->flags = GENHD_FL_REMOVABLE;
        strcpy(disk->disk_name, pd->name);
-       disk->nodename = pktcdvd_nodename;
+       disk->devnode = pktcdvd_devnode;
        disk->private_data = pd;
        disk->queue = blk_alloc_queue(GFP_KERNEL);
        if (!disk->queue)
 static struct miscdevice pkt_misc = {
        .minor          = MISC_DYNAMIC_MINOR,
        .name           = DRIVER_NAME,
-       .name           = "pktcdvd/control",
+       .nodename       = "pktcdvd/control",
        .fops           = &pkt_ctl_fops
 };
 
 
 static struct miscdevice rng_miscdev = {
        .minor          = RNG_MISCDEV_MINOR,
        .name           = RNG_MODULE_NAME,
-       .devnode        = "hwrng",
+       .nodename       = "hwrng",
        .fops           = &rng_chrdev_ops,
 };
 
 
 
 static const struct memdev {
        const char *name;
+       mode_t mode;
        const struct file_operations *fops;
        struct backing_dev_info *dev_info;
 } devlist[] = {
-       [ 1] = { "mem", &mem_fops, &directly_mappable_cdev_bdi },
+        [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi },
 #ifdef CONFIG_DEVKMEM
-       [ 2] = { "kmem", &kmem_fops, &directly_mappable_cdev_bdi },
+        [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi },
 #endif
-       [ 3] = {"null", &null_fops, NULL },
+        [3] = { "null", 0666, &null_fops, NULL },
 #ifdef CONFIG_DEVPORT
-       [ 4] = { "port", &port_fops, NULL },
+        [4] = { "port", 0, &port_fops, NULL },
 #endif
-       [ 5] = { "zero", &zero_fops, &zero_bdi },
-       [ 7] = { "full", &full_fops, NULL },
-       [ 8] = { "random", &random_fops, NULL },
-       [ 9] = { "urandom", &urandom_fops, NULL },
-       [11] = { "kmsg", &kmsg_fops, NULL },
+        [5] = { "zero", 0666, &zero_fops, &zero_bdi },
+        [7] = { "full", 0666, &full_fops, NULL },
+        [8] = { "random", 0666, &random_fops, NULL },
+        [9] = { "urandom", 0666, &urandom_fops, NULL },
+       [11] = { "kmsg", 0, &kmsg_fops, NULL },
 #ifdef CONFIG_CRASH_DUMP
-       [12] = { "oldmem", &oldmem_fops, NULL },
+       [12] = { "oldmem", 0, &oldmem_fops, NULL },
 #endif
 };
 
        .open           = memory_open,
 };
 
+static char *mem_devnode(struct device *dev, mode_t *mode)
+{
+       if (mode && devlist[MINOR(dev->devt)].mode)
+               *mode = devlist[MINOR(dev->devt)].mode;
+       return NULL;
+}
+
 static struct class *mem_class;
 
 static int __init chr_dev_init(void)
                printk("unable to get major %d for memory devs\n", MEM_MAJOR);
 
        mem_class = class_create(THIS_MODULE, "mem");
+       mem_class->devnode = mem_devnode;
        for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) {
                if (!devlist[minor].name)
                        continue;
 
 EXPORT_SYMBOL(misc_register);
 EXPORT_SYMBOL(misc_deregister);
 
-static char *misc_nodename(struct device *dev)
+static char *misc_devnode(struct device *dev, mode_t *mode)
 {
        struct miscdevice *c = dev_get_drvdata(dev);
 
-       if (c->devnode)
-               return kstrdup(c->devnode, GFP_KERNEL);
+       if (mode && c->mode)
+               *mode = c->mode;
+       if (c->nodename)
+               return kstrdup(c->nodename, GFP_KERNEL);
        return NULL;
 }
 
        err = -EIO;
        if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
                goto fail_printk;
-       misc_class->nodename = misc_nodename;
+       misc_class->devnode = misc_devnode;
        return 0;
 
 fail_printk:
 
 
 static struct cdev raw_cdev;
 
-static char *raw_nodename(struct device *dev)
+static char *raw_devnode(struct device *dev, mode_t *mode)
 {
        return kasprintf(GFP_KERNEL, "raw/%s", dev_name(dev));
 }
                ret = PTR_ERR(raw_class);
                goto error_region;
        }
-       raw_class->nodename = raw_nodename;
+       raw_class->devnode = raw_devnode;
        device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl");
 
        return 0;
 
        }
 }
 
+static char *tty_devnode(struct device *dev, mode_t *mode)
+{
+       if (!mode)
+               return NULL;
+       if (dev->devt == MKDEV(TTYAUX_MAJOR, 0) ||
+           dev->devt == MKDEV(TTYAUX_MAJOR, 2))
+               *mode = 0666;
+       return NULL;
+}
+
 static int __init tty_class_init(void)
 {
        tty_class = class_create(THIS_MODULE, "tty");
        if (IS_ERR(tty_class))
                return PTR_ERR(tty_class);
+       tty_class->devnode = tty_devnode;
        return 0;
 }
 
 
                       CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE);
 }
 
-static char *drm_nodename(struct device *dev)
+static char *drm_devnode(struct device *dev, mode_t *mode)
 {
        return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev));
 }
        if (err)
                goto err_out_class;
 
-       class->nodename = drm_nodename;
+       class->devnode = drm_devnode;
 
        return class;
 
 
 #endif
 };
 
-static char *hiddev_nodename(struct device *dev)
+static char *hiddev_devnode(struct device *dev, mode_t *mode)
 {
        return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
 }
 
 static struct usb_class_driver hiddev_class = {
        .name =         "hiddev%d",
-       .nodename =     hiddev_nodename,
+       .devnode =      hiddev_devnode,
        .fops =         &hiddev_fops,
        .minor_base =   HIDDEV_MINOR_BASE,
 };
 
        .uevent         = input_dev_uevent,
 };
 
-static char *input_nodename(struct device *dev)
+static char *input_devnode(struct device *dev, mode_t *mode)
 {
        return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev));
 }
 
 struct class input_class = {
        .name           = "input",
-       .nodename       = input_nodename,
+       .devnode        = input_devnode,
 };
 EXPORT_SYMBOL_GPL(input_class);
 
 
 static struct miscdevice _dm_misc = {
        .minor          = MISC_DYNAMIC_MINOR,
        .name           = DM_NAME,
-       .devnode        = "mapper/control",
+       .nodename       = "mapper/control",
        .fops           = &_ctl_fops
 };
 
 
        return 0;
 }
 
-static char *dvb_nodename(struct device *dev)
+static char *dvb_devnode(struct device *dev, mode_t *mode)
 {
        struct dvb_device *dvbdev = dev_get_drvdata(dev);
 
                goto error;
        }
        dvb_class->dev_uevent = dvb_uevent;
-       dvb_class->nodename = dvb_nodename;
+       dvb_class->devnode = dvb_devnode;
        return 0;
 
 error:
 
 static struct miscdevice tun_miscdev = {
        .minor = TUN_MINOR,
        .name = "tun",
-       .devnode = "net/tun",
+       .nodename = "net/tun",
        .fops = &tun_fops,
 };
 
 
        .release =      usblp_release,
 };
 
-static char *usblp_nodename(struct device *dev)
+static char *usblp_devnode(struct device *dev, mode_t *mode)
 {
        return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
 }
 
 static struct usb_class_driver usblp_class = {
        .name =         "lp%d",
-       .nodename =     usblp_nodename,
+       .devnode =      usblp_devnode,
        .fops =         &usblp_fops,
        .minor_base =   USBLP_MINOR_BASE,
 };
 
        struct class *class;
 } *usb_class;
 
-static char *usb_nodename(struct device *dev)
+static char *usb_devnode(struct device *dev, mode_t *mode)
 {
        struct usb_class_driver *drv;
 
        drv = dev_get_drvdata(dev);
-       if (!drv || !drv->nodename)
+       if (!drv || !drv->devnode)
                return NULL;
-       return drv->nodename(dev);
+       return drv->devnode(dev, mode);
 }
 
 static int init_usb_class(void)
                kfree(usb_class);
                usb_class = NULL;
        }
-       usb_class->class->nodename = usb_nodename;
+       usb_class->class->devnode = usb_devnode;
 
 exit:
        return result;
 
 #endif /* CONFIG_PM */
 
 
-static char *usb_nodename(struct device *dev)
+static char *usb_devnode(struct device *dev, mode_t *mode)
 {
        struct usb_device *usb_dev;
 
        .name =         "usb_device",
        .release =      usb_release_dev,
        .uevent =       usb_dev_uevent,
-       .nodename =     usb_nodename,
+       .devnode =      usb_devnode,
        .pm =           &usb_device_pm_ops,
 };
 
 
        .poll = iowarrior_poll,
 };
 
-static char *iowarrior_nodename(struct device *dev)
+static char *iowarrior_devnode(struct device *dev, mode_t *mode)
 {
        return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
 }
  */
 static struct usb_class_driver iowarrior_class = {
        .name = "iowarrior%d",
-       .nodename = iowarrior_nodename,
+       .devnode = iowarrior_devnode,
        .fops = &iowarrior_fops,
        .minor_base = IOWARRIOR_MINOR_BASE,
 };
 
        .llseek =       tower_llseek,
 };
 
-static char *legousbtower_nodename(struct device *dev)
+static char *legousbtower_devnode(struct device *dev, mode_t *mode)
 {
        return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
 }
  */
 static struct usb_class_driver tower_class = {
        .name =         "legousbtower%d",
-       .nodename =     legousbtower_nodename,
+       .devnode =      legousbtower_devnode,
        .fops =         &tower_fops,
        .minor_base =   LEGO_USB_TOWER_MINOR_BASE,
 };
 
        struct kobject                  *dev_kobj;
 
        int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
-       char *(*nodename)(struct device *dev);
+       char *(*devnode)(struct device *dev, mode_t *mode);
 
        void (*class_release)(struct class *class);
        void (*dev_release)(struct device *dev);
        const char *name;
        const struct attribute_group **groups;
        int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
-       char *(*nodename)(struct device *dev);
+       char *(*devnode)(struct device *dev, mode_t *mode);
        void (*release)(struct device *dev);
 
        const struct dev_pm_ops *pm;
 extern int device_rename(struct device *dev, char *new_name);
 extern int device_move(struct device *dev, struct device *new_parent,
                       enum dpm_order dpm_order);
-extern const char *device_get_nodename(struct device *dev, const char **tmp);
+extern const char *device_get_devnode(struct device *dev,
+                                     mode_t *mode, const char **tmp);
 extern void *dev_get_drvdata(const struct device *dev);
 extern void dev_set_drvdata(struct device *dev, void *data);
 
 
                                          * disks that can't be partitioned. */
 
        char disk_name[DISK_NAME_LEN];  /* name of major driver */
-       char *(*nodename)(struct gendisk *gd);
+       char *(*devnode)(struct gendisk *gd, mode_t *mode);
        /* Array of pointers to partitions indexed by partno.
         * Protected with matching bdev lock but stat and other
         * non-critical accesses use RCU.  Always access through
 
        struct list_head list;
        struct device *parent;
        struct device *this_device;
-       const char *devnode;
+       const char *nodename;
+       mode_t mode;
 };
 
 extern int misc_register(struct miscdevice * misc);
 
 /**
  * struct usb_class_driver - identifies a USB driver that wants to use the USB major number
  * @name: the usb class device name for this driver.  Will show up in sysfs.
- * @nodename: Callback to provide a naming hint for a possible
+ * @devnode: Callback to provide a naming hint for a possible
  *     device node to create.
  * @fops: pointer to the struct file_operations of this driver.
  * @minor_base: the start of the minor range for this driver.
  */
 struct usb_class_driver {
        char *name;
-       char *(*nodename)(struct device *dev);
+       char *(*devnode)(struct device *dev, mode_t *mode);
        const struct file_operations *fops;
        int minor_base;
 };
 
 MODULE_AUTHOR("Alan Cox");
 MODULE_LICENSE("GPL");
 
-static char *sound_nodename(struct device *dev)
+static char *sound_devnode(struct device *dev, mode_t *mode)
 {
        if (MAJOR(dev->devt) == SOUND_MAJOR)
                return NULL;
                return PTR_ERR(sound_class);
        }
 
-       sound_class->nodename = sound_nodename;
+       sound_class->devnode = sound_devnode;
 
        return 0;
 }