multipathd: fix reservation_key check
[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, see <http://www.gnu.org/licenses/>.
15  *
16  */
17
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <stddef.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <sys/stat.h>
27 #include <string.h>
28 #include <dirent.h>
29 #include <libudev.h>
30 #include <fnmatch.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         /* read attribute value */
61         fd = open(devpath, O_RDONLY);
62         if (fd < 0) {
63                 condlog(4, "attribute '%s' can not be opened: %s",
64                         devpath, strerror(errno));
65                 return -errno;
66         }
67         if (fstat(fd, &statbuf) < 0) {
68                 condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
69                 close(fd);
70                 return -ENXIO;
71         }
72         /* skip directories */
73         if (S_ISDIR(statbuf.st_mode)) {
74                 condlog(4, "%s is a directory", devpath);
75                 close(fd);
76                 return -EISDIR;
77         }
78         /* skip non-writeable files */
79         if ((statbuf.st_mode & S_IRUSR) == 0) {
80                 condlog(4, "%s is not readable", devpath);
81                 close(fd);
82                 return -EPERM;
83         }
84
85         size = read(fd, value, value_len);
86         if (size < 0) {
87                 condlog(4, "read from %s failed: %s", devpath, strerror(errno));
88                 size = -errno;
89                 value[0] = '\0';
90         } else if (size == value_len) {
91                 value[size - 1] = '\0';
92                 condlog(4, "overflow while reading from %s", devpath);
93                 size = 0;
94         } else {
95                 value[size] = '\0';
96                 size = strchop(value);
97         }
98
99         close(fd);
100         return size;
101 }
102
103 ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name,
104                                  unsigned char * value, size_t value_len)
105 {
106         char devpath[PATH_SIZE];
107         struct stat statbuf;
108         int fd;
109         ssize_t size = -1;
110
111         if (!dev || !attr_name || !value)
112                 return 0;
113
114         snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev),
115                  attr_name);
116         condlog(4, "open '%s'", devpath);
117         /* read attribute value */
118         fd = open(devpath, O_RDONLY);
119         if (fd < 0) {
120                 condlog(4, "attribute '%s' can not be opened: %s",
121                         devpath, strerror(errno));
122                 return -errno;
123         }
124         if (fstat(fd, &statbuf) != 0) {
125                 condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
126                 close(fd);
127                 return -ENXIO;
128         }
129
130         /* skip directories */
131         if (S_ISDIR(statbuf.st_mode)) {
132                 condlog(4, "%s is a directory", devpath);
133                 close(fd);
134                 return -EISDIR;
135         }
136
137         /* skip non-writeable files */
138         if ((statbuf.st_mode & S_IRUSR) == 0) {
139                 condlog(4, "%s is not readable", devpath);
140                 close(fd);
141                 return -EPERM;
142         }
143
144         size = read(fd, value, value_len);
145         if (size < 0) {
146                 condlog(4, "read from %s failed: %s", devpath, strerror(errno));
147                 size = -errno;
148         } else if (size == value_len) {
149                 condlog(4, "overflow while reading from %s", devpath);
150                 size = 0;
151         }
152
153         close(fd);
154         return size;
155 }
156
157 ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name,
158                              const char * value, size_t value_len)
159 {
160         char devpath[PATH_SIZE];
161         struct stat statbuf;
162         int fd;
163         ssize_t size = -1;
164
165         if (!dev || !attr_name || !value || !value_len)
166                 return 0;
167
168         snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev),
169                  attr_name);
170         condlog(4, "open '%s'", devpath);
171         /* write attribute value */
172         fd = open(devpath, O_WRONLY);
173         if (fd < 0) {
174                 condlog(4, "attribute '%s' can not be opened: %s",
175                         devpath, strerror(errno));
176                 return -errno;
177         }
178         if (fstat(fd, &statbuf) != 0) {
179                 condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
180                 close(fd);
181                 return -errno;
182         }
183
184         /* skip directories */
185         if (S_ISDIR(statbuf.st_mode)) {
186                 condlog(4, "%s is a directory", devpath);
187                 close(fd);
188                 return -EISDIR;
189         }
190
191         /* skip non-writeable files */
192         if ((statbuf.st_mode & S_IWUSR) == 0) {
193                 condlog(4, "%s is not writeable", devpath);
194                 close(fd);
195                 return -EPERM;
196         }
197
198         size = write(fd, value, value_len);
199         if (size < 0) {
200                 condlog(4, "write to %s failed: %s", devpath, strerror(errno));
201                 size = -errno;
202         } else if (size < value_len) {
203                 condlog(4, "tried to write %ld to %s. Wrote %ld",
204                         (long)value_len, devpath, (long)size);
205                 size = 0;
206         }
207
208         close(fd);
209         return size;
210 }
211
212 int
213 sysfs_get_size (struct path *pp, unsigned long long * size)
214 {
215         char attr[255];
216         int r;
217
218         if (!pp->udev || !size)
219                 return 1;
220
221         attr[0] = '\0';
222         if (sysfs_attr_get_value(pp->udev, "size", attr, 255) <= 0) {
223                 condlog(3, "%s: No size attribute in sysfs", pp->dev);
224                 return 1;
225         }
226
227         r = sscanf(attr, "%llu\n", size);
228
229         if (r != 1) {
230                 condlog(3, "%s: Cannot parse size attribute", pp->dev);
231                 *size = 0;
232                 return 1;
233         }
234
235         return 0;
236 }
237
238 int sysfs_check_holders(char * check_devt, char * new_devt)
239 {
240         unsigned int major, new_minor, table_minor;
241         char path[PATH_SIZE], check_dev[PATH_SIZE];
242         char * table_name;
243         DIR *dirfd;
244         struct dirent *holder;
245
246         if (sscanf(new_devt,"%d:%d", &major, &new_minor) != 2) {
247                 condlog(1, "invalid device number %s", new_devt);
248                 return 0;
249         }
250
251         if (devt2devname(check_dev, PATH_SIZE, check_devt)) {
252                 condlog(1, "can't get devname for %s", check_devt);
253                 return 0;
254         }
255
256         condlog(3, "%s: checking holder", check_dev);
257
258         snprintf(path, PATH_SIZE, "/sys/block/%s/holders", check_dev);
259         dirfd = opendir(path);
260         if (dirfd == NULL) {
261                 condlog(3, "%s: failed to open directory %s (%d)",
262                         check_dev, path, errno);
263                 return 0;
264         }
265         while ((holder = readdir(dirfd)) != NULL) {
266                 if ((strcmp(holder->d_name,".") == 0) ||
267                     (strcmp(holder->d_name,"..") == 0))
268                         continue;
269
270                 if (sscanf(holder->d_name, "dm-%d", &table_minor) != 1) {
271                         condlog(3, "%s: %s is not a dm-device",
272                                 check_dev, holder->d_name);
273                         continue;
274                 }
275                 if (table_minor == new_minor) {
276                         condlog(3, "%s: holder already correct", check_dev);
277                         continue;
278                 }
279                 table_name = dm_mapname(major, table_minor);
280
281                 condlog(0, "%s: reassign table %s old %s new %s", check_dev,
282                         table_name, check_devt, new_devt);
283
284                 dm_reassign_table(table_name, check_devt, new_devt);
285                 FREE(table_name);
286         }
287         closedir(dirfd);
288
289         return 0;
290 }
291
292 static int select_dm_devs(const struct dirent *di)
293 {
294         return fnmatch("dm-*", di->d_name, FNM_FILE_NAME) == 0;
295 }
296
297 static void close_fd(void *arg)
298 {
299         close((long)arg);
300 }
301
302 bool sysfs_is_multipathed(const struct path *pp)
303 {
304         char pathbuf[PATH_MAX];
305         struct dirent **di;
306         int n, r, i;
307         bool found = false;
308
309         n = snprintf(pathbuf, sizeof(pathbuf), "/sys/block/%s/holders",
310                      pp->dev);
311
312         if (n >= sizeof(pathbuf)) {
313                 condlog(1, "%s: pathname overflow", __func__);
314                 return false;
315         }
316
317         r = scandir(pathbuf, &di, select_dm_devs, alphasort);
318         if (r == 0)
319                 return false;
320         else if (r < 0) {
321                 condlog(1, "%s: error scanning %s", __func__, pathbuf);
322                 return false;
323         }
324
325         pthread_cleanup_push(free, di);
326         for (i = 0; i < r && !found; i++) {
327                 long fd;
328                 int nr;
329                 char uuid[6];
330
331                 if (snprintf(pathbuf + n, sizeof(pathbuf) - n,
332                              "/%s/dm/uuid", di[i]->d_name)
333                     >= sizeof(pathbuf) - n)
334                         continue;
335
336                 fd = open(pathbuf, O_RDONLY);
337                 if (fd == -1) {
338                         condlog(1, "%s: error opening %s", __func__, pathbuf);
339                         continue;
340                 }
341
342                 pthread_cleanup_push(close_fd, (void *)fd);
343                 nr = read(fd, uuid, sizeof(uuid));
344                 if (nr == sizeof(uuid) && !memcmp(uuid, "mpath-", sizeof(uuid)))
345                         found = true;
346                 else if (nr < 0) {
347                         condlog(1, "%s: error reading from %s: %s",
348                                 __func__, pathbuf, strerror(errno));
349                 }
350                 pthread_cleanup_pop(1);
351         }
352         pthread_cleanup_pop(1);
353
354         return found;
355 }