linux/fs/openpromfs/inode.c
<<
>>
Prefs
   1/* inode.c: /proc/openprom handling routines
   2 *
   3 * Copyright (C) 1996-1999 Jakub Jelinek  (jakub@redhat.com)
   4 * Copyright (C) 1998      Eddie C. Dost  (ecd@skynet.be)
   5 */
   6
   7#include <linux/module.h>
   8#include <linux/types.h>
   9#include <linux/string.h>
  10#include <linux/fs.h>
  11#include <linux/init.h>
  12#include <linux/slab.h>
  13#include <linux/seq_file.h>
  14#include <linux/magic.h>
  15
  16#include <asm/openprom.h>
  17#include <asm/oplib.h>
  18#include <asm/prom.h>
  19#include <asm/uaccess.h>
  20
  21static DEFINE_MUTEX(op_mutex);
  22
  23#define OPENPROM_ROOT_INO       0
  24
  25enum op_inode_type {
  26        op_inode_node,
  27        op_inode_prop,
  28};
  29
  30union op_inode_data {
  31        struct device_node      *node;
  32        struct property         *prop;
  33};
  34
  35struct op_inode_info {
  36        struct inode            vfs_inode;
  37        enum op_inode_type      type;
  38        union op_inode_data     u;
  39};
  40
  41static struct inode *openprom_iget(struct super_block *sb, ino_t ino);
  42
  43static inline struct op_inode_info *OP_I(struct inode *inode)
  44{
  45        return container_of(inode, struct op_inode_info, vfs_inode);
  46}
  47
  48static int is_string(unsigned char *p, int len)
  49{
  50        int i;
  51
  52        for (i = 0; i < len; i++) {
  53                unsigned char val = p[i];
  54
  55                if ((i && !val) ||
  56                    (val >= ' ' && val <= '~'))
  57                        continue;
  58
  59                return 0;
  60        }
  61
  62        return 1;
  63}
  64
  65static int property_show(struct seq_file *f, void *v)
  66{
  67        struct property *prop = f->private;
  68        void *pval;
  69        int len;
  70
  71        len = prop->length;
  72        pval = prop->value;
  73
  74        if (is_string(pval, len)) {
  75                while (len > 0) {
  76                        int n = strlen(pval);
  77
  78                        seq_printf(f, "%s", (char *) pval);
  79
  80                        /* Skip over the NULL byte too.  */
  81                        pval += n + 1;
  82                        len -= n + 1;
  83
  84                        if (len > 0)
  85                                seq_printf(f, " + ");
  86                }
  87        } else {
  88                if (len & 3) {
  89                        while (len) {
  90                                len--;
  91                                if (len)
  92                                        seq_printf(f, "%02x.",
  93                                                   *(unsigned char *) pval);
  94                                else
  95                                        seq_printf(f, "%02x",
  96                                                   *(unsigned char *) pval);
  97                                pval++;
  98                        }
  99                } else {
 100                        while (len >= 4) {
 101                                len -= 4;
 102
 103                                if (len)
 104                                        seq_printf(f, "%08x.",
 105                                                   *(unsigned int *) pval);
 106                                else
 107                                        seq_printf(f, "%08x",
 108                                                   *(unsigned int *) pval);
 109                                pval += 4;
 110                        }
 111                }
 112        }
 113        seq_printf(f, "\n");
 114
 115        return 0;
 116}
 117
 118static void *property_start(struct seq_file *f, loff_t *pos)
 119{
 120        if (*pos == 0)
 121                return pos;
 122        return NULL;
 123}
 124
 125static void *property_next(struct seq_file *f, void *v, loff_t *pos)
 126{
 127        (*pos)++;
 128        return NULL;
 129}
 130
 131static void property_stop(struct seq_file *f, void *v)
 132{
 133        /* Nothing to do */
 134}
 135
 136static const struct seq_operations property_op = {
 137        .start          = property_start,
 138        .next           = property_next,
 139        .stop           = property_stop,
 140        .show           = property_show
 141};
 142
 143static int property_open(struct inode *inode, struct file *file)
 144{
 145        struct op_inode_info *oi = OP_I(inode);
 146        int ret;
 147
 148        BUG_ON(oi->type != op_inode_prop);
 149
 150        ret = seq_open(file, &property_op);
 151        if (!ret) {
 152                struct seq_file *m = file->private_data;
 153                m->private = oi->u.prop;
 154        }
 155        return ret;
 156}
 157
 158static const struct file_operations openpromfs_prop_ops = {
 159        .open           = property_open,
 160        .read           = seq_read,
 161        .llseek         = seq_lseek,
 162        .release        = seq_release,
 163};
 164
 165static int openpromfs_readdir(struct file *, void *, filldir_t);
 166
 167static const struct file_operations openprom_operations = {
 168        .read           = generic_read_dir,
 169        .readdir        = openpromfs_readdir,
 170        .llseek         = generic_file_llseek,
 171};
 172
 173static struct dentry *openpromfs_lookup(struct inode *, struct dentry *, struct nameidata *);
 174
 175static const struct inode_operations openprom_inode_operations = {
 176        .lookup         = openpromfs_lookup,
 177};
 178
 179static struct dentry *openpromfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
 180{
 181        struct op_inode_info *ent_oi, *oi = OP_I(dir);
 182        struct device_node *dp, *child;
 183        struct property *prop;
 184        enum op_inode_type ent_type;
 185        union op_inode_data ent_data;
 186        const char *name;
 187        struct inode *inode;
 188        unsigned int ino;
 189        int len;
 190        
 191        BUG_ON(oi->type != op_inode_node);
 192
 193        dp = oi->u.node;
 194
 195        name = dentry->d_name.name;
 196        len = dentry->d_name.len;
 197
 198        mutex_lock(&op_mutex);
 199
 200        child = dp->child;
 201        while (child) {
 202                int n = strlen(child->path_component_name);
 203
 204                if (len == n &&
 205                    !strncmp(child->path_component_name, name, len)) {
 206                        ent_type = op_inode_node;
 207                        ent_data.node = child;
 208                        ino = child->unique_id;
 209                        goto found;
 210                }
 211                child = child->sibling;
 212        }
 213
 214        prop = dp->properties;
 215        while (prop) {
 216                int n = strlen(prop->name);
 217
 218                if (len == n && !strncmp(prop->name, name, len)) {
 219                        ent_type = op_inode_prop;
 220                        ent_data.prop = prop;
 221                        ino = prop->unique_id;
 222                        goto found;
 223                }
 224
 225                prop = prop->next;
 226        }
 227
 228        mutex_unlock(&op_mutex);
 229        return ERR_PTR(-ENOENT);
 230
 231found:
 232        inode = openprom_iget(dir->i_sb, ino);
 233        mutex_unlock(&op_mutex);
 234        if (IS_ERR(inode))
 235                return ERR_CAST(inode);
 236        ent_oi = OP_I(inode);
 237        ent_oi->type = ent_type;
 238        ent_oi->u = ent_data;
 239
 240        switch (ent_type) {
 241        case op_inode_node:
 242                inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
 243                inode->i_op = &openprom_inode_operations;
 244                inode->i_fop = &openprom_operations;
 245                inode->i_nlink = 2;
 246                break;
 247        case op_inode_prop:
 248                if (!strcmp(dp->name, "options") && (len == 17) &&
 249                    !strncmp (name, "security-password", 17))
 250                        inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
 251                else
 252                        inode->i_mode = S_IFREG | S_IRUGO;
 253                inode->i_fop = &openpromfs_prop_ops;
 254                inode->i_nlink = 1;
 255                inode->i_size = ent_oi->u.prop->length;
 256                break;
 257        }
 258
 259        d_add(dentry, inode);
 260        return NULL;
 261}
 262
 263static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
 264{
 265        struct inode *inode = filp->f_path.dentry->d_inode;
 266        struct op_inode_info *oi = OP_I(inode);
 267        struct device_node *dp = oi->u.node;
 268        struct device_node *child;
 269        struct property *prop;
 270        unsigned int ino;
 271        int i;
 272
 273        mutex_lock(&op_mutex);
 274        
 275        ino = inode->i_ino;
 276        i = filp->f_pos;
 277        switch (i) {
 278        case 0:
 279                if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
 280                        goto out;
 281                i++;
 282                filp->f_pos++;
 283                /* fall thru */
 284        case 1:
 285                if (filldir(dirent, "..", 2, i,
 286                            (dp->parent == NULL ?
 287                             OPENPROM_ROOT_INO :
 288                             dp->parent->unique_id), DT_DIR) < 0) 
 289                        goto out;
 290                i++;
 291                filp->f_pos++;
 292                /* fall thru */
 293        default:
 294                i -= 2;
 295
 296                /* First, the children nodes as directories.  */
 297                child = dp->child;
 298                while (i && child) {
 299                        child = child->sibling;
 300                        i--;
 301                }
 302                while (child) {
 303                        if (filldir(dirent,
 304                                    child->path_component_name,
 305                                    strlen(child->path_component_name),
 306                                    filp->f_pos, child->unique_id, DT_DIR) < 0)
 307                                goto out;
 308
 309                        filp->f_pos++;
 310                        child = child->sibling;
 311                }
 312
 313                /* Next, the properties as files.  */
 314                prop = dp->properties;
 315                while (i && prop) {
 316                        prop = prop->next;
 317                        i--;
 318                }
 319                while (prop) {
 320                        if (filldir(dirent, prop->name, strlen(prop->name),
 321                                    filp->f_pos, prop->unique_id, DT_REG) < 0)
 322                                goto out;
 323
 324                        filp->f_pos++;
 325                        prop = prop->next;
 326                }
 327        }
 328out:
 329        mutex_unlock(&op_mutex);
 330        return 0;
 331}
 332
 333static struct kmem_cache *op_inode_cachep;
 334
 335static struct inode *openprom_alloc_inode(struct super_block *sb)
 336{
 337        struct op_inode_info *oi;
 338
 339        oi = kmem_cache_alloc(op_inode_cachep, GFP_KERNEL);
 340        if (!oi)
 341                return NULL;
 342
 343        return &oi->vfs_inode;
 344}
 345
 346static void openprom_i_callback(struct rcu_head *head)
 347{
 348        struct inode *inode = container_of(head, struct inode, i_rcu);
 349        INIT_LIST_HEAD(&inode->i_dentry);
 350        kmem_cache_free(op_inode_cachep, OP_I(inode));
 351}
 352
 353static void openprom_destroy_inode(struct inode *inode)
 354{
 355        call_rcu(&inode->i_rcu, openprom_i_callback);
 356}
 357
 358static struct inode *openprom_iget(struct super_block *sb, ino_t ino)
 359{
 360        struct inode *inode;
 361
 362        inode = iget_locked(sb, ino);
 363        if (!inode)
 364                return ERR_PTR(-ENOMEM);
 365        if (inode->i_state & I_NEW) {
 366                inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
 367                if (inode->i_ino == OPENPROM_ROOT_INO) {
 368                        inode->i_op = &openprom_inode_operations;
 369                        inode->i_fop = &openprom_operations;
 370                        inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
 371                }
 372                unlock_new_inode(inode);
 373        }
 374        return inode;
 375}
 376
 377static int openprom_remount(struct super_block *sb, int *flags, char *data)
 378{
 379        *flags |= MS_NOATIME;
 380        return 0;
 381}
 382
 383static const struct super_operations openprom_sops = {
 384        .alloc_inode    = openprom_alloc_inode,
 385        .destroy_inode  = openprom_destroy_inode,
 386        .statfs         = simple_statfs,
 387        .remount_fs     = openprom_remount,
 388};
 389
 390static int openprom_fill_super(struct super_block *s, void *data, int silent)
 391{
 392        struct inode *root_inode;
 393        struct op_inode_info *oi;
 394        int ret;
 395
 396        s->s_flags |= MS_NOATIME;
 397        s->s_blocksize = 1024;
 398        s->s_blocksize_bits = 10;
 399        s->s_magic = OPENPROM_SUPER_MAGIC;
 400        s->s_op = &openprom_sops;
 401        s->s_time_gran = 1;
 402        root_inode = openprom_iget(s, OPENPROM_ROOT_INO);
 403        if (IS_ERR(root_inode)) {
 404                ret = PTR_ERR(root_inode);
 405                goto out_no_root;
 406        }
 407
 408        oi = OP_I(root_inode);
 409        oi->type = op_inode_node;
 410        oi->u.node = of_find_node_by_path("/");
 411
 412        s->s_root = d_alloc_root(root_inode);
 413        if (!s->s_root)
 414                goto out_no_root_dentry;
 415        return 0;
 416
 417out_no_root_dentry:
 418        iput(root_inode);
 419        ret = -ENOMEM;
 420out_no_root:
 421        printk("openprom_fill_super: get root inode failed\n");
 422        return ret;
 423}
 424
 425static struct dentry *openprom_mount(struct file_system_type *fs_type,
 426        int flags, const char *dev_name, void *data)
 427{
 428        return mount_single(fs_type, flags, data, openprom_fill_super);
 429}
 430
 431static struct file_system_type openprom_fs_type = {
 432        .owner          = THIS_MODULE,
 433        .name           = "openpromfs",
 434        .mount          = openprom_mount,
 435        .kill_sb        = kill_anon_super,
 436};
 437
 438static void op_inode_init_once(void *data)
 439{
 440        struct op_inode_info *oi = (struct op_inode_info *) data;
 441
 442        inode_init_once(&oi->vfs_inode);
 443}
 444
 445static int __init init_openprom_fs(void)
 446{
 447        int err;
 448
 449        op_inode_cachep = kmem_cache_create("op_inode_cache",
 450                                            sizeof(struct op_inode_info),
 451                                            0,
 452                                            (SLAB_RECLAIM_ACCOUNT |
 453                                             SLAB_MEM_SPREAD),
 454                                            op_inode_init_once);
 455        if (!op_inode_cachep)
 456                return -ENOMEM;
 457
 458        err = register_filesystem(&openprom_fs_type);
 459        if (err)
 460                kmem_cache_destroy(op_inode_cachep);
 461
 462        return err;
 463}
 464
 465static void __exit exit_openprom_fs(void)
 466{
 467        unregister_filesystem(&openprom_fs_type);
 468        kmem_cache_destroy(op_inode_cachep);
 469}
 470
 471module_init(init_openprom_fs)
 472module_exit(exit_openprom_fs)
 473MODULE_LICENSE("GPL");
 474