linux/fs/sysfs/symlink.c
<<
>>
Prefs
   1/*
   2 * fs/sysfs/symlink.c - sysfs symlink implementation
   3 *
   4 * Copyright (c) 2001-3 Patrick Mochel
   5 * Copyright (c) 2007 SUSE Linux Products GmbH
   6 * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
   7 *
   8 * This file is released under the GPLv2.
   9 *
  10 * Please see Documentation/filesystems/sysfs.txt for more information.
  11 */
  12
  13#include <linux/fs.h>
  14#include <linux/gfp.h>
  15#include <linux/mount.h>
  16#include <linux/module.h>
  17#include <linux/kobject.h>
  18#include <linux/namei.h>
  19#include <linux/mutex.h>
  20#include <linux/security.h>
  21
  22#include "sysfs.h"
  23
  24static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target,
  25                                const char *name, int warn)
  26{
  27        struct sysfs_dirent *parent_sd = NULL;
  28        struct sysfs_dirent *target_sd = NULL;
  29        struct sysfs_dirent *sd = NULL;
  30        struct sysfs_addrm_cxt acxt;
  31        enum kobj_ns_type ns_type;
  32        int error;
  33
  34        BUG_ON(!name);
  35
  36        if (!kobj)
  37                parent_sd = &sysfs_root;
  38        else
  39                parent_sd = kobj->sd;
  40
  41        error = -EFAULT;
  42        if (!parent_sd)
  43                goto out_put;
  44
  45        /* target->sd can go away beneath us but is protected with
  46         * sysfs_assoc_lock.  Fetch target_sd from it.
  47         */
  48        spin_lock(&sysfs_assoc_lock);
  49        if (target->sd)
  50                target_sd = sysfs_get(target->sd);
  51        spin_unlock(&sysfs_assoc_lock);
  52
  53        error = -ENOENT;
  54        if (!target_sd)
  55                goto out_put;
  56
  57        error = -ENOMEM;
  58        sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK);
  59        if (!sd)
  60                goto out_put;
  61
  62        ns_type = sysfs_ns_type(parent_sd);
  63        if (ns_type)
  64                sd->s_ns = target->ktype->namespace(target);
  65        sd->s_symlink.target_sd = target_sd;
  66        target_sd = NULL;       /* reference is now owned by the symlink */
  67
  68        sysfs_addrm_start(&acxt, parent_sd);
  69        /* Symlinks must be between directories with the same ns_type */
  70        if (!ns_type ||
  71            (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent))) {
  72                if (warn)
  73                        error = sysfs_add_one(&acxt, sd);
  74                else
  75                        error = __sysfs_add_one(&acxt, sd);
  76        } else {
  77                error = -EINVAL;
  78                WARN(1, KERN_WARNING
  79                        "sysfs: symlink across ns_types %s/%s -> %s/%s\n",
  80                        parent_sd->s_name,
  81                        sd->s_name,
  82                        sd->s_symlink.target_sd->s_parent->s_name,
  83                        sd->s_symlink.target_sd->s_name);
  84        }
  85        sysfs_addrm_finish(&acxt);
  86
  87        if (error)
  88                goto out_put;
  89
  90        return 0;
  91
  92 out_put:
  93        sysfs_put(target_sd);
  94        sysfs_put(sd);
  95        return error;
  96}
  97
  98/**
  99 *      sysfs_create_link - create symlink between two objects.
 100 *      @kobj:  object whose directory we're creating the link in.
 101 *      @target:        object we're pointing to.
 102 *      @name:          name of the symlink.
 103 */
 104int sysfs_create_link(struct kobject *kobj, struct kobject *target,
 105                      const char *name)
 106{
 107        return sysfs_do_create_link(kobj, target, name, 1);
 108}
 109
 110/**
 111 *      sysfs_create_link_nowarn - create symlink between two objects.
 112 *      @kobj:  object whose directory we're creating the link in.
 113 *      @target:        object we're pointing to.
 114 *      @name:          name of the symlink.
 115 *
 116 *      This function does the same as sysf_create_link(), but it
 117 *      doesn't warn if the link already exists.
 118 */
 119int sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target,
 120                             const char *name)
 121{
 122        return sysfs_do_create_link(kobj, target, name, 0);
 123}
 124
 125/**
 126 *      sysfs_delete_link - remove symlink in object's directory.
 127 *      @kobj:  object we're acting for.
 128 *      @targ:  object we're pointing to.
 129 *      @name:  name of the symlink to remove.
 130 *
 131 *      Unlike sysfs_remove_link sysfs_delete_link has enough information
 132 *      to successfully delete symlinks in tagged directories.
 133 */
 134void sysfs_delete_link(struct kobject *kobj, struct kobject *targ,
 135                        const char *name)
 136{
 137        const void *ns = NULL;
 138        spin_lock(&sysfs_assoc_lock);
 139        if (targ->sd && sysfs_ns_type(kobj->sd))
 140                ns = targ->sd->s_ns;
 141        spin_unlock(&sysfs_assoc_lock);
 142        sysfs_hash_and_remove(kobj->sd, ns, name);
 143}
 144
 145/**
 146 *      sysfs_remove_link - remove symlink in object's directory.
 147 *      @kobj:  object we're acting for.
 148 *      @name:  name of the symlink to remove.
 149 */
 150
 151void sysfs_remove_link(struct kobject * kobj, const char * name)
 152{
 153        struct sysfs_dirent *parent_sd = NULL;
 154
 155        if (!kobj)
 156                parent_sd = &sysfs_root;
 157        else
 158                parent_sd = kobj->sd;
 159
 160        sysfs_hash_and_remove(parent_sd, NULL, name);
 161}
 162
 163/**
 164 *      sysfs_rename_link - rename symlink in object's directory.
 165 *      @kobj:  object we're acting for.
 166 *      @targ:  object we're pointing to.
 167 *      @old:   previous name of the symlink.
 168 *      @new:   new name of the symlink.
 169 *
 170 *      A helper function for the common rename symlink idiom.
 171 */
 172int sysfs_rename_link(struct kobject *kobj, struct kobject *targ,
 173                        const char *old, const char *new)
 174{
 175        struct sysfs_dirent *parent_sd, *sd = NULL;
 176        const void *old_ns = NULL, *new_ns = NULL;
 177        int result;
 178
 179        if (!kobj)
 180                parent_sd = &sysfs_root;
 181        else
 182                parent_sd = kobj->sd;
 183
 184        if (targ->sd)
 185                old_ns = targ->sd->s_ns;
 186
 187        result = -ENOENT;
 188        sd = sysfs_get_dirent(parent_sd, old_ns, old);
 189        if (!sd)
 190                goto out;
 191
 192        result = -EINVAL;
 193        if (sysfs_type(sd) != SYSFS_KOBJ_LINK)
 194                goto out;
 195        if (sd->s_symlink.target_sd->s_dir.kobj != targ)
 196                goto out;
 197
 198        if (sysfs_ns_type(parent_sd))
 199                new_ns = targ->ktype->namespace(targ);
 200
 201        result = sysfs_rename(sd, parent_sd, new_ns, new);
 202
 203out:
 204        sysfs_put(sd);
 205        return result;
 206}
 207
 208static int sysfs_get_target_path(struct sysfs_dirent *parent_sd,
 209                                 struct sysfs_dirent *target_sd, char *path)
 210{
 211        struct sysfs_dirent *base, *sd;
 212        char *s = path;
 213        int len = 0;
 214
 215        /* go up to the root, stop at the base */
 216        base = parent_sd;
 217        while (base->s_parent) {
 218                sd = target_sd->s_parent;
 219                while (sd->s_parent && base != sd)
 220                        sd = sd->s_parent;
 221
 222                if (base == sd)
 223                        break;
 224
 225                strcpy(s, "../");
 226                s += 3;
 227                base = base->s_parent;
 228        }
 229
 230        /* determine end of target string for reverse fillup */
 231        sd = target_sd;
 232        while (sd->s_parent && sd != base) {
 233                len += strlen(sd->s_name) + 1;
 234                sd = sd->s_parent;
 235        }
 236
 237        /* check limits */
 238        if (len < 2)
 239                return -EINVAL;
 240        len--;
 241        if ((s - path) + len > PATH_MAX)
 242                return -ENAMETOOLONG;
 243
 244        /* reverse fillup of target string from target to base */
 245        sd = target_sd;
 246        while (sd->s_parent && sd != base) {
 247                int slen = strlen(sd->s_name);
 248
 249                len -= slen;
 250                strncpy(s + len, sd->s_name, slen);
 251                if (len)
 252                        s[--len] = '/';
 253
 254                sd = sd->s_parent;
 255        }
 256
 257        return 0;
 258}
 259
 260static int sysfs_getlink(struct dentry *dentry, char * path)
 261{
 262        struct sysfs_dirent *sd = dentry->d_fsdata;
 263        struct sysfs_dirent *parent_sd = sd->s_parent;
 264        struct sysfs_dirent *target_sd = sd->s_symlink.target_sd;
 265        int error;
 266
 267        mutex_lock(&sysfs_mutex);
 268        error = sysfs_get_target_path(parent_sd, target_sd, path);
 269        mutex_unlock(&sysfs_mutex);
 270
 271        return error;
 272}
 273
 274static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
 275{
 276        int error = -ENOMEM;
 277        unsigned long page = get_zeroed_page(GFP_KERNEL);
 278        if (page) {
 279                error = sysfs_getlink(dentry, (char *) page); 
 280                if (error < 0)
 281                        free_page((unsigned long)page);
 282        }
 283        nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
 284        return NULL;
 285}
 286
 287static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
 288{
 289        char *page = nd_get_link(nd);
 290        if (!IS_ERR(page))
 291                free_page((unsigned long)page);
 292}
 293
 294const struct inode_operations sysfs_symlink_inode_operations = {
 295        .setxattr       = sysfs_setxattr,
 296        .readlink       = generic_readlink,
 297        .follow_link    = sysfs_follow_link,
 298        .put_link       = sysfs_put_link,
 299        .setattr        = sysfs_setattr,
 300        .getattr        = sysfs_getattr,
 301        .permission     = sysfs_permission,
 302};
 303
 304
 305EXPORT_SYMBOL_GPL(sysfs_create_link);
 306EXPORT_SYMBOL_GPL(sysfs_remove_link);
 307EXPORT_SYMBOL_GPL(sysfs_rename_link);
 308