Imported Upstream version 0.5.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 <sysmacros.h>
29 #include <asm/posix_types.h>
30 #include <linux/loop.h>
31
32 #include "lopart.h"
33 #include "xstrncpy.h"
34
35 #ifndef LOOP_CTL_GET_FREE
36 #define LOOP_CTL_GET_FREE       0x4C82
37 #endif
38
39 #if !defined (__alpha__) && !defined (__ia64__) && !defined (__x86_64__) \
40         && !defined (__s390x__)
41 #define int2ptr(x)      ((void *) ((int) x))
42 #else
43 #define int2ptr(x)      ((void *) ((long) x))
44 #endif
45
46 static char *
47 xstrdup (const char *s)
48 {
49         char *t;
50
51         if (s == NULL)
52                 return NULL;
53
54         t = strdup (s);
55
56         if (t == NULL) {
57                 fprintf(stderr, "not enough memory");
58                 exit(1);
59         }
60
61         return t;
62 }
63
64 extern int
65 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 extern char *
99 find_loop_by_file (const char * filename)
100 {
101         char dev[64];
102         char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
103         int i, j, fd;
104         struct stat statbuf;
105         struct loop_info loopinfo;
106
107         for (j = 0; j < SIZE(loop_formats); j++) {
108
109                 for (i = 0; i < 256; i++) {
110                         sprintf (dev, loop_formats[j], i);
111
112                         if (stat (dev, &statbuf) != 0 ||
113                             !S_ISBLK(statbuf.st_mode))
114                                 continue;
115
116                         fd = open (dev, O_RDONLY);
117
118                         if (fd < 0)
119                                 break;
120
121                         if (ioctl (fd, LOOP_GET_STATUS, &loopinfo) != 0) {
122                                 close (fd);
123                                 continue;
124                         }
125
126                         if (0 == strcmp(filename, loopinfo.lo_name)) {
127                                 close (fd);
128                                 return xstrdup(dev); /*found */
129                         }
130
131                         close (fd);
132                         continue;
133                 }
134         }
135         return NULL;
136 }
137
138 extern char *
139 find_unused_loop_device (void)
140 {
141         /* Just creating a device, say in /tmp, is probably a bad idea -
142            people might have problems with backup or so.
143            So, we just try /dev/loop[0-7]. */
144
145         char dev[20];
146         char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
147         int i, j, fd, first = 0, somedev = 0, someloop = 0, loop_known = 0;
148         struct stat statbuf;
149         struct loop_info loopinfo;
150         FILE *procdev;
151
152         if (stat("/dev/loop-control", &statbuf) == 0 &&
153             S_ISCHR(statbuf.st_mode)) {
154                 fd = open("/dev/loop-control", O_RDWR);
155                 if (fd >= 0)
156                         first = ioctl(fd, LOOP_CTL_GET_FREE);
157                 close(fd);
158                 if (first < 0)
159                         first = 0;
160         }
161         for (j = 0; j < SIZE(loop_formats); j++) {
162
163             for(i = first; i < 256; i++) {
164                 sprintf(dev, loop_formats[j], i);
165
166                 if (stat (dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) {
167                         somedev++;
168                         fd = open (dev, O_RDONLY);
169
170                         if (fd >= 0) {
171
172                                 if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0)
173                                         someloop++;             /* in use */
174
175                                 else if (errno == ENXIO) {
176                                         close (fd);
177                                         return xstrdup(dev);/* probably free */
178                                 }
179
180                                 close (fd);
181                         }
182                         
183                         /* continue trying as long as devices exist */
184                         continue;
185                 }
186                 break;
187             }
188         }
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
212             if (loop_known == 1)
213                 fprintf(stderr,
214                     "mount: Could not find any loop device.\n"
215                     "       Maybe /dev/loop# has a wrong major number?");
216             
217             else if (loop_known == -1)
218                 fprintf(stderr,
219                     "mount: Could not find any loop device, and, according to %s,\n"
220                     "       this kernel does not know about the loop device.\n"
221                     "       (If so, then recompile or `modprobe loop'.)",
222                       PROC_DEVICES);
223
224             else
225                 fprintf(stderr,
226                     "mount: Could not find any loop device. Maybe this kernel does not know\n"
227                     "       about the loop device (then recompile or `modprobe loop'), or\n"
228                     "       maybe /dev/loop# has the wrong major number?");
229
230         } else
231                 fprintf(stderr, "mount: could not find any free loop device");
232         
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                 perror (device);
257                 return 1;
258         }
259
260         *loopro = (mode == O_RDONLY);
261         memset (&loopinfo, 0, sizeof (loopinfo));
262
263         xstrncpy (loopinfo.lo_name, file, LO_NAME_SIZE);
264         loopinfo.lo_offset = offset;
265         loopinfo.lo_encrypt_type = LO_CRYPT_NONE;
266         loopinfo.lo_encrypt_key_size = 0;
267
268         if (ioctl (fd, LOOP_SET_FD, int2ptr(ffd)) < 0) {
269                 perror ("ioctl: LOOP_SET_FD");
270                 close (fd);
271                 close (ffd);
272                 return 1;
273         }
274
275         if (ioctl (fd, LOOP_SET_STATUS, &loopinfo) < 0) {
276                 (void) ioctl (fd, LOOP_CLR_FD, 0);
277                 perror ("ioctl: LOOP_SET_STATUS");
278                 close (fd);
279                 close (ffd);
280                 return 1;
281         }
282
283         close (fd);
284         close (ffd);
285         return 0;
286 }
287
288 extern int 
289 del_loop (const char *device)
290 {
291         int retries = 3;
292         int fd;
293
294         if ((fd = open (device, O_RDONLY)) < 0) {
295                 int errsv = errno;
296                 fprintf(stderr, "loop: can't delete device %s: %s\n",
297                         device, strerror (errsv));
298                 return 1;
299         }
300
301         while (ioctl (fd, LOOP_CLR_FD, 0) < 0) {
302                 if (errno != EBUSY || retries-- <= 0) {
303                         perror ("ioctl: LOOP_CLR_FD");
304                         close (fd);
305                         return 1;
306                 }
307                 fprintf(stderr,
308                         "loop: device %s still in use, retrying delete\n",
309                         device);
310                 sleep(1);
311                 continue;
312         }
313
314         close (fd);
315         return 0;
316 }