diff options
Diffstat (limited to 'fs/sysfs')
| -rw-r--r-- | fs/sysfs/dir.c | 175 |
1 files changed, 44 insertions, 131 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index b72b42ed80d1..953e8432b0ae 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
| @@ -33,10 +33,20 @@ static DEFINE_IDA(sysfs_ino_ida); | |||
| 33 | static void sysfs_link_sibling(struct sysfs_dirent *sd) | 33 | static void sysfs_link_sibling(struct sysfs_dirent *sd) |
| 34 | { | 34 | { |
| 35 | struct sysfs_dirent *parent_sd = sd->s_parent; | 35 | struct sysfs_dirent *parent_sd = sd->s_parent; |
| 36 | struct sysfs_dirent **pos; | ||
| 36 | 37 | ||
| 37 | BUG_ON(sd->s_sibling); | 38 | BUG_ON(sd->s_sibling); |
| 38 | sd->s_sibling = parent_sd->s_children; | 39 | |
| 39 | parent_sd->s_children = sd; | 40 | /* Store directory entries in order by ino. This allows |
| 41 | * readdir to properly restart without having to add a | ||
| 42 | * cursor into the s_children list. | ||
| 43 | */ | ||
| 44 | for (pos = &parent_sd->s_children; *pos; pos = &(*pos)->s_sibling) { | ||
| 45 | if (sd->s_ino < (*pos)->s_ino) | ||
| 46 | break; | ||
| 47 | } | ||
| 48 | sd->s_sibling = *pos; | ||
| 49 | *pos = sd; | ||
| 40 | } | 50 | } |
| 41 | 51 | ||
| 42 | /** | 52 | /** |
| @@ -659,7 +669,7 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, | |||
| 659 | struct sysfs_dirent *sd; | 669 | struct sysfs_dirent *sd; |
| 660 | 670 | ||
| 661 | for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) | 671 | for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) |
| 662 | if (sysfs_type(sd) && !strcmp(sd->s_name, name)) | 672 | if (!strcmp(sd->s_name, name)) |
| 663 | return sd; | 673 | return sd; |
| 664 | return NULL; | 674 | return NULL; |
| 665 | } | 675 | } |
| @@ -811,7 +821,7 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) | |||
| 811 | while (*pos) { | 821 | while (*pos) { |
| 812 | struct sysfs_dirent *sd = *pos; | 822 | struct sysfs_dirent *sd = *pos; |
| 813 | 823 | ||
| 814 | if (sysfs_type(sd) && sysfs_type(sd) != SYSFS_DIR) | 824 | if (sysfs_type(sd) != SYSFS_DIR) |
| 815 | sysfs_remove_one(&acxt, sd); | 825 | sysfs_remove_one(&acxt, sd); |
| 816 | else | 826 | else |
| 817 | pos = &(*pos)->s_sibling; | 827 | pos = &(*pos)->s_sibling; |
| @@ -976,37 +986,6 @@ again: | |||
| 976 | return error; | 986 | return error; |
| 977 | } | 987 | } |
| 978 | 988 | ||
| 979 | static int sysfs_dir_open(struct inode *inode, struct file *file) | ||
| 980 | { | ||
| 981 | struct dentry * dentry = file->f_path.dentry; | ||
| 982 | struct sysfs_dirent * parent_sd = dentry->d_fsdata; | ||
| 983 | struct sysfs_dirent * sd; | ||
| 984 | |||
| 985 | sd = sysfs_new_dirent("_DIR_", 0, 0); | ||
| 986 | if (sd) { | ||
| 987 | mutex_lock(&sysfs_mutex); | ||
| 988 | sd->s_parent = sysfs_get(parent_sd); | ||
| 989 | sysfs_link_sibling(sd); | ||
| 990 | mutex_unlock(&sysfs_mutex); | ||
| 991 | } | ||
| 992 | |||
| 993 | file->private_data = sd; | ||
| 994 | return sd ? 0 : -ENOMEM; | ||
| 995 | } | ||
| 996 | |||
| 997 | static int sysfs_dir_close(struct inode *inode, struct file *file) | ||
| 998 | { | ||
| 999 | struct sysfs_dirent * cursor = file->private_data; | ||
| 1000 | |||
| 1001 | mutex_lock(&sysfs_mutex); | ||
| 1002 | sysfs_unlink_sibling(cursor); | ||
| 1003 | mutex_unlock(&sysfs_mutex); | ||
| 1004 | |||
| 1005 | release_sysfs_dirent(cursor); | ||
| 1006 | |||
| 1007 | return 0; | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | /* Relationship between s_mode and the DT_xxx types */ | 989 | /* Relationship between s_mode and the DT_xxx types */ |
| 1011 | static inline unsigned char dt_type(struct sysfs_dirent *sd) | 990 | static inline unsigned char dt_type(struct sysfs_dirent *sd) |
| 1012 | { | 991 | { |
| @@ -1017,117 +996,51 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) | |||
| 1017 | { | 996 | { |
| 1018 | struct dentry *dentry = filp->f_path.dentry; | 997 | struct dentry *dentry = filp->f_path.dentry; |
| 1019 | struct sysfs_dirent * parent_sd = dentry->d_fsdata; | 998 | struct sysfs_dirent * parent_sd = dentry->d_fsdata; |
| 1020 | struct sysfs_dirent *cursor = filp->private_data; | 999 | struct sysfs_dirent *pos; |
| 1021 | struct sysfs_dirent **pos; | ||
| 1022 | ino_t ino; | 1000 | ino_t ino; |
| 1023 | int i = filp->f_pos; | ||
| 1024 | 1001 | ||
| 1025 | switch (i) { | 1002 | if (filp->f_pos == 0) { |
| 1026 | case 0: | 1003 | ino = parent_sd->s_ino; |
| 1027 | ino = parent_sd->s_ino; | 1004 | if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) == 0) |
| 1028 | if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) | ||
| 1029 | break; | ||
| 1030 | filp->f_pos++; | 1005 | filp->f_pos++; |
| 1031 | i++; | 1006 | } |
| 1032 | /* fallthrough */ | 1007 | if (filp->f_pos == 1) { |
| 1033 | case 1: | 1008 | if (parent_sd->s_parent) |
| 1034 | if (parent_sd->s_parent) | 1009 | ino = parent_sd->s_parent->s_ino; |
| 1035 | ino = parent_sd->s_parent->s_ino; | 1010 | else |
| 1036 | else | 1011 | ino = parent_sd->s_ino; |
| 1037 | ino = parent_sd->s_ino; | 1012 | if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) == 0) |
| 1038 | if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0) | ||
| 1039 | break; | ||
| 1040 | filp->f_pos++; | 1013 | filp->f_pos++; |
| 1041 | i++; | 1014 | } |
| 1042 | /* fallthrough */ | 1015 | if ((filp->f_pos > 1) && (filp->f_pos < INT_MAX)) { |
| 1043 | default: | 1016 | mutex_lock(&sysfs_mutex); |
| 1044 | mutex_lock(&sysfs_mutex); | ||
| 1045 | |||
| 1046 | pos = &parent_sd->s_children; | ||
| 1047 | while (*pos != cursor) | ||
| 1048 | pos = &(*pos)->s_sibling; | ||
| 1049 | |||
| 1050 | /* unlink cursor */ | ||
| 1051 | *pos = cursor->s_sibling; | ||
| 1052 | |||
| 1053 | if (filp->f_pos == 2) | ||
| 1054 | pos = &parent_sd->s_children; | ||
| 1055 | |||
| 1056 | for ( ; *pos; pos = &(*pos)->s_sibling) { | ||
| 1057 | struct sysfs_dirent *next = *pos; | ||
| 1058 | const char * name; | ||
| 1059 | int len; | ||
| 1060 | |||
| 1061 | if (!sysfs_type(next)) | ||
| 1062 | continue; | ||
| 1063 | |||
| 1064 | name = next->s_name; | ||
| 1065 | len = strlen(name); | ||
| 1066 | ino = next->s_ino; | ||
| 1067 | |||
| 1068 | if (filldir(dirent, name, len, filp->f_pos, ino, | ||
| 1069 | dt_type(next)) < 0) | ||
| 1070 | break; | ||
| 1071 | |||
| 1072 | filp->f_pos++; | ||
| 1073 | } | ||
| 1074 | 1017 | ||
| 1075 | /* put cursor back in */ | 1018 | /* Skip the dentries we have already reported */ |
| 1076 | cursor->s_sibling = *pos; | 1019 | pos = parent_sd->s_children; |
| 1077 | *pos = cursor; | 1020 | while (pos && (filp->f_pos > pos->s_ino)) |
| 1021 | pos = pos->s_sibling; | ||
| 1078 | 1022 | ||
| 1079 | mutex_unlock(&sysfs_mutex); | 1023 | for ( ; pos; pos = pos->s_sibling) { |
| 1080 | } | 1024 | const char * name; |
| 1081 | return 0; | 1025 | int len; |
| 1082 | } | ||
| 1083 | 1026 | ||
| 1084 | static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) | 1027 | name = pos->s_name; |
| 1085 | { | 1028 | len = strlen(name); |
| 1086 | struct dentry * dentry = file->f_path.dentry; | 1029 | filp->f_pos = ino = pos->s_ino; |
| 1087 | 1030 | ||
| 1088 | switch (origin) { | 1031 | if (filldir(dirent, name, len, filp->f_pos, ino, |
| 1089 | case 1: | 1032 | dt_type(pos)) < 0) |
| 1090 | offset += file->f_pos; | ||
| 1091 | case 0: | ||
| 1092 | if (offset >= 0) | ||
| 1093 | break; | 1033 | break; |
| 1094 | default: | ||
| 1095 | return -EINVAL; | ||
| 1096 | } | ||
| 1097 | if (offset != file->f_pos) { | ||
| 1098 | mutex_lock(&sysfs_mutex); | ||
| 1099 | |||
| 1100 | file->f_pos = offset; | ||
| 1101 | if (file->f_pos >= 2) { | ||
| 1102 | struct sysfs_dirent *sd = dentry->d_fsdata; | ||
| 1103 | struct sysfs_dirent *cursor = file->private_data; | ||
| 1104 | struct sysfs_dirent **pos; | ||
| 1105 | loff_t n = file->f_pos - 2; | ||
| 1106 | |||
| 1107 | sysfs_unlink_sibling(cursor); | ||
| 1108 | |||
| 1109 | pos = &sd->s_children; | ||
| 1110 | while (n && *pos) { | ||
| 1111 | struct sysfs_dirent *next = *pos; | ||
| 1112 | if (sysfs_type(next)) | ||
| 1113 | n--; | ||
| 1114 | pos = &(*pos)->s_sibling; | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | cursor->s_sibling = *pos; | ||
| 1118 | *pos = cursor; | ||
| 1119 | } | 1034 | } |
| 1120 | 1035 | if (!pos) | |
| 1036 | filp->f_pos = INT_MAX; | ||
| 1121 | mutex_unlock(&sysfs_mutex); | 1037 | mutex_unlock(&sysfs_mutex); |
| 1122 | } | 1038 | } |
| 1123 | 1039 | return 0; | |
| 1124 | return offset; | ||
| 1125 | } | 1040 | } |
| 1126 | 1041 | ||
| 1042 | |||
| 1127 | const struct file_operations sysfs_dir_operations = { | 1043 | const struct file_operations sysfs_dir_operations = { |
| 1128 | .open = sysfs_dir_open, | ||
| 1129 | .release = sysfs_dir_close, | ||
| 1130 | .llseek = sysfs_dir_lseek, | ||
| 1131 | .read = generic_read_dir, | 1044 | .read = generic_read_dir, |
| 1132 | .readdir = sysfs_readdir, | 1045 | .readdir = sysfs_readdir, |
| 1133 | }; | 1046 | }; |
