7e872ad6693bf5ce40d296fe88c3d526815688b1
[platform/upstream/cryptsetup.git] / lib / utils_devpath.c
1 /*
2  * devname - search for device name
3  *
4  * Copyright (C) 2004, Christophe Saout <christophe@saout.de>
5  * Copyright (C) 2004-2007, Clemens Fruhwirth <clemens@endorphin.org>
6  * Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved.
7  * Copyright (C) 2009-2012, Milan Broz
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23
24 #include <string.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <dirent.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include "utils_dm.h"
34
35 char *crypt_lookup_dev(const char *dev_id);
36 int crypt_sysfs_get_rotational(int major, int minor, int *rotational);
37
38 static char *__lookup_dev(char *path, dev_t dev, int dir_level, const int max_level)
39 {
40         struct dirent *entry;
41         struct stat st;
42         char *ptr;
43         char *result = NULL;
44         DIR *dir;
45         int space;
46
47         /* Ignore strange nested directories */
48         if (dir_level > max_level)
49                 return NULL;
50
51         path[PATH_MAX - 1] = '\0';
52         ptr = path + strlen(path);
53         *ptr++ = '/';
54         *ptr = '\0';
55         space = PATH_MAX - (ptr - path);
56
57         dir = opendir(path);
58         if (!dir)
59                 return NULL;
60
61         while((entry = readdir(dir))) {
62                 if (entry->d_name[0] == '.' ||
63                     !strncmp(entry->d_name, "..", 2))
64                         continue;
65
66                 if (dir_level == 0 &&
67                     (!strcmp(entry->d_name, "shm") ||
68                      !strcmp(entry->d_name, "fd") ||
69                      !strcmp(entry->d_name, "char") ||
70                      !strcmp(entry->d_name, "pts")))
71                         continue;
72
73                 strncpy(ptr, entry->d_name, space);
74                 if (stat(path, &st) < 0)
75                         continue;
76
77                 if (S_ISDIR(st.st_mode)) {
78                         result = __lookup_dev(path, dev, dir_level + 1, max_level);
79                         if (result)
80                                 break;
81                 } else if (S_ISBLK(st.st_mode)) {
82                         /* workaround: ignore dm-X devices, these are internal kernel names */
83                         if (dir_level == 0 && dm_is_dm_kernel_name(entry->d_name))
84                                 continue;
85                         if (st.st_rdev == dev) {
86                                 result = strdup(path);
87                                 break;
88                         }
89                 }
90         }
91
92         closedir(dir);
93         return result;
94 }
95
96 /*
97  * Non-udev systemd need to scan for device here.
98  */
99 static char *lookup_dev_old(int major, int minor)
100 {
101         dev_t dev;
102         char *result = NULL, buf[PATH_MAX + 1];
103
104         dev = makedev(major, minor);
105         strncpy(buf, "/dev", PATH_MAX);
106         buf[PATH_MAX] = '\0';
107
108         /* First try low level device */
109         if ((result = __lookup_dev(buf, dev, 0, 0)))
110                 return result;
111
112         /* If it is dm, try DM dir  */
113         if (dm_is_dm_device(major, minor)) {
114                 strncpy(buf, dm_get_dir(), PATH_MAX);
115                 if ((result = __lookup_dev(buf, dev, 0, 0)))
116                         return result;
117         }
118
119         strncpy(buf, "/dev", PATH_MAX);
120         return  __lookup_dev(buf, dev, 0, 4);
121 }
122
123 /*
124  * Returns string pointing to device in /dev according to "major:minor" dev_id
125  */
126 char *crypt_lookup_dev(const char *dev_id)
127 {
128         int major, minor;
129         char link[PATH_MAX], path[PATH_MAX], *devname, *devpath = NULL;
130         struct stat st;
131         ssize_t len;
132
133         if (sscanf(dev_id, "%d:%d", &major, &minor) != 2)
134                 return NULL;
135
136         if (snprintf(path, sizeof(path), "/sys/dev/block/%s", dev_id) < 0)
137                 return NULL;
138
139         len = readlink(path, link, sizeof(link) - 1);
140         if (len < 0) {
141                 /* Without /sys use old scan */
142                 if (stat("/sys/dev/block", &st) < 0)
143                         return lookup_dev_old(major, minor);
144                 return NULL;
145         }
146
147         link[len] = '\0';
148         devname = strrchr(link, '/');
149         if (!devname)
150                 return NULL;
151         devname++;
152
153         if (dm_is_dm_kernel_name(devname))
154                 devpath = dm_device_path("/dev/mapper/", major, minor);
155         else if (snprintf(path, sizeof(path), "/dev/%s", devname) > 0)
156                 devpath = strdup(path);
157
158         /*
159          * Check that path is correct.
160          */
161         if (devpath && ((stat(devpath, &st) < 0) ||
162             !S_ISBLK(st.st_mode) ||
163             (st.st_rdev != makedev(major, minor)))) {
164                 free(devpath);
165                 /* Should never happen unless user mangles with dev nodes. */
166                 return lookup_dev_old(major, minor);
167         }
168
169         return devpath;
170 }
171
172 int crypt_sysfs_get_rotational(int major, int minor, int *rotational)
173 {
174         char path[PATH_MAX], tmp[64] = {0};
175         int fd, r;
176
177         if (snprintf(path, sizeof(path), "/sys/dev/block/%d:%d/queue/rotational",
178                      major, minor) < 0)
179                 return 0;
180
181         if ((fd = open(path, O_RDONLY)) < 0)
182                 return 0;
183         r = read(fd, tmp, sizeof(tmp));
184         close(fd);
185
186         if (r <= 0)
187                 return 0;
188
189         if (sscanf(tmp, "%d", rotational) != 1)
190                 return 0;
191
192         return 1;
193 }