0e05316d30ed8592365912b78b3f3078af5bda76
[multipath-tools/.git] / libmultipath / sysfs.c
1 /*
2  * Copyright (C) 2005-2006 Kay Sievers <kay.sievers@vrfy.org>
3  *
4  *      This program is free software; you can redistribute it and/or modify it
5  *      under the terms of the GNU General Public License as published by the
6  *      Free Software Foundation version 2 of the License.
7  *
8  *      This program is distributed in the hope that it will be useful, but
9  *      WITHOUT ANY WARRANTY; without even the implied warranty of
10  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  *      General Public License for more details.
12  *
13  *      You should have received a copy of the GNU General Public License along
14  *      with this program; if not, write to the Free Software Foundation, Inc.,
15  *      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16  *
17  */
18
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <stddef.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <sys/stat.h>
28 #include <string.h>
29 #include <dirent.h>
30 #include <libudev.h>
31
32 #include "checkers.h"
33 #include "vector.h"
34 #include "structs.h"
35 #include "sysfs.h"
36 #include "list.h"
37 #include "util.h"
38 #include "debug.h"
39 #include "devmapper.h"
40
41 /*
42  * When we modify an attribute value we cannot rely on libudev for now,
43  * as libudev lacks the capability to update an attribute value.
44  * So for modified attributes we need to implement our own function.
45  */
46 ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name,
47                              char * value, size_t value_len)
48 {
49         char devpath[PATH_SIZE];
50         struct stat statbuf;
51         int fd;
52         ssize_t size = -1;
53
54         if (!dev || !attr_name || !value)
55                 return 0;
56
57         snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev),
58                  attr_name);
59         condlog(4, "open '%s'", devpath);
60         if (stat(devpath, &statbuf) != 0) {
61                 condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
62                 return -ENXIO;
63         }
64
65         /* skip directories */
66         if (S_ISDIR(statbuf.st_mode)) {
67                 condlog(4, "%s is a directory", devpath);
68                 return -EISDIR;
69         }
70
71         /* skip non-writeable files */
72         if ((statbuf.st_mode & S_IRUSR) == 0) {
73                 condlog(4, "%s is not readable", devpath);
74                 return -EPERM;
75         }
76
77         /* read attribute value */
78         fd = open(devpath, O_RDONLY);
79         if (fd < 0) {
80                 condlog(4, "attribute '%s' can not be opened: %s",
81                         devpath, strerror(errno));
82                 return -errno;
83         }
84         size = read(fd, value, value_len);
85         if (size < 0) {
86                 condlog(4, "read from %s failed: %s", devpath, strerror(errno));
87                 size = -errno;
88         } else if (size == value_len) {
89                 condlog(4, "overflow while reading from %s", devpath);
90                 size = 0;
91         } else {
92                 value[size] = '\0';
93         }
94
95         close(fd);
96         return size;
97 }
98
99 ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name,
100                              char * value, size_t value_len)
101 {
102         char devpath[PATH_SIZE];
103         struct stat statbuf;
104         int fd;
105         ssize_t size = -1;
106
107         if (!dev || !attr_name || !value || !value_len)
108                 return 0;
109
110         snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev),
111                  attr_name);
112         condlog(4, "open '%s'", devpath);
113         if (stat(devpath, &statbuf) != 0) {
114                 condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
115                 return -errno;
116         }
117
118         /* skip directories */
119         if (S_ISDIR(statbuf.st_mode)) {
120                 condlog(4, "%s is a directory", devpath);
121                 return -EISDIR;
122         }
123
124         /* skip non-writeable files */
125         if ((statbuf.st_mode & S_IWUSR) == 0) {
126                 condlog(4, "%s is not writeable", devpath);
127                 return -EPERM;
128         }
129
130         /* write attribute value */
131         fd = open(devpath, O_WRONLY);
132         if (fd < 0) {
133                 condlog(4, "attribute '%s' can not be opened: %s",
134                         devpath, strerror(errno));
135                 return -errno;
136         }
137         size = write(fd, value, value_len);
138         if (size < 0) {
139                 condlog(4, "write to %s failed: %s", devpath, strerror(errno));
140                 size = -errno;
141         } else if (size < value_len) {
142                 condlog(4, "tried to write %ld to %s. Wrote %ld",
143                         (long)value_len, devpath, (long)size);
144                 size = 0;
145         }
146
147         close(fd);
148         return size;
149 }
150
151 int
152 sysfs_get_size (struct path *pp, unsigned long long * size)
153 {
154         char attr[255];
155         int r;
156
157         if (!pp->udev || !size)
158                 return 1;
159
160         attr[0] = '\0';
161         if (sysfs_attr_get_value(pp->udev, "size", attr, 255) <= 0) {
162                 condlog(3, "%s: No size attribute in sysfs", pp->dev);
163                 return 1;
164         }
165
166         r = sscanf(attr, "%llu\n", size);
167
168         if (r != 1) {
169                 condlog(3, "%s: Cannot parse size attribute", pp->dev);
170                 *size = 0;
171                 return 1;
172         }
173
174         return 0;
175 }
176
177 int sysfs_check_holders(char * check_devt, char * new_devt)
178 {
179         unsigned int major, new_minor, table_minor;
180         char path[PATH_SIZE], check_dev[PATH_SIZE];
181         char * table_name;
182         DIR *dirfd;
183         struct dirent *holder;
184
185         if (sscanf(new_devt,"%d:%d", &major, &new_minor) != 2) {
186                 condlog(1, "invalid device number %s", new_devt);
187                 return 0;
188         }
189
190         if (devt2devname(check_dev, PATH_SIZE, check_devt)) {
191                 condlog(1, "can't get devname for %s", check_devt);
192                 return 0;
193         }
194
195         condlog(3, "%s: checking holder", check_dev);
196
197         snprintf(path, PATH_SIZE, "/sys/block/%s/holders", check_dev);
198         dirfd = opendir(path);
199         if (dirfd == NULL) {
200                 condlog(3, "%s: failed to open directory %s (%d)",
201                         check_dev, path, errno);
202                 return 0;
203         }
204         while ((holder = readdir(dirfd)) != NULL) {
205                 if ((strcmp(holder->d_name,".") == 0) ||
206                     (strcmp(holder->d_name,"..") == 0))
207                         continue;
208
209                 if (sscanf(holder->d_name, "dm-%d", &table_minor) != 1) {
210                         condlog(3, "%s: %s is not a dm-device",
211                                 check_dev, holder->d_name);
212                         continue;
213                 }
214                 if (table_minor == new_minor) {
215                         condlog(3, "%s: holder already correct", check_dev);
216                         continue;
217                 }
218                 table_name = dm_mapname(major, table_minor);
219
220                 condlog(0, "%s: reassign table %s old %s new %s", check_dev,
221                         table_name, check_devt, new_devt);
222
223                 dm_reassign_table(table_name, check_devt, new_devt);
224                 FREE(table_name);
225         }
226         closedir(dirfd);
227
228         return 0;
229 }