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