X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Flibfs.c;h=f503fcd989fc71013974f42f6d86e6307db3486d;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=f90b29595927e206c6fa4cee6e6ae796c509ce28;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/fs/libfs.c b/fs/libfs.c index f90b29595..f503fcd98 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -7,6 +7,8 @@ #include #include #include +#include + #include int simple_getattr(struct vfsmount *mnt, struct dentry *dentry, @@ -18,9 +20,9 @@ int simple_getattr(struct vfsmount *mnt, struct dentry *dentry, return 0; } -int simple_statfs(struct super_block *sb, struct kstatfs *buf) +int simple_statfs(struct dentry *dentry, struct kstatfs *buf) { - buf->f_type = sb->s_magic; + buf->f_type = dentry->d_sb->s_magic; buf->f_bsize = PAGE_CACHE_SIZE; buf->f_namelen = NAME_MAX; return 0; @@ -61,7 +63,7 @@ int dcache_dir_open(struct inode *inode, struct file *file) { static struct qstr cursor_name = {.len = 1, .name = "."}; - file->private_data = d_alloc(file->f_dentry, &cursor_name); + file->private_data = d_alloc(file->f_path.dentry, &cursor_name); return file->private_data ? 0 : -ENOMEM; } @@ -74,7 +76,7 @@ int dcache_dir_close(struct inode *inode, struct file *file) loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) { - down(&file->f_dentry->d_inode->i_sem); + mutex_lock(&file->f_path.dentry->d_inode->i_mutex); switch (origin) { case 1: offset += file->f_pos; @@ -82,7 +84,7 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) if (offset >= 0) break; default: - up(&file->f_dentry->d_inode->i_sem); + mutex_unlock(&file->f_path.dentry->d_inode->i_mutex); return -EINVAL; } if (offset != file->f_pos) { @@ -93,20 +95,20 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) loff_t n = file->f_pos - 2; spin_lock(&dcache_lock); - list_del(&cursor->d_child); - p = file->f_dentry->d_subdirs.next; - while (n && p != &file->f_dentry->d_subdirs) { + list_del(&cursor->d_u.d_child); + p = file->f_path.dentry->d_subdirs.next; + while (n && p != &file->f_path.dentry->d_subdirs) { struct dentry *next; - next = list_entry(p, struct dentry, d_child); + next = list_entry(p, struct dentry, d_u.d_child); if (!d_unhashed(next) && next->d_inode) n--; p = p->next; } - list_add_tail(&cursor->d_child, p); + list_add_tail(&cursor->d_u.d_child, p); spin_unlock(&dcache_lock); } } - up(&file->f_dentry->d_inode->i_sem); + mutex_unlock(&file->f_path.dentry->d_inode->i_mutex); return offset; } @@ -122,11 +124,12 @@ static inline unsigned char dt_type(struct inode *inode) * both impossible due to the lock on directory. */ -int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) +static inline int do_dcache_readdir_filter(struct file * filp, + void * dirent, filldir_t filldir, int (*filter)(struct dentry *dentry)) { - struct dentry *dentry = filp->f_dentry; + struct dentry *dentry = filp->f_path.dentry; struct dentry *cursor = filp->private_data; - struct list_head *p, *q = &cursor->d_child; + struct list_head *p, *q = &cursor->d_u.d_child; ino_t ino; int i = filp->f_pos; @@ -147,23 +150,23 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) /* fallthrough */ default: spin_lock(&dcache_lock); - if (filp->f_pos == 2) { - list_del(q); - list_add(q, &dentry->d_subdirs); - } + if (filp->f_pos == 2) + list_move(q, &dentry->d_subdirs); + for (p=q->next; p != &dentry->d_subdirs; p=p->next) { struct dentry *next; - next = list_entry(p, struct dentry, d_child); + next = list_entry(p, struct dentry, d_u.d_child); if (d_unhashed(next) || !next->d_inode) continue; + if (filter && !filter(next)) + continue; spin_unlock(&dcache_lock); if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, dt_type(next->d_inode)) < 0) return 0; spin_lock(&dcache_lock); /* next is still alive */ - list_del(q); - list_add(q, p); + list_move(q, p); p = q; filp->f_pos++; } @@ -172,17 +175,30 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) return 0; } +int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) +{ + return do_dcache_readdir_filter(filp, dirent, filldir, NULL); +} + +int dcache_readdir_filter(struct file * filp, void * dirent, filldir_t filldir, + int (*filter)(struct dentry *)) +{ + return do_dcache_readdir_filter(filp, dirent, filldir, filter); +} + + ssize_t generic_read_dir(struct file *filp, char __user *buf, size_t siz, loff_t *ppos) { return -EISDIR; } -struct file_operations simple_dir_operations = { +const struct file_operations simple_dir_operations = { .open = dcache_dir_open, .release = dcache_dir_close, .llseek = dcache_dir_lseek, .read = generic_read_dir, .readdir = dcache_readdir, + .fsync = simple_sync_file, }; struct inode_operations simple_dir_inode_operations = { @@ -193,9 +209,9 @@ struct inode_operations simple_dir_inode_operations = { * Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that * will never be mountable) */ -struct super_block * -get_sb_pseudo(struct file_system_type *fs_type, char *name, - struct super_operations *ops, unsigned long magic) +int get_sb_pseudo(struct file_system_type *fs_type, char *name, + struct super_operations *ops, unsigned long magic, + struct vfsmount *mnt) { struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL); static struct super_operations default_ops = {.statfs = simple_statfs}; @@ -204,7 +220,7 @@ get_sb_pseudo(struct file_system_type *fs_type, char *name, struct qstr d_name = {.name = name, .len = strlen(name)}; if (IS_ERR(s)) - return s; + return PTR_ERR(s); s->s_flags = MS_NOUSER; s->s_maxbytes = ~0ULL; @@ -229,12 +245,12 @@ get_sb_pseudo(struct file_system_type *fs_type, char *name, d_instantiate(dentry, root); s->s_root = dentry; s->s_flags |= MS_ACTIVE; - return s; + return simple_set_mnt(mnt, s); Enomem: up_write(&s->s_umount); deactivate_super(s); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } int simple_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) @@ -242,7 +258,7 @@ int simple_link(struct dentry *old_dentry, struct inode *dir, struct dentry *den struct inode *inode = old_dentry->d_inode; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - inode->i_nlink++; + inc_nlink(inode); atomic_inc(&inode->i_count); dget(dentry); d_instantiate(dentry, inode); @@ -260,7 +276,7 @@ int simple_empty(struct dentry *dentry) int ret = 0; spin_lock(&dcache_lock); - list_for_each_entry(child, &dentry->d_subdirs, d_child) + list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) if (simple_positive(child)) goto out; ret = 1; @@ -274,7 +290,7 @@ int simple_unlink(struct inode *dir, struct dentry *dentry) struct inode *inode = dentry->d_inode; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - inode->i_nlink--; + drop_nlink(inode); dput(dentry); return 0; } @@ -284,9 +300,9 @@ int simple_rmdir(struct inode *dir, struct dentry *dentry) if (!simple_empty(dentry)) return -ENOTEMPTY; - dentry->d_inode->i_nlink--; + drop_nlink(dentry->d_inode); simple_unlink(dir, dentry); - dir->i_nlink--; + drop_nlink(dir); return 0; } @@ -302,10 +318,10 @@ int simple_rename(struct inode *old_dir, struct dentry *old_dentry, if (new_dentry->d_inode) { simple_unlink(new_dir, new_dentry); if (they_are_dirs) - old_dir->i_nlink--; + drop_nlink(old_dir); } else if (they_are_dirs) { - old_dir->i_nlink--; - new_dir->i_nlink++; + drop_nlink(old_dir); + inc_nlink(new_dir); } old_dir->i_ctime = old_dir->i_mtime = new_dir->i_ctime = @@ -316,17 +332,9 @@ int simple_rename(struct inode *old_dir, struct dentry *old_dentry, int simple_readpage(struct file *file, struct page *page) { - void *kaddr; - - if (PageUptodate(page)) - goto out; - - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr, 0, PAGE_CACHE_SIZE); - kunmap_atomic(kaddr, KM_USER0); + clear_highpage(page); flush_dcache_page(page); SetPageUptodate(page); -out: unlock_page(page); return 0; } @@ -355,7 +363,7 @@ int simple_commit_write(struct file *file, struct page *page, /* * No need to use i_size_read() here, the i_size - * cannot change under us because we hold the i_sem. + * cannot change under us because we hold the i_mutex. */ if (pos > inode->i_size) i_size_write(inode, pos); @@ -382,11 +390,11 @@ int simple_fill_super(struct super_block *s, int magic, struct tree_descr *files return -ENOMEM; inode->i_mode = S_IFDIR | 0755; inode->i_uid = inode->i_gid = 0; - inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; + inode->i_nlink = 2; root = d_alloc_root(inode); if (!root) { iput(inode); @@ -403,7 +411,6 @@ int simple_fill_super(struct super_block *s, int magic, struct tree_descr *files goto out; inode->i_mode = S_IFREG | files->mode; inode->i_uid = inode->i_gid = 0; - inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_fop = files->ops; @@ -420,13 +427,13 @@ out: static DEFINE_SPINLOCK(pin_fs_lock); -int simple_pin_fs(char *name, struct vfsmount **mount, int *count) +int simple_pin_fs(struct file_system_type *type, struct vfsmount **mount, int *count) { struct vfsmount *mnt = NULL; spin_lock(&pin_fs_lock); if (unlikely(!*mount)) { spin_unlock(&pin_fs_lock); - mnt = do_kern_mount(name, 0, name, NULL); + mnt = vfs_kern_mount(type, 0, type->name, NULL); if (IS_ERR(mnt)) return PTR_ERR(mnt); spin_lock(&pin_fs_lock); @@ -519,10 +526,107 @@ int simple_transaction_release(struct inode *inode, struct file *file) return 0; } +/* Simple attribute files */ + +struct simple_attr { + u64 (*get)(void *); + void (*set)(void *, u64); + char get_buf[24]; /* enough to store a u64 and "\n\0" */ + char set_buf[24]; + void *data; + const char *fmt; /* format for read operation */ + struct mutex mutex; /* protects access to these buffers */ +}; + +/* simple_attr_open is called by an actual attribute open file operation + * to set the attribute specific access operations. */ +int simple_attr_open(struct inode *inode, struct file *file, + u64 (*get)(void *), void (*set)(void *, u64), + const char *fmt) +{ + struct simple_attr *attr; + + attr = kmalloc(sizeof(*attr), GFP_KERNEL); + if (!attr) + return -ENOMEM; + + attr->get = get; + attr->set = set; + attr->data = inode->i_private; + attr->fmt = fmt; + mutex_init(&attr->mutex); + + file->private_data = attr; + + return nonseekable_open(inode, file); +} + +int simple_attr_close(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +/* read from the buffer that is filled with the get function */ +ssize_t simple_attr_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + struct simple_attr *attr; + size_t size; + ssize_t ret; + + attr = file->private_data; + + if (!attr->get) + return -EACCES; + + mutex_lock(&attr->mutex); + if (*ppos) /* continued read */ + size = strlen(attr->get_buf); + else /* first read */ + size = scnprintf(attr->get_buf, sizeof(attr->get_buf), + attr->fmt, + (unsigned long long)attr->get(attr->data)); + + ret = simple_read_from_buffer(buf, len, ppos, attr->get_buf, size); + mutex_unlock(&attr->mutex); + return ret; +} + +/* interpret the buffer as a number to call the set function with */ +ssize_t simple_attr_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct simple_attr *attr; + u64 val; + size_t size; + ssize_t ret; + + attr = file->private_data; + + if (!attr->set) + return -EACCES; + + mutex_lock(&attr->mutex); + ret = -EFAULT; + size = min(sizeof(attr->set_buf) - 1, len); + if (copy_from_user(attr->set_buf, buf, size)) + goto out; + + ret = len; /* claim we got the whole input */ + attr->set_buf[size] = '\0'; + val = simple_strtol(attr->set_buf, NULL, 0); + attr->set(attr->data, val); +out: + mutex_unlock(&attr->mutex); + return ret; +} + EXPORT_SYMBOL(dcache_dir_close); EXPORT_SYMBOL(dcache_dir_lseek); EXPORT_SYMBOL(dcache_dir_open); EXPORT_SYMBOL(dcache_readdir); +EXPORT_SYMBOL(dcache_readdir_filter); EXPORT_SYMBOL(generic_read_dir); EXPORT_SYMBOL(get_sb_pseudo); EXPORT_SYMBOL(simple_commit_write); @@ -547,3 +651,7 @@ EXPORT_SYMBOL(simple_read_from_buffer); EXPORT_SYMBOL(simple_transaction_get); EXPORT_SYMBOL(simple_transaction_read); EXPORT_SYMBOL(simple_transaction_release); +EXPORT_SYMBOL_GPL(simple_attr_open); +EXPORT_SYMBOL_GPL(simple_attr_close); +EXPORT_SYMBOL_GPL(simple_attr_read); +EXPORT_SYMBOL_GPL(simple_attr_write);