symlink.c 3.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
/*
 * symlink.c - operations for sysfs symlinks.
 */

#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/namei.h>

#include "sysfs.h"

static int object_depth(struct kobject * kobj)
{
	struct kobject * p = kobj;
	int depth = 0;
	do { depth++; } while ((p = p->parent));
	return depth;
}

static int object_path_length(struct kobject * kobj)
{
	struct kobject * p = kobj;
	int length = 1;
	do {
		length += strlen(kobject_name(p)) + 1;
		p = p->parent;
	} while (p);
	return length;
}

static void fill_object_path(struct kobject * kobj, char * buffer, int length)
{
	struct kobject * p;

	--length;
	for (p = kobj; p; p = p->parent) {
		int cur = strlen(kobject_name(p));

		/* back up enough to print this bus id with '/' */
		length -= cur;
		strncpy(buffer + length,kobject_name(p),cur);
		*(buffer + --length) = '/';
	}
}

46
static int sysfs_add_link(struct dentry * parent, const char * name, struct kobject * target)
L
Linus Torvalds 已提交
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
{
	struct sysfs_dirent * parent_sd = parent->d_fsdata;
	struct sysfs_symlink * sl;
	int error = 0;

	error = -ENOMEM;
	sl = kmalloc(sizeof(*sl), GFP_KERNEL);
	if (!sl)
		goto exit1;

	sl->link_name = kmalloc(strlen(name) + 1, GFP_KERNEL);
	if (!sl->link_name)
		goto exit2;

	strcpy(sl->link_name, name);
	sl->target_kobj = kobject_get(target);

	error = sysfs_make_dirent(parent_sd, NULL, sl, S_IFLNK|S_IRWXUGO,
				SYSFS_KOBJ_LINK);
	if (!error)
		return 0;

69
	kobject_put(target);
L
Linus Torvalds 已提交
70 71 72 73 74 75 76 77 78 79 80 81 82
	kfree(sl->link_name);
exit2:
	kfree(sl);
exit1:
	return error;
}

/**
 *	sysfs_create_link - create symlink between two objects.
 *	@kobj:	object whose directory we're creating the link in.
 *	@target:	object we're pointing to.
 *	@name:		name of the symlink.
 */
83
int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)
L
Linus Torvalds 已提交
84 85
{
	struct dentry * dentry = kobj->dentry;
86
	int error = -EEXIST;
L
Linus Torvalds 已提交
87 88 89

	BUG_ON(!kobj || !kobj->dentry || !name);

90
	mutex_lock(&dentry->d_inode->i_mutex);
91 92
	if (!sysfs_dirent_exist(dentry->d_fsdata, name))
		error = sysfs_add_link(dentry, name, target);
93
	mutex_unlock(&dentry->d_inode->i_mutex);
L
Linus Torvalds 已提交
94 95 96 97 98 99 100 101 102 103
	return error;
}


/**
 *	sysfs_remove_link - remove symlink in object's directory.
 *	@kobj:	object we're acting for.
 *	@name:	name of the symlink to remove.
 */

104
void sysfs_remove_link(struct kobject * kobj, const char * name)
L
Linus Torvalds 已提交
105 106 107 108 109
{
	sysfs_hash_and_remove(kobj->dentry,name);
}

static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target,
110
				 char *path)
L
Linus Torvalds 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
{
	char * s;
	int depth, size;

	depth = object_depth(kobj);
	size = object_path_length(target) + depth * 3 - 1;
	if (size > PATH_MAX)
		return -ENAMETOOLONG;

	pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size);

	for (s = path; depth--; s += 3)
		strcpy(s,"../");

	fill_object_path(target, path, size);
	pr_debug("%s: path = '%s'\n", __FUNCTION__, path);

	return 0;
}

static int sysfs_getlink(struct dentry *dentry, char * path)
{
	struct kobject *kobj, *target_kobj;
	int error = 0;

	kobj = sysfs_get_kobject(dentry->d_parent);
	if (!kobj)
		return -EINVAL;

	target_kobj = sysfs_get_kobject(dentry);
	if (!target_kobj) {
		kobject_put(kobj);
		return -EINVAL;
	}

	down_read(&sysfs_rename_sem);
	error = sysfs_get_target_path(kobj, target_kobj, path);
	up_read(&sysfs_rename_sem);
	
	kobject_put(kobj);
	kobject_put(target_kobj);
	return error;

}

156
static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
L
Linus Torvalds 已提交
157 158 159 160 161 162
{
	int error = -ENOMEM;
	unsigned long page = get_zeroed_page(GFP_KERNEL);
	if (page)
		error = sysfs_getlink(dentry, (char *) page); 
	nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
163
	return NULL;
L
Linus Torvalds 已提交
164 165
}

166
static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
L
Linus Torvalds 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
{
	char *page = nd_get_link(nd);
	if (!IS_ERR(page))
		free_page((unsigned long)page);
}

struct inode_operations sysfs_symlink_inode_operations = {
	.readlink = generic_readlink,
	.follow_link = sysfs_follow_link,
	.put_link = sysfs_put_link,
};


EXPORT_SYMBOL_GPL(sysfs_create_link);
EXPORT_SYMBOL_GPL(sysfs_remove_link);