linux-old/arch/ia64/sn/io/hwgfs/interface.c
<<
>>
Prefs
   1/* $Id$
   2 *
   3 * This file is subject to the terms and conditions of the GNU General Public
   4 * License.  See the file "COPYING" in the main directory of this archive
   5 * for more details.
   6 *
   7 * Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
   8 */
   9#include <linux/module.h>
  10#include <linux/kernel.h>
  11#include <linux/fs.h>
  12#include <linux/string.h>
  13#include <linux/sched.h>                /* needed for smp_lock.h :( */
  14#include <linux/smp_lock.h>
  15
  16#include <linux/slab.h>
  17
  18#include <asm/sn/hwgfs.h>
  19
  20extern struct vfsmount *hwgfs_vfsmount;
  21
  22/* TODO: Move this to some .h file or, more likely, use a slightly
  23   different interface from lookup_create. */
  24extern struct dentry *lookup_create(struct nameidata *nd, int is_dir);
  25
  26static int
  27walk_parents_mkdir(
  28        const char              **path,
  29        struct nameidata        *nd,
  30        int                     is_dir)
  31{
  32        char                    *slash;
  33        char                    buf[strlen(*path)+1];
  34        int                     error;
  35
  36        while ((slash = strchr(*path, '/')) != NULL) {
  37                int len = slash - *path;
  38                memcpy(buf, *path, len);
  39                buf[len] = '\0';
  40
  41                error = path_walk(buf, nd); 
  42                if (unlikely(error))
  43                        return error;
  44
  45                nd->dentry = lookup_create(nd, is_dir);
  46                if (unlikely(IS_ERR(nd->dentry)))
  47                        return PTR_ERR(nd->dentry);
  48
  49                if (!nd->dentry->d_inode)
  50                        error = vfs_mkdir(nd->dentry->d_parent->d_inode,
  51                                        nd->dentry, 0755);
  52                
  53                up(&nd->dentry->d_parent->d_inode->i_sem);
  54                if (unlikely(error))
  55                        return error;
  56
  57                *path += len + 1;
  58        }
  59
  60        return 0;
  61}
  62
  63/* On success, returns with parent_inode->i_sem taken. */
  64static int
  65hwgfs_decode(
  66        hwgfs_handle_t          dir,
  67        const char              *name,
  68        int                     is_dir,
  69        struct inode            **parent_inode,
  70        struct dentry           **dentry)
  71{
  72        struct nameidata        nd;
  73        int                     error;
  74
  75        if (!dir)
  76                dir = hwgfs_vfsmount->mnt_sb->s_root;
  77
  78        memset(&nd, 0, sizeof(nd));
  79        nd.flags = LOOKUP_PARENT;
  80        nd.mnt = mntget(hwgfs_vfsmount);
  81        nd.dentry = dget(dir);
  82
  83        error = walk_parents_mkdir(&name, &nd, is_dir);
  84        if (unlikely(error))
  85                return error;
  86
  87        error = path_walk(name, &nd);
  88        if (unlikely(error))
  89                return error;
  90
  91        *dentry = lookup_create(&nd, is_dir);
  92
  93        if (unlikely(IS_ERR(*dentry)))
  94                return PTR_ERR(*dentry);
  95        *parent_inode = (*dentry)->d_parent->d_inode;
  96        return 0;
  97}
  98
  99static int
 100path_len(
 101        struct dentry           *de,
 102        struct dentry           *root)
 103{
 104        int                     len = 0;
 105
 106        while (de != root) {
 107                len += de->d_name.len + 1;      /* count the '/' */
 108                de = de->d_parent;
 109        }
 110        return len;             /* -1 because we omit the leading '/',
 111                                   +1 because we include trailing '\0' */
 112}
 113
 114int
 115hwgfs_generate_path(
 116        hwgfs_handle_t          de,
 117        char                    *path,
 118        int                     buflen)
 119{
 120        struct dentry           *hwgfs_root;
 121        int                     len;
 122        char                    *path_orig = path;
 123
 124        if (unlikely(de == NULL))
 125                return -EINVAL;
 126
 127        hwgfs_root = hwgfs_vfsmount->mnt_sb->s_root;
 128        if (unlikely(de == hwgfs_root))
 129                return -EINVAL;
 130
 131        spin_lock(&dcache_lock);
 132        len = path_len(de, hwgfs_root);
 133        if (len > buflen) {
 134                spin_unlock(&dcache_lock);
 135                return -ENAMETOOLONG;
 136        }
 137
 138        path += len - 1;
 139        *path = '\0';
 140
 141        for (;;) {
 142                path -= de->d_name.len;
 143                memcpy(path, de->d_name.name, de->d_name.len);
 144                de = de->d_parent;
 145                if (de == hwgfs_root)
 146                        break;
 147                *(--path) = '/';
 148        }
 149                
 150        spin_unlock(&dcache_lock);
 151        BUG_ON(path != path_orig);
 152        return 0;
 153}
 154
 155hwgfs_handle_t
 156hwgfs_register(
 157        hwgfs_handle_t          dir,
 158        const char              *name,
 159        unsigned int            flags,
 160        unsigned int            major,
 161        unsigned int            minor,
 162        umode_t                 mode,
 163        void                    *ops,
 164        void                    *info)
 165{
 166        dev_t                   devnum = MKDEV(major, minor);
 167        struct inode            *parent_inode;
 168        struct dentry           *dentry;
 169        int                     error;
 170
 171        error = hwgfs_decode(dir, name, 0, &parent_inode, &dentry);
 172        if (likely(!error)) {
 173                error = vfs_mknod(parent_inode, dentry, mode, devnum);
 174                if (likely(!error)) {
 175                        /*
 176                         * Do this inside parents i_sem to avoid racing
 177                         * with lookups.
 178                         */
 179                        if (S_ISCHR(mode))
 180                                dentry->d_inode->i_fop = ops;
 181                        dentry->d_fsdata = info;
 182                        up(&parent_inode->i_sem);
 183                } else {
 184                        up(&parent_inode->i_sem);
 185                        dput(dentry);
 186                        dentry = NULL;
 187                }
 188        }
 189
 190        return dentry;
 191}
 192
 193int
 194hwgfs_mk_symlink(
 195        hwgfs_handle_t          dir,
 196        const char              *name,
 197        unsigned int            flags,
 198        const char              *link,
 199        hwgfs_handle_t          *handle,
 200        void                    *info)
 201{
 202        struct inode            *parent_inode;
 203        struct dentry           *dentry;
 204        int                     error;
 205
 206        error = hwgfs_decode(dir, name, 0, &parent_inode, &dentry);
 207        if (likely(!error)) {
 208                error = vfs_symlink(parent_inode, dentry, link);
 209                dentry->d_fsdata = info;
 210                if (handle)
 211                        *handle = dentry;
 212                up(&parent_inode->i_sem);
 213                /* dput(dentry); */
 214        }
 215        return error;
 216}
 217
 218hwgfs_handle_t
 219hwgfs_mk_dir(
 220        hwgfs_handle_t          dir,
 221        const char              *name,
 222        void                    *info)
 223{
 224        struct inode            *parent_inode;
 225        struct dentry           *dentry;
 226        int                     error;
 227
 228        error = hwgfs_decode(dir, name, 1, &parent_inode, &dentry);
 229        if (likely(!error)) {
 230                error = vfs_mkdir(parent_inode, dentry, 0755);
 231                up(&parent_inode->i_sem);
 232
 233                if (unlikely(error)) {
 234                        dput(dentry);
 235                        dentry = NULL;
 236                } else {
 237                        dentry->d_fsdata = info;
 238                }
 239        }
 240        return dentry;
 241}
 242
 243void
 244hwgfs_unregister(
 245        hwgfs_handle_t          de)
 246{
 247        struct inode            *parent_inode = de->d_parent->d_inode;
 248
 249        if (S_ISDIR(de->d_inode->i_mode))
 250                vfs_rmdir(parent_inode, de);
 251        else
 252                vfs_unlink(parent_inode, de);
 253}
 254
 255/* XXX: this function is utterly bogus.  Every use of it is racy and the
 256        prototype is stupid.  You have been warned.  --hch.  */
 257hwgfs_handle_t
 258hwgfs_find_handle(
 259        hwgfs_handle_t          base,
 260        const char              *name,
 261        unsigned int            major,          /* IGNORED */
 262        unsigned int            minor,          /* IGNORED */
 263        char                    type,           /* IGNORED */
 264        int                     traverse_symlinks)
 265{
 266        struct dentry           *dentry = NULL;
 267        struct nameidata        nd;
 268        int                     error;
 269
 270        BUG_ON(*name=='/');
 271
 272        memset(&nd, 0, sizeof(nd));
 273
 274        nd.mnt = mntget(hwgfs_vfsmount);
 275        nd.dentry = dget(base ? base : hwgfs_vfsmount->mnt_sb->s_root);
 276        nd.flags = LOOKUP_POSITIVE | (traverse_symlinks ? LOOKUP_FOLLOW : 0);
 277
 278        error = path_walk(name, &nd);
 279        if (likely(!error)) {
 280                dentry = nd.dentry;
 281                path_release(&nd);              /* stale data from here! */
 282        }
 283
 284        return dentry;
 285}
 286
 287hwgfs_handle_t
 288hwgfs_get_parent(
 289        hwgfs_handle_t          de)
 290{
 291        struct dentry           *parent;
 292
 293        lock_kernel();          /* XXX: for LBS this must be dparent_lock */
 294        parent = de->d_parent;
 295        unlock_kernel();
 296
 297        return parent;
 298}
 299
 300int
 301hwgfs_set_info(
 302        hwgfs_handle_t          de,
 303        void                    *info)
 304{
 305        if (unlikely(de == NULL))
 306                return -EINVAL;
 307        de->d_fsdata = info;
 308        return 0;
 309}
 310
 311void *
 312hwgfs_get_info(
 313        hwgfs_handle_t          de)
 314{
 315        return de->d_fsdata;
 316}
 317
 318EXPORT_SYMBOL(hwgfs_generate_path);
 319EXPORT_SYMBOL(hwgfs_register);
 320EXPORT_SYMBOL(hwgfs_unregister);
 321EXPORT_SYMBOL(hwgfs_mk_symlink);
 322EXPORT_SYMBOL(hwgfs_mk_dir);
 323EXPORT_SYMBOL(hwgfs_find_handle);
 324EXPORT_SYMBOL(hwgfs_get_parent);
 325EXPORT_SYMBOL(hwgfs_set_info);
 326EXPORT_SYMBOL(hwgfs_get_info);
 327
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.