/* $Id: capifs.c,v 1.1.4.1 2001/11/20 14:19:34 kai Exp $ * * Copyright 2000 by Carsten Paeth * * Heavily based on devpts filesystem from H. Peter Anvin * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_DESCRIPTION("CAPI4Linux: /dev/capi/ filesystem"); MODULE_AUTHOR("Carsten Paeth"); MODULE_LICENSE("GPL"); static char *revision = "$Revision: 1.1.4.1 $"; struct capifs_ncci { struct inode *inode; char used; char type; unsigned int num; kdev_t kdev; }; struct capifs_sb_info { u32 magic; struct super_block *next; struct super_block **back; int setuid; int setgid; uid_t uid; gid_t gid; umode_t mode; unsigned int max_ncci; struct capifs_ncci *nccis; }; #define CAPIFS_SUPER_MAGIC (('C'<<8)|'N') #define CAPIFS_SBI_MAGIC (('C'<<24)|('A'<<16)|('P'<<8)|'I') static inline struct capifs_sb_info *SBI(struct super_block *sb) { return (struct capifs_sb_info *)(sb->u.generic_sbp); } /* ------------------------------------------------------------------ */ static int capifs_root_readdir(struct file *,void *,filldir_t); static struct dentry *capifs_root_lookup(struct inode *,struct dentry *); static int capifs_revalidate(struct dentry *, int); static struct inode *capifs_new_inode(struct super_block *sb); static struct file_operations capifs_root_operations = { read: generic_read_dir, readdir: capifs_root_readdir, }; struct inode_operations capifs_root_inode_operations = { lookup: capifs_root_lookup, }; static struct dentry_operations capifs_dentry_operations = { d_revalidate: capifs_revalidate, }; /* * /dev/capi/%d * /dev/capi/r%d */ static int capifs_root_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode * inode = filp->f_dentry->d_inode; struct capifs_sb_info * sbi = SBI(filp->f_dentry->d_inode->i_sb); off_t nr; char numbuf[32]; nr = filp->f_pos; switch(nr) { case 0: if (filldir(dirent, ".", 1, nr, inode->i_ino, DT_DIR) < 0) return 0; filp->f_pos = ++nr; /* fall through */ case 1: if (filldir(dirent, "..", 2, nr, inode->i_ino, DT_DIR) < 0) return 0; filp->f_pos = ++nr; /* fall through */ default: while (nr < sbi->max_ncci) { int n = nr - 2; struct capifs_ncci *np = &sbi->nccis[n]; if (np->inode && np->used) { char *p = numbuf; if (np->type) *p++ = np->type; sprintf(p, "%u", np->num); if ( filldir(dirent, numbuf, strlen(numbuf), nr, nr, DT_UNKNOWN) < 0 ) return 0; } filp->f_pos = ++nr; } break; } return 0; } /* * Revalidate is called on every cache lookup. We use it to check that * the ncci really does still exist. Never revalidate negative dentries; * for simplicity (fix later?) */ static int capifs_revalidate(struct dentry * dentry, int flags) { struct capifs_sb_info *sbi; if ( !dentry->d_inode ) return 0; sbi = SBI(dentry->d_inode->i_sb); return ( sbi->nccis[dentry->d_inode->i_ino - 2].inode == dentry->d_inode ); } static struct dentry *capifs_root_lookup(struct inode * dir, struct dentry * dentry) { struct capifs_sb_info *sbi = SBI(dir->i_sb); struct capifs_ncci *np; unsigned int i; char numbuf[32]; char *p, *tmp; unsigned int num; char type = 0; dentry->d_inode = NULL; /* Assume failure */ dentry->d_op = &capifs_dentry_operations; if (dentry->d_name.len >= sizeof(numbuf) ) return NULL; strncpy(numbuf, dentry->d_name.name, dentry->d_name.len); numbuf[dentry->d_name.len] = 0; p = numbuf; if (!isdigit(*p)) type = *p++; tmp = p; num = (unsigned int)simple_strtoul(p, &tmp, 10); if (tmp == p || *tmp) return NULL; for (i = 0, np = sbi->nccis ; i < sbi->max_ncci; i++, np++) { if (np->used && np->num == num && np->type == type) break; } if ( i >= sbi->max_ncci ) return NULL; dentry->d_inode = np->inode; if ( dentry->d_inode ) atomic_inc(&dentry->d_inode->i_count); d_add(dentry, dentry->d_inode); return NULL; } /* ------------------------------------------------------------------ */ static struct super_block *mounts = NULL; static void capifs_put_super(struct super_block *sb) { struct capifs_sb_info *sbi = SBI(sb); struct inode *inode; int i; for ( i = 0 ; i < sbi->max_ncci ; i++ ) { if ( (inode = sbi->nccis[i].inode) ) { if (atomic_read(&inode->i_count) != 1 ) printk("capifs_put_super: badness: entry %d count %d\n", i, (unsigned)atomic_read(&inode->i_count)); inode->i_nlink--; iput(inode); } } *sbi->back = sbi->next; if ( sbi->next ) SBI(sbi->next)->back = sbi->back; kfree(sbi->nccis); kfree(sbi); } static int capifs_statfs(struct super_block *sb, struct statfs *buf); static struct super_operations capifs_sops = { put_super: capifs_put_super, statfs: capifs_statfs, }; static int capifs_parse_options(char *options, struct capifs_sb_info *sbi) { int setuid = 0; int setgid = 0; uid_t uid = 0; gid_t gid = 0; umode_t mode = 0600; unsigned int maxncci = 512; char *this_char, *value; this_char = NULL; if ( options ) this_char = strtok(options,","); for ( ; this_char; this_char = strtok(NULL,",")) { if ((value = strchr(this_char,'=')) != NULL) *value++ = 0; if (!strcmp(this_char,"uid")) { if (!value || !*value) return 1; uid = simple_strtoul(value,&value,0); if (*value) return 1; setuid = 1; } else if (!strcmp(this_char,"gid")) { if (!value || !*value) return 1; gid = simple_strtoul(value,&value,0); if (*value) return 1; setgid = 1; } else if (!strcmp(this_char,"mode")) { if (!value || !*value) return 1; mode = simple_strtoul(value,&value,8); if (*value) return 1; } else if (!strcmp(this_char,"maxncci")) { if (!value || !*value) return 1; maxncci = simple_strtoul(value,&value,8); if (*value) return 1; } else return 1; } sbi->setuid = setuid; sbi->setgid = setgid; sbi->uid = uid; sbi->gid = gid; sbi->mode = mode & ~S_IFMT; sbi->max_ncci = maxncci; return 0; } struct super_block *capifs_read_super(struct super_block *s, void *data, int silent) { struct inode * root_inode; struct dentry * root; struct capifs_sb_info *sbi; /* Super block already completed? */ if (s->s_root) goto out; sbi = (struct capifs_sb_info *) kmalloc(sizeof(struct capifs_sb_info), GFP_KERNEL); if ( !sbi ) goto fail; memset(sbi, 0, sizeof(struct capifs_sb_info)); sbi->magic = CAPIFS_SBI_MAGIC; if ( capifs_parse_options(data,sbi) ) { kfree(sbi); printk("capifs: called with bogus options\n"); goto fail; } sbi->nccis = kmalloc(sizeof(struct capifs_ncci) * sbi->max_ncci, GFP_KERNEL); if ( !sbi->nccis ) { kfree(sbi); goto fail; } memset(sbi->nccis, 0, sizeof(struct capifs_ncci) * sbi->max_ncci); s->u.generic_sbp = (void *) sbi; s->s_blocksize = 1024; s->s_blocksize_bits = 10; s->s_magic = CAPIFS_SUPER_MAGIC; s->s_op = &capifs_sops; s->s_root = NULL; /* * Get the root inode and dentry, but defer checking for errors. */ root_inode = capifs_new_inode(s); if (root_inode) { root_inode->i_ino = 1; root_inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; root_inode->i_op = &capifs_root_inode_operations; root_inode->i_fop = &capifs_root_operations; root_inode->i_nlink = 2; } root = d_alloc_root(root_inode); /* * Check whether somebody else completed the super block. */ if (s->s_root) { if (root) dput(root); else iput(root_inode); goto out; } if (!root) { printk("capifs: get root dentry failed\n"); /* * iput() can block, so we clear the super block first. */ iput(root_inode); kfree(sbi->nccis); kfree(sbi); goto fail; } /* * Check whether somebody else completed the super block. */ if (s->s_root) goto out; /* * Success! Install the root dentry now to indicate completion. */ s->s_root = root; sbi->next = mounts; if ( sbi->next ) SBI(sbi->next)->back = &(sbi->next); sbi->back = &mounts; mounts = s; out: /* Success ... somebody else completed the super block for us. */ return s; fail: return NULL; } static int capifs_statfs(struct super_block *sb, struct statfs *buf) { buf->f_type = CAPIFS_SUPER_MAGIC; buf->f_bsize = 1024; buf->f_blocks = 0; buf->f_bfree = 0; buf->f_bavail = 0; buf->f_files = 0; buf->f_ffree = 0; buf->f_namelen = NAME_MAX; return 0; } static struct inode *capifs_new_inode(struct super_block *sb) { struct inode *inode = new_inode(sb); if (inode) { inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_blocks = 0; inode->i_blksize = 1024; inode->i_uid = inode->i_gid = 0; } return inode; } static DECLARE_FSTYPE(capifs_fs_type, "capifs", capifs_read_super, 0); void capifs_new_ncci(char type, unsigned int num, kdev_t device) { struct super_block *sb; struct capifs_sb_info *sbi; struct capifs_ncci *np; ino_t ino; for ( sb = mounts ; sb ; sb = sbi->next ) { sbi = SBI(sb); for (ino = 0, np = sbi->nccis ; ino < sbi->max_ncci; ino++, np++) { if (np->used == 0) { np->used = 1; np->type = type; np->num = num; np->kdev = device; break; } } if ( ino >= sbi->max_ncci ) continue; if ((np->inode = capifs_new_inode(sb)) != NULL) { struct inode *inode = np->inode; inode->i_uid = sbi->setuid ? sbi->uid : current->fsuid; inode->i_gid = sbi->setgid ? sbi->gid : current->fsgid; inode->i_nlink = 1; inode->i_ino = ino + 2; init_special_inode(inode, sbi->mode|S_IFCHR, np->kdev); } } } void capifs_free_ncci(char type, unsigned int num) { struct super_block *sb; struct capifs_sb_info *sbi; struct inode *inode; struct capifs_ncci *np; ino_t ino; for ( sb = mounts ; sb ; sb = sbi->next ) { sbi = SBI(sb); for (ino = 0, np = sbi->nccis ; ino < sbi->max_ncci; ino++, np++) { if (!np->used || np->type != type || np->num != num) continue; if (np->inode) { inode = np->inode; np->inode = 0; np->used = 0; inode->i_nlink--; iput(inode); break; } } } } static int __init capifs_init(void) { char rev[32]; char *p; int err; MOD_INC_USE_COUNT; if ((p = strchr(revision, ':')) != 0 && p[1]) { strncpy(rev, p + 2, sizeof(rev)); rev[sizeof(rev)-1] = 0; if ((p = strchr(rev, '$')) != 0 && p > rev) *(p-1) = 0; } else strcpy(rev, "1.0"); err = register_filesystem(&capifs_fs_type); if (err) { MOD_DEC_USE_COUNT; return err; } printk(KERN_NOTICE "capifs: Rev %s\n", rev); MOD_DEC_USE_COUNT; return 0; } static void __exit capifs_exit(void) { unregister_filesystem(&capifs_fs_type); } EXPORT_SYMBOL(capifs_new_ncci); EXPORT_SYMBOL(capifs_free_ncci); module_init(capifs_init); module_exit(capifs_exit);