db44ebdc74
LOOP_SET_FD, LOOP_SET_STATUS ioctls trigger a `change` event with loopback device in question. udev handles those events with (builtin) blkid command. Probing a device with blkid takes a while, so init might try to mount the loopback device in question while `blkid` is still running. As a result init and udev block each other. Eventually (after 3 minutes or whatever udev event timeout is) `blkid` gets killed and boot proceeds. However such long delays are very annoying. Therefore run `udev_settle` after each loop related ioctl to avoid the concurrent access to the same loopback device. Closes: #40687
193 lines
4.1 KiB
C
193 lines
4.1 KiB
C
/*
|
|
* Guillaume Cottenceau (gc@mandrakesoft.com)
|
|
*
|
|
* Copyright 2000 MandrakeSoft
|
|
*
|
|
* This software may be freely redistributed under the terms of the GNU
|
|
* public license.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
/* This code comes from util-linux-2.10n (mount/lomount.c)
|
|
* (this is a simplified version of this code)
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mount.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "stage1.h"
|
|
#include "frontend.h"
|
|
#include "log.h"
|
|
#include "mount.h"
|
|
#include "modules.h"
|
|
#include "udev.h"
|
|
|
|
#include "lomount.h"
|
|
|
|
|
|
#define LO_NAME_SIZE 64
|
|
#define LO_KEY_SIZE 32
|
|
|
|
struct loop_info
|
|
{
|
|
int lo_number; /* ioctl r/o */
|
|
dev_t lo_device; /* ioctl r/o */
|
|
unsigned long lo_inode; /* ioctl r/o */
|
|
dev_t lo_rdevice; /* ioctl r/o */
|
|
int lo_offset;
|
|
int lo_encrypt_type;
|
|
int lo_encrypt_key_size; /* ioctl w/o */
|
|
int lo_flags; /* ioctl r/o */
|
|
char lo_name[LO_NAME_SIZE];
|
|
unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
|
|
unsigned long lo_init[2];
|
|
char reserved[4];
|
|
};
|
|
|
|
#define LOOP_SET_FD 0x4C00
|
|
#define LOOP_CLR_FD 0x4C01
|
|
#define LOOP_SET_STATUS 0x4C02
|
|
#define LOOP_GET_STATUS 0x4C03
|
|
|
|
int
|
|
set_loop (const char *device, const char *file)
|
|
{
|
|
struct loop_info loopinfo;
|
|
int i, fd, ffd, mode;
|
|
|
|
mode = O_RDONLY;
|
|
|
|
if ((ffd = open (file, mode)) < 0)
|
|
return 1;
|
|
|
|
for (i=3; i && (fd = open(device, mode)) < 0; --i, sleep(1));
|
|
if (fd < 0) {
|
|
close(ffd);
|
|
return 1;
|
|
}
|
|
|
|
memset(&loopinfo, 0, sizeof (loopinfo));
|
|
strncpy(loopinfo.lo_name, file, LO_NAME_SIZE);
|
|
loopinfo.lo_name[LO_NAME_SIZE - 1] = 0;
|
|
loopinfo.lo_offset = 0;
|
|
|
|
#ifdef MCL_FUTURE
|
|
/*
|
|
* Oh-oh, sensitive data coming up. Better lock into memory to prevent
|
|
* passwd etc being swapped out and left somewhere on disk.
|
|
*/
|
|
|
|
if(mlockall(MCL_CURRENT|MCL_FUTURE)) {
|
|
log_message("CRITICAL Couldn't lock into memory! %s (memlock)", strerror(errno));
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
if (ioctl(fd, LOOP_SET_FD, ffd) < 0) {
|
|
close(fd);
|
|
close(ffd);
|
|
return 1;
|
|
}
|
|
/* Note: LOOP_SET_FD triggers change event. While processing it
|
|
* udev reads the loop device with builtin blkid. This can race
|
|
* with subsequent access by kernel due to LOOP_SET_STATUS (or
|
|
* mounting the loop device). Therefore give udev a chance to
|
|
* process the event without concurrent access to the loop device
|
|
*/
|
|
udev_settle();
|
|
|
|
if (ioctl(fd, LOOP_SET_STATUS, &loopinfo) < 0) {
|
|
(void) ioctl (fd, LOOP_CLR_FD, 0);
|
|
close(fd);
|
|
close(ffd);
|
|
return 1;
|
|
}
|
|
|
|
/* Same here: LOOP_SET_STATUS triggers change event, give udev
|
|
* a chance to process it without concurrent access to the loop
|
|
* device. In other words, prevent the caller of this function
|
|
* from mounting the device while udev still running blkid on it
|
|
*/
|
|
udev_settle();
|
|
|
|
close(fd);
|
|
close(ffd);
|
|
/* udev might be monitoring loopback device (with fanotify) and
|
|
* will synthesize a change event on close. Give udev some time
|
|
* to process without racing with the caller of this function
|
|
* (which is going to mount the newly configured loopback device)
|
|
*/
|
|
udev_settle();
|
|
return 0;
|
|
}
|
|
|
|
|
|
char * loopdev = "/dev/loop3"; /* Ugly. But do I care? */
|
|
|
|
void del_loop(char *device)
|
|
{
|
|
int fd;
|
|
|
|
if ((fd = open (device, O_RDONLY)) < 0)
|
|
return;
|
|
|
|
if (ioctl (fd, LOOP_CLR_FD, 0) < 0)
|
|
return;
|
|
|
|
close (fd);
|
|
}
|
|
|
|
|
|
static char * where_mounted = NULL;
|
|
|
|
int
|
|
lomount(char *loopfile, char *where)
|
|
{
|
|
|
|
long int flag;
|
|
|
|
flag = MS_MGC_VAL;
|
|
flag |= MS_RDONLY;
|
|
|
|
my_insmod("loop", NULL);
|
|
|
|
if (set_loop(loopdev, loopfile)) {
|
|
log_message("set_loop failed on %s (%s)", loopdev, strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
if (my_mount(loopdev, where, "iso9660", 0)) {
|
|
del_loop(loopdev);
|
|
return 1;
|
|
}
|
|
|
|
where_mounted = strdup(where);
|
|
log_message("lomount succeeded for %s on %s", loopfile, where);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
loumount()
|
|
{
|
|
if (where_mounted) {
|
|
umount(where_mounted);
|
|
where_mounted = NULL;
|
|
}
|
|
del_loop(loopdev);
|
|
return 0;
|
|
}
|
|
|
|
|