Imported Upstream version 0.7.0
[platform/upstream/multipath-tools.git] / kpartx / lopart.c
1 /* Taken from Ted's losetup.c - Mitch <m.dsouza@mrc-apu.cam.ac.uk> */
2 /* Added vfs mount options - aeb - 960223 */
3 /* Removed lomount - aeb - 960224 */
4
5 /* 1999-02-22 Arkadiusz Miƛkiewicz <misiek@pld.ORG.PL>
6  * - added Native Language Support
7  * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
8  * - fixed strerr(errno) in gettext calls
9  */
10
11 #define PROC_DEVICES    "/proc/devices"
12
13 /*
14  * losetup.c - setup and control loop devices
15  */
16
17 #include "kpartx.h"
18 #include <stdio.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <sys/ioctl.h>
26 #include <sys/stat.h>
27 #include <sys/mman.h>
28 #include <sys/types.h>
29 #include <dirent.h>
30 #include "sysmacros.h"
31 #include <linux/loop.h>
32
33 #include "lopart.h"
34 #include "xstrncpy.h"
35
36 #ifndef LOOP_CTL_GET_FREE
37 #define LOOP_CTL_GET_FREE       0x4C82
38 #endif
39
40 #if !defined (__alpha__) && !defined (__ia64__) && !defined (__x86_64__) \
41         && !defined (__s390x__)
42 #define int2ptr(x)      ((void *) ((int) x))
43 #else
44 #define int2ptr(x)      ((void *) ((long) x))
45 #endif
46
47 static char *
48 xstrdup (const char *s)
49 {
50         char *t;
51
52         if (s == NULL)
53                 return NULL;
54
55         t = strdup (s);
56
57         if (t == NULL) {
58                 fprintf(stderr, "not enough memory");
59                 exit(1);
60         }
61
62         return t;
63 }
64
65 int is_loop_device(const char *device)
66 {
67         struct stat statbuf;
68         int loopmajor;
69 #if 1
70         loopmajor = 7;
71 #else
72         FILE *procdev;
73         char line[100], *cp;
74
75         loopmajor = 0;
76
77         if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) {
78
79                 while (fgets (line, sizeof(line), procdev)) {
80
81                         if ((cp = strstr (line, " loop\n")) != NULL) {
82                                 *cp='\0';
83                                 loopmajor=atoi(line);
84                                 break;
85                         }
86                 }
87
88                 fclose(procdev);
89         }
90 #endif
91         return (loopmajor && stat(device, &statbuf) == 0 &&
92                 S_ISBLK(statbuf.st_mode) &&
93                 major(statbuf.st_rdev) == loopmajor);
94 }
95
96 #define SIZE(a) (sizeof(a)/sizeof(a[0]))
97
98 char *find_loop_by_file(const char *filename)
99 {
100         DIR *dir;
101         struct dirent *dent;
102         char dev[64], *found = NULL;
103         int fd;
104         struct stat statbuf;
105         struct loop_info loopinfo;
106
107         dir = opendir("/dev");
108         if (!dir)
109                 return NULL;
110
111         while ((dent = readdir(dir)) != NULL) {
112                 if (strncmp(dent->d_name,"loop",4))
113                         continue;
114                 if (!strcmp(dent->d_name, "loop-control"))
115                         continue;
116                 sprintf(dev, "/dev/%s", dent->d_name);
117
118                 fd = open (dev, O_RDONLY);
119                 if (fd < 0)
120                         break;
121
122                 if (fstat (fd, &statbuf) != 0 ||
123                     !S_ISBLK(statbuf.st_mode)) {
124                         close (fd);
125                         continue;
126                 }
127
128                 if (ioctl (fd, LOOP_GET_STATUS, &loopinfo) != 0) {
129                         close (fd);
130                         continue;
131                 }
132
133                 if (0 == strcmp(filename, loopinfo.lo_name)) {
134                         close (fd);
135                         found = xstrdup(dev);
136                         break;
137                 }
138
139                 close (fd);
140         }
141         closedir(dir);
142         return found;
143 }
144
145 char *find_unused_loop_device(void)
146 {
147         char dev[20], *next_loop_dev = NULL;
148         int fd, next_loop = 0, somedev = 0, someloop = 0, loop_known = 0;
149         struct stat statbuf;
150         struct loop_info loopinfo;
151         FILE *procdev;
152
153         while (next_loop_dev == NULL) {
154                 if (stat("/dev/loop-control", &statbuf) == 0 &&
155                     S_ISCHR(statbuf.st_mode)) {
156                         int next_loop_fd;
157
158                         next_loop_fd = open("/dev/loop-control", O_RDWR);
159                         if (next_loop_fd < 0)
160                                 return NULL;
161                         next_loop = ioctl(next_loop_fd, LOOP_CTL_GET_FREE);
162                         close(next_loop_fd);
163                         if (next_loop < 0)
164                                 return NULL;
165                 }
166
167                 sprintf(dev, "/dev/loop%d", next_loop);
168
169                 fd = open (dev, O_RDONLY);
170                 if (fd >= 0) {
171                         if (fstat (fd, &statbuf) == 0 &&
172                             S_ISBLK(statbuf.st_mode)) {
173                                 somedev++;
174                                 if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0)
175                                         someloop++;             /* in use */
176                                 else if (errno == ENXIO)
177                                         next_loop_dev = xstrdup(dev);
178
179                         }
180                         close (fd);
181
182                         /* continue trying as long as devices exist */
183                         continue;
184                 }
185                 break;
186         }
187         if (next_loop_dev)
188                 return next_loop_dev;
189
190         /* Nothing found. Why not? */
191         if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) {
192                 char line[100];
193
194                 while (fgets (line, sizeof(line), procdev))
195
196                         if (strstr (line, " loop\n")) {
197                                 loop_known = 1;
198                                 break;
199                         }
200
201                 fclose(procdev);
202
203                 if (!loop_known)
204                         loop_known = -1;
205         }
206
207         if (!somedev)
208                 fprintf(stderr, "mount: could not find any device /dev/loop#");
209
210         else if (!someloop) {
211                 if (loop_known == 1)
212                         fprintf(stderr,
213                                 "mount: Could not find any loop device.\n"
214                                 "       Maybe /dev/loop# has a wrong major number?");
215                 else if (loop_known == -1)
216                         fprintf(stderr,
217                                 "mount: Could not find any loop device, and, according to %s,\n"
218                                 "       this kernel does not know about the loop device.\n"
219                                 "       (If so, then recompile or `modprobe loop'.)",
220                                 PROC_DEVICES);
221                 else
222                         fprintf(stderr,
223                                 "mount: Could not find any loop device. Maybe this kernel does not know\n"
224                                 "       about the loop device (then recompile or `modprobe loop'), or\n"
225                                 "       maybe /dev/loop# has the wrong major number?");
226         } else
227                 fprintf(stderr, "mount: could not find any free loop device");
228         return NULL;
229 }
230
231 int set_loop(const char *device, const char *file, int offset, int *loopro)
232 {
233         struct loop_info loopinfo;
234         int fd, ffd, mode;
235
236         mode = (*loopro ? O_RDONLY : O_RDWR);
237
238         if ((ffd = open (file, mode)) < 0) {
239
240                 if (!*loopro && (errno == EROFS || errno == EACCES))
241                         ffd = open (file, mode = O_RDONLY);
242
243                 if (ffd < 0) {
244                         perror (file);
245                         return 1;
246                 }
247         }
248
249         if ((fd = open (device, mode)) < 0) {
250                 close(ffd);
251                 perror (device);
252                 return 1;
253         }
254
255         *loopro = (mode == O_RDONLY);
256         memset (&loopinfo, 0, sizeof (loopinfo));
257
258         xstrncpy (loopinfo.lo_name, file, LO_NAME_SIZE);
259         loopinfo.lo_offset = offset;
260         loopinfo.lo_encrypt_type = LO_CRYPT_NONE;
261         loopinfo.lo_encrypt_key_size = 0;
262
263         if (ioctl (fd, LOOP_SET_FD, int2ptr(ffd)) < 0) {
264                 perror ("ioctl: LOOP_SET_FD");
265                 close (fd);
266                 close (ffd);
267                 return 1;
268         }
269
270         if (ioctl (fd, LOOP_SET_STATUS, &loopinfo) < 0) {
271                 (void) ioctl (fd, LOOP_CLR_FD, 0);
272                 perror ("ioctl: LOOP_SET_STATUS");
273                 close (fd);
274                 close (ffd);
275                 return 1;
276         }
277
278         close (fd);
279         close (ffd);
280         return 0;
281 }
282
283 int del_loop(const char *device)
284 {
285         int retries = 5;
286         int fd;
287
288         if ((fd = open (device, O_RDONLY)) < 0) {
289                 int errsv = errno;
290                 fprintf(stderr, "loop: can't delete device %s: %s\n",
291                         device, strerror (errsv));
292                 return 1;
293         }
294
295         while (ioctl (fd, LOOP_CLR_FD, 0) < 0) {
296                 if (errno != EBUSY || retries-- <= 0) {
297                         perror ("ioctl: LOOP_CLR_FD");
298                         close (fd);
299                         return 1;
300                 }
301                 fprintf(stderr,
302                         "loop: device %s still in use, retrying delete\n",
303                         device);
304                 sleep(1);
305                 continue;
306         }
307
308         close (fd);
309         return 0;
310 }