Imported Upstream version 0.6.2
[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 extern int
66 is_loop_device (const char *device)
67 {
68         struct stat statbuf;
69         int loopmajor;
70 #if 1
71         loopmajor = 7;
72 #else
73         FILE *procdev;
74         char line[100], *cp;
75
76         loopmajor = 0;
77
78         if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) {
79
80                 while (fgets (line, sizeof(line), procdev)) {
81
82                         if ((cp = strstr (line, " loop\n")) != NULL) {
83                                 *cp='\0';
84                                 loopmajor=atoi(line);
85                                 break;
86                         }
87                 }
88
89                 fclose(procdev);
90         }
91 #endif
92         return (loopmajor && stat(device, &statbuf) == 0 &&
93                 S_ISBLK(statbuf.st_mode) &&
94                 major(statbuf.st_rdev) == loopmajor);
95 }
96
97 #define SIZE(a) (sizeof(a)/sizeof(a[0]))
98
99 extern char *
100 find_loop_by_file (const char * filename)
101 {
102         DIR *dir;
103         struct dirent *dent;
104         char dev[64], *found = NULL;
105         int fd;
106         struct stat statbuf;
107         struct loop_info loopinfo;
108
109         dir = opendir("/dev");
110         if (!dir)
111                 return NULL;
112
113         while ((dent = readdir(dir)) != NULL) {
114                 if (strncmp(dent->d_name,"loop",4))
115                         continue;
116                 if (!strcmp(dent->d_name, "loop-control"))
117                         continue;
118                 sprintf(dev, "/dev/%s", dent->d_name);
119
120                 fd = open (dev, O_RDONLY);
121                 if (fd < 0)
122                         break;
123
124                 if (fstat (fd, &statbuf) != 0 ||
125                     !S_ISBLK(statbuf.st_mode)) {
126                         close (fd);
127                         continue;
128                 }
129
130                 if (ioctl (fd, LOOP_GET_STATUS, &loopinfo) != 0) {
131                         close (fd);
132                         continue;
133                 }
134
135                 if (0 == strcmp(filename, loopinfo.lo_name)) {
136                         close (fd);
137                         found = xstrdup(dev);
138                         break;
139                 }
140
141                 close (fd);
142         }
143         closedir(dir);
144         return found;
145 }
146
147 extern char *
148 find_unused_loop_device (void)
149 {
150         char dev[20], *next_loop_dev = NULL;;
151         int fd, next_loop = 0, somedev = 0, someloop = 0, loop_known = 0;
152         struct stat statbuf;
153         struct loop_info loopinfo;
154         FILE *procdev;
155
156         while (next_loop_dev == NULL) {
157                 if (stat("/dev/loop-control", &statbuf) == 0 &&
158                     S_ISCHR(statbuf.st_mode)) {
159                         int next_loop_fd;
160
161                         next_loop_fd = open("/dev/loop-control", O_RDWR);
162                         if (next_loop_fd < 0)
163                                 return NULL;
164                         next_loop = ioctl(next_loop_fd, LOOP_CTL_GET_FREE);
165                         close(next_loop_fd);
166                         if (next_loop < 0)
167                                 return NULL;
168                 }
169
170                 sprintf(dev, "/dev/loop%d", next_loop);
171
172                 fd = open (dev, O_RDONLY);
173                 if (fd >= 0) {
174                         if (fstat (fd, &statbuf) == 0 &&
175                             S_ISBLK(statbuf.st_mode)) {
176                                 somedev++;
177                                 if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0)
178                                         someloop++;             /* in use */
179                                 else if (errno == ENXIO)
180                                         next_loop_dev = xstrdup(dev);
181
182                         }
183                         close (fd);
184
185                         /* continue trying as long as devices exist */
186                         continue;
187                 }
188                 break;
189         }
190         if (next_loop_dev)
191                 return next_loop_dev;
192
193         /* Nothing found. Why not? */
194         if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) {
195                 char line[100];
196
197                 while (fgets (line, sizeof(line), procdev))
198
199                         if (strstr (line, " loop\n")) {
200                                 loop_known = 1;
201                                 break;
202                         }
203
204                 fclose(procdev);
205
206                 if (!loop_known)
207                         loop_known = -1;
208         }
209
210         if (!somedev)
211                 fprintf(stderr, "mount: could not find any device /dev/loop#");
212
213         else if (!someloop) {
214             if (loop_known == 1)
215                 fprintf(stderr,
216                     "mount: Could not find any loop device.\n"
217                     "       Maybe /dev/loop# has a wrong major number?");
218             else if (loop_known == -1)
219                 fprintf(stderr,
220                     "mount: Could not find any loop device, and, according to %s,\n"
221                     "       this kernel does not know about the loop device.\n"
222                     "       (If so, then recompile or `modprobe loop'.)",
223                       PROC_DEVICES);
224
225             else
226                 fprintf(stderr,
227                     "mount: Could not find any loop device. Maybe this kernel does not know\n"
228                     "       about the loop device (then recompile or `modprobe loop'), or\n"
229                     "       maybe /dev/loop# has the wrong major number?");
230
231         } else
232                 fprintf(stderr, "mount: could not find any free loop device");
233         return 0;
234 }
235
236 extern int
237 set_loop (const char *device, const char *file, int offset, int *loopro)
238 {
239         struct loop_info loopinfo;
240         int fd, ffd, mode;
241
242         mode = (*loopro ? O_RDONLY : O_RDWR);
243
244         if ((ffd = open (file, mode)) < 0) {
245
246                 if (!*loopro && (errno == EROFS || errno == EACCES))
247                         ffd = open (file, mode = O_RDONLY);
248
249                 if (ffd < 0) {
250                         perror (file);
251                         return 1;
252                 }
253         }
254
255         if ((fd = open (device, mode)) < 0) {
256                 close(ffd);
257                 perror (device);
258                 return 1;
259         }
260
261         *loopro = (mode == O_RDONLY);
262         memset (&loopinfo, 0, sizeof (loopinfo));
263
264         xstrncpy (loopinfo.lo_name, file, LO_NAME_SIZE);
265         loopinfo.lo_offset = offset;
266         loopinfo.lo_encrypt_type = LO_CRYPT_NONE;
267         loopinfo.lo_encrypt_key_size = 0;
268
269         if (ioctl (fd, LOOP_SET_FD, int2ptr(ffd)) < 0) {
270                 perror ("ioctl: LOOP_SET_FD");
271                 close (fd);
272                 close (ffd);
273                 return 1;
274         }
275
276         if (ioctl (fd, LOOP_SET_STATUS, &loopinfo) < 0) {
277                 (void) ioctl (fd, LOOP_CLR_FD, 0);
278                 perror ("ioctl: LOOP_SET_STATUS");
279                 close (fd);
280                 close (ffd);
281                 return 1;
282         }
283
284         close (fd);
285         close (ffd);
286         return 0;
287 }
288
289 extern int
290 del_loop (const char *device)
291 {
292         int retries = 5;
293         int fd;
294
295         if ((fd = open (device, O_RDONLY)) < 0) {
296                 int errsv = errno;
297                 fprintf(stderr, "loop: can't delete device %s: %s\n",
298                         device, strerror (errsv));
299                 return 1;
300         }
301
302         while (ioctl (fd, LOOP_CLR_FD, 0) < 0) {
303                 if (errno != EBUSY || retries-- <= 0) {
304                         perror ("ioctl: LOOP_CLR_FD");
305                         close (fd);
306                         return 1;
307                 }
308                 fprintf(stderr,
309                         "loop: device %s still in use, retrying delete\n",
310                         device);
311                 sleep(1);
312                 continue;
313         }
314
315         close (fd);
316         return 0;
317 }