diff options
| -rw-r--r-- | fs/sysfs/dir.c | 2 | ||||
| -rw-r--r-- | fs/sysfs/symlink.c | 88 | ||||
| -rw-r--r-- | fs/sysfs/sysfs.h | 9 |
3 files changed, 53 insertions, 46 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index e9fddcc5944..2a94dc36d16 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
| @@ -54,7 +54,7 @@ void release_sysfs_dirent(struct sysfs_dirent * sd) | |||
| 54 | parent_sd = sd->s_parent; | 54 | parent_sd = sd->s_parent; |
| 55 | 55 | ||
| 56 | if (sd->s_type & SYSFS_KOBJ_LINK) | 56 | if (sd->s_type & SYSFS_KOBJ_LINK) |
| 57 | kobject_put(sd->s_elem.symlink.target_kobj); | 57 | sysfs_put(sd->s_elem.symlink.target_sd); |
| 58 | if (sd->s_type & SYSFS_COPY_NAME) | 58 | if (sd->s_type & SYSFS_COPY_NAME) |
| 59 | kfree(sd->s_name); | 59 | kfree(sd->s_name); |
| 60 | kfree(sd->s_iattr); | 60 | kfree(sd->s_iattr); |
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 27df635b786..ff605d3f4d3 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c | |||
| @@ -11,50 +11,49 @@ | |||
| 11 | 11 | ||
| 12 | #include "sysfs.h" | 12 | #include "sysfs.h" |
| 13 | 13 | ||
| 14 | static int object_depth(struct kobject * kobj) | 14 | static int object_depth(struct sysfs_dirent *sd) |
| 15 | { | 15 | { |
| 16 | struct kobject * p = kobj; | ||
| 17 | int depth = 0; | 16 | int depth = 0; |
| 18 | do { depth++; } while ((p = p->parent)); | 17 | |
| 18 | for (; sd->s_parent; sd = sd->s_parent) | ||
| 19 | depth++; | ||
| 20 | |||
| 19 | return depth; | 21 | return depth; |
| 20 | } | 22 | } |
| 21 | 23 | ||
| 22 | static int object_path_length(struct kobject * kobj) | 24 | static int object_path_length(struct sysfs_dirent * sd) |
| 23 | { | 25 | { |
| 24 | struct kobject * p = kobj; | ||
| 25 | int length = 1; | 26 | int length = 1; |
| 26 | do { | 27 | |
| 27 | length += strlen(kobject_name(p)) + 1; | 28 | for (; sd->s_parent; sd = sd->s_parent) |
| 28 | p = p->parent; | 29 | length += strlen(sd->s_name) + 1; |
| 29 | } while (p); | 30 | |
| 30 | return length; | 31 | return length; |
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | static void fill_object_path(struct kobject * kobj, char * buffer, int length) | 34 | static void fill_object_path(struct sysfs_dirent *sd, char *buffer, int length) |
| 34 | { | 35 | { |
| 35 | struct kobject * p; | ||
| 36 | |||
| 37 | --length; | 36 | --length; |
| 38 | for (p = kobj; p; p = p->parent) { | 37 | for (; sd->s_parent; sd = sd->s_parent) { |
| 39 | int cur = strlen(kobject_name(p)); | 38 | int cur = strlen(sd->s_name); |
| 40 | 39 | ||
| 41 | /* back up enough to print this bus id with '/' */ | 40 | /* back up enough to print this bus id with '/' */ |
| 42 | length -= cur; | 41 | length -= cur; |
| 43 | strncpy(buffer + length,kobject_name(p),cur); | 42 | strncpy(buffer + length, sd->s_name, cur); |
| 44 | *(buffer + --length) = '/'; | 43 | *(buffer + --length) = '/'; |
| 45 | } | 44 | } |
| 46 | } | 45 | } |
| 47 | 46 | ||
| 48 | static int sysfs_add_link(struct dentry * parent, const char * name, struct kobject * target) | 47 | static int sysfs_add_link(struct sysfs_dirent * parent_sd, const char * name, |
| 48 | struct sysfs_dirent * target_sd) | ||
| 49 | { | 49 | { |
| 50 | struct sysfs_dirent * parent_sd = parent->d_fsdata; | ||
| 51 | struct sysfs_dirent * sd; | 50 | struct sysfs_dirent * sd; |
| 52 | 51 | ||
| 53 | sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); | 52 | sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); |
| 54 | if (!sd) | 53 | if (!sd) |
| 55 | return -ENOMEM; | 54 | return -ENOMEM; |
| 56 | 55 | ||
| 57 | sd->s_elem.symlink.target_kobj = kobject_get(target); | 56 | sd->s_elem.symlink.target_sd = target_sd; |
| 58 | sysfs_attach_dirent(sd, parent_sd, NULL); | 57 | sysfs_attach_dirent(sd, parent_sd, NULL); |
| 59 | return 0; | 58 | return 0; |
| 60 | } | 59 | } |
| @@ -68,6 +67,8 @@ static int sysfs_add_link(struct dentry * parent, const char * name, struct kobj | |||
| 68 | int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) | 67 | int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) |
| 69 | { | 68 | { |
| 70 | struct dentry *dentry = NULL; | 69 | struct dentry *dentry = NULL; |
| 70 | struct sysfs_dirent *parent_sd = NULL; | ||
| 71 | struct sysfs_dirent *target_sd = NULL; | ||
| 71 | int error = -EEXIST; | 72 | int error = -EEXIST; |
| 72 | 73 | ||
| 73 | BUG_ON(!name); | 74 | BUG_ON(!name); |
| @@ -80,11 +81,27 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char | |||
| 80 | 81 | ||
| 81 | if (!dentry) | 82 | if (!dentry) |
| 82 | return -EFAULT; | 83 | return -EFAULT; |
| 84 | parent_sd = dentry->d_fsdata; | ||
| 85 | |||
| 86 | /* target->dentry can go away beneath us but is protected with | ||
| 87 | * kobj_sysfs_assoc_lock. Fetch target_sd from it. | ||
| 88 | */ | ||
| 89 | spin_lock(&kobj_sysfs_assoc_lock); | ||
| 90 | if (target->dentry) | ||
| 91 | target_sd = sysfs_get(target->dentry->d_fsdata); | ||
| 92 | spin_unlock(&kobj_sysfs_assoc_lock); | ||
| 93 | |||
| 94 | if (!target_sd) | ||
| 95 | return -ENOENT; | ||
| 83 | 96 | ||
| 84 | mutex_lock(&dentry->d_inode->i_mutex); | 97 | mutex_lock(&dentry->d_inode->i_mutex); |
| 85 | if (!sysfs_dirent_exist(dentry->d_fsdata, name)) | 98 | if (!sysfs_dirent_exist(dentry->d_fsdata, name)) |
| 86 | error = sysfs_add_link(dentry, name, target); | 99 | error = sysfs_add_link(parent_sd, name, target_sd); |
| 87 | mutex_unlock(&dentry->d_inode->i_mutex); | 100 | mutex_unlock(&dentry->d_inode->i_mutex); |
| 101 | |||
| 102 | if (error) | ||
| 103 | sysfs_put(target_sd); | ||
| 104 | |||
| 88 | return error; | 105 | return error; |
| 89 | } | 106 | } |
| 90 | 107 | ||
| @@ -100,14 +117,14 @@ void sysfs_remove_link(struct kobject * kobj, const char * name) | |||
| 100 | sysfs_hash_and_remove(kobj->dentry,name); | 117 | sysfs_hash_and_remove(kobj->dentry,name); |
| 101 | } | 118 | } |
| 102 | 119 | ||
| 103 | static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, | 120 | static int sysfs_get_target_path(struct sysfs_dirent * parent_sd, |
| 104 | char *path) | 121 | struct sysfs_dirent * target_sd, char *path) |
| 105 | { | 122 | { |
| 106 | char * s; | 123 | char * s; |
| 107 | int depth, size; | 124 | int depth, size; |
| 108 | 125 | ||
| 109 | depth = object_depth(kobj); | 126 | depth = object_depth(parent_sd); |
| 110 | size = object_path_length(target) + depth * 3 - 1; | 127 | size = object_path_length(target_sd) + depth * 3 - 1; |
| 111 | if (size > PATH_MAX) | 128 | if (size > PATH_MAX) |
| 112 | return -ENAMETOOLONG; | 129 | return -ENAMETOOLONG; |
| 113 | 130 | ||
| @@ -116,7 +133,7 @@ static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, | |||
| 116 | for (s = path; depth--; s += 3) | 133 | for (s = path; depth--; s += 3) |
| 117 | strcpy(s,"../"); | 134 | strcpy(s,"../"); |
| 118 | 135 | ||
| 119 | fill_object_path(target, path, size); | 136 | fill_object_path(target_sd, path, size); |
| 120 | pr_debug("%s: path = '%s'\n", __FUNCTION__, path); | 137 | pr_debug("%s: path = '%s'\n", __FUNCTION__, path); |
| 121 | 138 | ||
| 122 | return 0; | 139 | return 0; |
| @@ -124,27 +141,16 @@ static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, | |||
| 124 | 141 | ||
| 125 | static int sysfs_getlink(struct dentry *dentry, char * path) | 142 | static int sysfs_getlink(struct dentry *dentry, char * path) |
| 126 | { | 143 | { |
| 127 | struct kobject *kobj, *target_kobj; | 144 | struct sysfs_dirent *sd = dentry->d_fsdata; |
| 128 | int error = 0; | 145 | struct sysfs_dirent *parent_sd = sd->s_parent; |
| 129 | 146 | struct sysfs_dirent *target_sd = sd->s_elem.symlink.target_sd; | |
| 130 | kobj = sysfs_get_kobject(dentry->d_parent); | 147 | int error; |
| 131 | if (!kobj) | ||
| 132 | return -EINVAL; | ||
| 133 | |||
| 134 | target_kobj = sysfs_get_kobject(dentry); | ||
| 135 | if (!target_kobj) { | ||
| 136 | kobject_put(kobj); | ||
| 137 | return -EINVAL; | ||
| 138 | } | ||
| 139 | 148 | ||
| 140 | down_read(&sysfs_rename_sem); | 149 | down_read(&sysfs_rename_sem); |
| 141 | error = sysfs_get_target_path(kobj, target_kobj, path); | 150 | error = sysfs_get_target_path(parent_sd, target_sd, path); |
| 142 | up_read(&sysfs_rename_sem); | 151 | up_read(&sysfs_rename_sem); |
| 143 | |||
| 144 | kobject_put(kobj); | ||
| 145 | kobject_put(target_kobj); | ||
| 146 | return error; | ||
| 147 | 152 | ||
| 153 | return error; | ||
| 148 | } | 154 | } |
| 149 | 155 | ||
| 150 | static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) | 156 | static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) |
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 718e2e123fa..60717660ac5 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h | |||
| @@ -3,7 +3,7 @@ struct sysfs_elem_dir { | |||
| 3 | }; | 3 | }; |
| 4 | 4 | ||
| 5 | struct sysfs_elem_symlink { | 5 | struct sysfs_elem_symlink { |
| 6 | struct kobject * target_kobj; | 6 | struct sysfs_dirent * target_sd; |
| 7 | }; | 7 | }; |
| 8 | 8 | ||
| 9 | struct sysfs_elem_attr { | 9 | struct sysfs_elem_attr { |
| @@ -100,10 +100,11 @@ static inline struct kobject *sysfs_get_kobject(struct dentry *dentry) | |||
| 100 | spin_lock(&dcache_lock); | 100 | spin_lock(&dcache_lock); |
| 101 | if (!d_unhashed(dentry)) { | 101 | if (!d_unhashed(dentry)) { |
| 102 | struct sysfs_dirent * sd = dentry->d_fsdata; | 102 | struct sysfs_dirent * sd = dentry->d_fsdata; |
| 103 | |||
| 103 | if (sd->s_type & SYSFS_KOBJ_LINK) | 104 | if (sd->s_type & SYSFS_KOBJ_LINK) |
| 104 | kobj = kobject_get(sd->s_elem.symlink.target_kobj); | 105 | sd = sd->s_elem.symlink.target_sd; |
| 105 | else | 106 | |
| 106 | kobj = kobject_get(sd->s_elem.dir.kobj); | 107 | kobj = kobject_get(sd->s_elem.dir.kobj); |
| 107 | } | 108 | } |
| 108 | spin_unlock(&dcache_lock); | 109 | spin_unlock(&dcache_lock); |
| 109 | 110 | ||
