/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (c) 2003 Silicon Graphics, Inc. All Rights Reserved. * * Portions based on Adam Richter's smalldevfs and thus * Copyright 2002-2003 Yggdrasil Computing, Inc. */ #include #include #include #include #include #include #include #include extern struct vfsmount *hwgfs_vfsmount; static int walk_parents_mkdir( const char **path, struct nameidata *nd, int is_dir) { char *slash; char buf[strlen(*path)+1]; int error; while ((slash = strchr(*path, '/')) != NULL) { int len = slash - *path; memcpy(buf, *path, len); buf[len] = '\0'; error = path_walk(buf, nd); if (unlikely(error)) return error; nd->dentry = lookup_create(nd, is_dir); nd->flags |= LOOKUP_PARENT; if (unlikely(IS_ERR(nd->dentry))) return PTR_ERR(nd->dentry); if (!nd->dentry->d_inode) error = vfs_mkdir(nd->dentry->d_parent->d_inode, nd->dentry, 0755); up(&nd->dentry->d_parent->d_inode->i_sem); if (unlikely(error)) return error; *path += len + 1; } return 0; } /* On success, returns with parent_inode->i_sem taken. */ static int hwgfs_decode( hwgfs_handle_t dir, const char *name, int is_dir, struct inode **parent_inode, struct dentry **dentry) { struct nameidata nd; int error; if (!dir) dir = hwgfs_vfsmount->mnt_sb->s_root; memset(&nd, 0, sizeof(nd)); nd.flags = LOOKUP_PARENT; nd.mnt = mntget(hwgfs_vfsmount); nd.dentry = dget(dir); error = walk_parents_mkdir(&name, &nd, is_dir); if (unlikely(error)) return error; error = path_walk(name, &nd); if (unlikely(error)) return error; *dentry = lookup_create(&nd, is_dir); if (unlikely(IS_ERR(*dentry))) return PTR_ERR(*dentry); *parent_inode = (*dentry)->d_parent->d_inode; return 0; } static int path_len( struct dentry *de, struct dentry *root) { int len = 0; while (de != root) { len += de->d_name.len + 1; /* count the '/' */ de = de->d_parent; } return len; /* -1 because we omit the leading '/', +1 because we include trailing '\0' */ } int hwgfs_generate_path( hwgfs_handle_t de, char *path, int buflen) { struct dentry *hwgfs_root; int len; char *path_orig = path; if (unlikely(de == NULL)) return -EINVAL; hwgfs_root = hwgfs_vfsmount->mnt_sb->s_root; if (unlikely(de == hwgfs_root)) return -EINVAL; spin_lock(&dcache_lock); len = path_len(de, hwgfs_root); if (len > buflen) { spin_unlock(&dcache_lock); return -ENAMETOOLONG; } path += len - 1; *path = '\0'; for (;;) { path -= de->d_name.len; memcpy(path, de->d_name.name, de->d_name.len); de = de->d_parent; if (de == hwgfs_root) break; *(--path) = '/'; } spin_unlock(&dcache_lock); BUG_ON(path != path_orig); return 0; } hwgfs_handle_t hwgfs_register( hwgfs_handle_t dir, const char *name, unsigned int flags, unsigned int major, unsigned int minor, umode_t mode, void *ops, void *info) { dev_t devnum = MKDEV(major, minor); struct inode *parent_inode; struct dentry *dentry; int error; error = hwgfs_decode(dir, name, 0, &parent_inode, &dentry); if (likely(!error)) { error = vfs_mknod(parent_inode, dentry, mode, devnum); if (likely(!error)) { /* * Do this inside parents i_sem to avoid racing * with lookups. */ if (S_ISCHR(mode)) dentry->d_inode->i_fop = ops; dentry->d_fsdata = info; up(&parent_inode->i_sem); } else { up(&parent_inode->i_sem); dput(dentry); dentry = NULL; } } return dentry; } int hwgfs_mk_symlink( hwgfs_handle_t dir, const char *name, unsigned int flags, const char *link, hwgfs_handle_t *handle, void *info) { struct inode *parent_inode; struct dentry *dentry; int error; error = hwgfs_decode(dir, name, 0, &parent_inode, &dentry); if (likely(!error)) { error = vfs_symlink(parent_inode, dentry, link); dentry->d_fsdata = info; if (handle) *handle = dentry; up(&parent_inode->i_sem); /* dput(dentry); */ } return error; } hwgfs_handle_t hwgfs_mk_dir( hwgfs_handle_t dir, const char *name, void *info) { struct inode *parent_inode; struct dentry *dentry; int error; error = hwgfs_decode(dir, name, 1, &parent_inode, &dentry); if (likely(!error)) { error = vfs_mkdir(parent_inode, dentry, 0755); up(&parent_inode->i_sem); if (unlikely(error)) { dput(dentry); dentry = NULL; } else { dentry->d_fsdata = info; } } return dentry; } void hwgfs_unregister( hwgfs_handle_t de) { struct inode *parent_inode = de->d_parent->d_inode; if (S_ISDIR(de->d_inode->i_mode)) vfs_rmdir(parent_inode, de); else vfs_unlink(parent_inode, de); } /* XXX: this function is utterly bogus. Every use of it is racy and the prototype is stupid. You have been warned. --hch. */ hwgfs_handle_t hwgfs_find_handle( hwgfs_handle_t base, const char *name, unsigned int major, /* IGNORED */ unsigned int minor, /* IGNORED */ char type, /* IGNORED */ int traverse_symlinks) { struct dentry *dentry = NULL; struct nameidata nd; int error; BUG_ON(*name=='/'); memset(&nd, 0, sizeof(nd)); nd.mnt = mntget(hwgfs_vfsmount); nd.dentry = dget(base ? base : hwgfs_vfsmount->mnt_sb->s_root); nd.flags = (traverse_symlinks ? LOOKUP_FOLLOW : 0); error = path_walk(name, &nd); if (likely(!error)) { dentry = nd.dentry; path_release(&nd); /* stale data from here! */ } return dentry; } hwgfs_handle_t hwgfs_get_parent( hwgfs_handle_t de) { struct dentry *parent; spin_lock(&de->d_lock); parent = de->d_parent; spin_unlock(&de->d_lock); return parent; } int hwgfs_set_info( hwgfs_handle_t de, void *info) { if (unlikely(de == NULL)) return -EINVAL; de->d_fsdata = info; return 0; } void * hwgfs_get_info( hwgfs_handle_t de) { return de->d_fsdata; } EXPORT_SYMBOL(hwgfs_generate_path); EXPORT_SYMBOL(hwgfs_register); EXPORT_SYMBOL(hwgfs_unregister); EXPORT_SYMBOL(hwgfs_mk_symlink); EXPORT_SYMBOL(hwgfs_mk_dir); EXPORT_SYMBOL(hwgfs_find_handle); EXPORT_SYMBOL(hwgfs_get_parent); EXPORT_SYMBOL(hwgfs_set_info); EXPORT_SYMBOL(hwgfs_get_info);