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/mount.h>
  15#include <linux/module.h>
  16#include <linux/kobject.h>
  17#include <linux/namei.h>
  18#include <linux/mutex.h>
  19
  20#include "sysfs.h"
  21
  22static int object_depth(struct sysfs_dirent *sd)
  23{
  24        int depth = 0;
  25
  26        for (; sd->s_parent; sd = sd->s_parent)
  27                depth++;
  28
  29        return depth;
  30}
  31
  32static int object_path_length(struct sysfs_dirent * sd)
  33{
  34        int length = 1;
  35
  36        for (; sd->s_parent; sd = sd->s_parent)
  37                length += strlen(sd->s_name) + 1;
  38
  39        return length;
  40}
  41
  42static void fill_object_path(struct sysfs_dirent *sd, char *buffer, int length)
  43{
  44        --length;
  45        for (; sd->s_parent; sd = sd->s_parent) {
  46                int cur = strlen(sd->s_name);
  47
  48                /* back up enough to print this bus id with '/' */
  49                length -= cur;
  50                strncpy(buffer + length, sd->s_name, cur);
  51                *(buffer + --length) = '/';
  52        }
  53}
  54
  55/**
  56 *      sysfs_create_link - create symlink between two objects.
  57 *      @kobj:  object whose directory we're creating the link in.
  58 *      @target:        object we're pointing to.
  59 *      @name:          name of the symlink.
  60 */
  61int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)
  62{
  63        struct sysfs_dirent *parent_sd = NULL;
  64        struct sysfs_dirent *target_sd = NULL;
  65        struct sysfs_dirent *sd = NULL;
  66        struct sysfs_addrm_cxt acxt;
  67        int error;
  68
  69        BUG_ON(!name);
  70
  71        if (!kobj)
  72                parent_sd = &sysfs_root;
  73        else
  74                parent_sd = kobj->sd;
  75
  76        error = -EFAULT;
  77        if (!parent_sd)
  78                goto out_put;
  79
  80        /* target->sd can go away beneath us but is protected with
  81         * sysfs_assoc_lock.  Fetch target_sd from it.
  82         */
  83        spin_lock(&sysfs_assoc_lock);
  84        if (target->sd)
  85                target_sd = sysfs_get(target->sd);
  86        spin_unlock(&sysfs_assoc_lock);
  87
  88        error = -ENOENT;
  89        if (!target_sd)
  90                goto out_put;
  91
  92        error = -ENOMEM;
  93        sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK);
  94        if (!sd)
  95                goto out_put;
  96
  97        sd->s_symlink.target_sd = target_sd;
  98        target_sd = NULL;       /* reference is now owned by the symlink */
  99
 100        sysfs_addrm_start(&acxt, parent_sd);
 101        error = sysfs_add_one(&acxt, sd);
 102        sysfs_addrm_finish(&acxt);
 103
 104        if (error)
 105                goto out_put;
 106
 107        return 0;
 108
 109 out_put:
 110        sysfs_put(target_sd);
 111        sysfs_put(sd);
 112        return error;
 113}
 114
 115
 116/**
 117 *      sysfs_remove_link - remove symlink in object's directory.
 118 *      @kobj:  object we're acting for.
 119 *      @name:  name of the symlink to remove.
 120 */
 121
 122void sysfs_remove_link(struct kobject * kobj, const char * name)
 123{
 124        sysfs_hash_and_remove(kobj->sd, name);
 125}
 126
 127static int sysfs_get_target_path(struct sysfs_dirent * parent_sd,
 128                                 struct sysfs_dirent * target_sd, char *path)
 129{
 130        char * s;
 131        int depth, size;
 132
 133        depth = object_depth(parent_sd);
 134        size = object_path_length(target_sd) + depth * 3 - 1;
 135        if (size > PATH_MAX)
 136                return -ENAMETOOLONG;
 137
 138        pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size);
 139
 140        for (s = path; depth--; s += 3)
 141                strcpy(s,"../");
 142
 143        fill_object_path(target_sd, path, size);
 144        pr_debug("%s: path = '%s'\n", __FUNCTION__, path);
 145
 146        return 0;
 147}
 148
 149static int sysfs_getlink(struct dentry *dentry, char * path)
 150{
 151        struct sysfs_dirent *sd = dentry->d_fsdata;
 152        struct sysfs_dirent *parent_sd = sd->s_parent;
 153        struct sysfs_dirent *target_sd = sd->s_symlink.target_sd;
 154        int error;
 155
 156        mutex_lock(&sysfs_mutex);
 157        error = sysfs_get_target_path(parent_sd, target_sd, path);
 158        mutex_unlock(&sysfs_mutex);
 159
 160        return error;
 161}
 162
 163static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
 164{
 165        int error = -ENOMEM;
 166        unsigned long page = get_zeroed_page(GFP_KERNEL);
 167        if (page)
 168                error = sysfs_getlink(dentry, (char *) page); 
 169        nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
 170        return NULL;
 171}
 172
 173static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
 174{
 175        char *page = nd_get_link(nd);
 176        if (!IS_ERR(page))
 177                free_page((unsigned long)page);
 178}
 179
 180const struct inode_operations sysfs_symlink_inode_operations = {
 181        .readlink = generic_readlink,
 182        .follow_link = sysfs_follow_link,
 183        .put_link = sysfs_put_link,
 184};
 185
 186
 187EXPORT_SYMBOL_GPL(sysfs_create_link);
 188EXPORT_SYMBOL_GPL(sysfs_remove_link);
 189