blob: 0874f3ea0979b3532b798d5b96cccd6341acfece [file] [log] [blame]
/* Taken from Ted's losetup.c - Mitch <m.dsouza@mrc-apu.cam.ac.uk> */
/* Added vfs mount options - aeb - 960223 */
/* Removed lomount - aeb - 960224 */
/*
* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
* - added Native Language Support
* 1999-03-21 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
* - fixed strerr(errno) in gettext calls
* 2000-09-24 Marc Mutz <Marc@Mutz.com>
* - added -p option to pass passphrases via fd's to losetup/mount.
* Used for encryption in non-interactive environments.
* The idea behind xgetpass() is stolen from GnuPG, v.1.0.3.
*/
#define LOOPMAJOR 7
/*
* losetup.c - setup and control loop devices
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/sysmacros.h>
#include "loop.h"
#include "lomount.h"
#include "xstrncpy.h"
#include "nls.h"
extern int verbose;
extern char *xstrdup (const char *s); /* not: #include "sundries.h" */
extern void error (const char *fmt, ...); /* idem */
#ifdef LOOP_SET_FD
static int
loop_info64_to_old(const struct loop_info64 *info64, struct loop_info *info)
{
memset(info, 0, sizeof(*info));
info->lo_number = info64->lo_number;
info->lo_device = info64->lo_device;
info->lo_inode = info64->lo_inode;
info->lo_rdevice = info64->lo_rdevice;
info->lo_offset = info64->lo_offset;
info->lo_encrypt_type = info64->lo_encrypt_type;
info->lo_encrypt_key_size = info64->lo_encrypt_key_size;
info->lo_flags = info64->lo_flags;
info->lo_init[0] = info64->lo_init[0];
info->lo_init[1] = info64->lo_init[1];
if (info->lo_encrypt_type == LO_CRYPT_CRYPTOAPI)
memcpy(info->lo_name, info64->lo_crypt_name, LO_NAME_SIZE);
else
memcpy(info->lo_name, info64->lo_file_name, LO_NAME_SIZE);
memcpy(info->lo_encrypt_key, info64->lo_encrypt_key, LO_KEY_SIZE);
/* error in case values were truncated */
if (info->lo_device != info64->lo_device ||
info->lo_rdevice != info64->lo_rdevice ||
info->lo_inode != info64->lo_inode ||
info->lo_offset != info64->lo_offset)
return -EOVERFLOW;
return 0;
}
#ifdef MAIN
static int
show_loop(char *device) {
struct loop_info loopinfo;
struct loop_info64 loopinfo64;
int fd, errsv;
if ((fd = open(device, O_RDONLY)) < 0) {
int errsv = errno;
fprintf(stderr, _("loop: can't open device %s: %s\n"),
device, strerror (errsv));
return 2;
}
if (ioctl(fd, LOOP_GET_STATUS64, &loopinfo64) == 0) {
loopinfo64.lo_file_name[LO_NAME_SIZE-2] = '*';
loopinfo64.lo_file_name[LO_NAME_SIZE-1] = 0;
loopinfo64.lo_crypt_name[LO_NAME_SIZE-1] = 0;
printf("%s: [%04llx]:%llu (%s)",
device, loopinfo64.lo_device, loopinfo64.lo_inode,
loopinfo64.lo_file_name);
if (loopinfo64.lo_offset)
printf(_(", offset %lld"), loopinfo64.lo_offset);
if (loopinfo64.lo_sizelimit)
printf(_(", sizelimit %lld"), loopinfo64.lo_sizelimit);
if (loopinfo64.lo_encrypt_type ||
loopinfo64.lo_crypt_name[0]) {
char *e = loopinfo64.lo_crypt_name;
if (*e == 0 && loopinfo64.lo_encrypt_type == 1)
e = "XOR";
printf(_(", encryption %s (type %d)"),
e, loopinfo64.lo_encrypt_type);
}
printf("\n");
close (fd);
return 0;
}
if (ioctl(fd, LOOP_GET_STATUS, &loopinfo) == 0) {
printf ("%s: [%04x]:%ld (%s)",
device, loopinfo.lo_device, loopinfo.lo_inode,
loopinfo.lo_name);
if (loopinfo.lo_offset)
printf(_(", offset %d"), loopinfo.lo_offset);
if (loopinfo.lo_encrypt_type)
printf(_(", encryption type %d\n"),
loopinfo.lo_encrypt_type);
printf("\n");
close (fd);
return 0;
}
errsv = errno;
fprintf(stderr, _("loop: can't get info on device %s: %s\n"),
device, strerror (errsv));
close (fd);
return 1;
}
#endif
int
is_loop_device (const char *device) {
struct stat statbuf;
return (stat(device, &statbuf) == 0 &&
S_ISBLK(statbuf.st_mode) &&
major(statbuf.st_rdev) == LOOPMAJOR);
}
#define SIZE(a) (sizeof(a)/sizeof(a[0]))
char *
find_unused_loop_device (void) {
/* Just creating a device, say in /tmp, is probably a bad idea -
people might have problems with backup or so.
So, we just try /dev/loop[0-7]. */
char dev[20];
char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
int i, j, fd, somedev = 0, someloop = 0;
struct stat statbuf;
struct loop_info loopinfo;
for (j = 0; j < SIZE(loop_formats); j++) {
for(i = 0; i < 256; i++) {
sprintf(dev, loop_formats[j], i);
if (stat (dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) {
somedev++;
fd = open (dev, O_RDONLY);
if (fd >= 0) {
if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0)
someloop++; /* in use */
else if (errno == ENXIO) {
close (fd);
return xstrdup(dev);/* probably free */
}
close (fd);
}
continue;/* continue trying as long as devices exist */
}
break;
}
}
if (!somedev)
error(_("mount: could not find any device /dev/loop#"));
else if (!someloop) {
error(_(
"mount: Could not find any loop device. Maybe this kernel "
"does not know\n"
" about the loop device? (If so, recompile or "
"`modprobe loop'.)"));
} else
error(_("mount: could not find any free loop device"));
return 0;
}
/*
* A function to read the passphrase either from the terminal or from
* an open file descriptor.
*/
static char *
xgetpass(int pfd, const char *prompt) {
char *pass;
int buflen, i;
if (pfd < 0) /* terminal */
return getpass(prompt);
pass = NULL;
buflen = 0;
for (i=0; ; i++) {
if (i >= buflen-1) {
/* we're running out of space in the buffer.
* Make it bigger: */
char *tmppass = pass;
buflen += 128;
pass = realloc(tmppass, buflen);
if (pass == NULL) {
/* realloc failed. Stop reading. */
error("Out of memory while reading passphrase");
pass = tmppass; /* the old buffer hasn't changed */
break;
}
}
if (read(pfd, pass+i, 1) != 1 || pass[i] == '\n')
break;
}
if (pass == NULL)
return "";
else {
pass[i] = 0;
return pass;
}
}
static int
digits_only(const char *s) {
while (*s)
if (!isdigit(*s++))
return 0;
return 1;
}
int
set_loop(const char *device, const char *file, int offset,
const char *encryption, int pfd, int *loopro) {
struct loop_info64 loopinfo64;
int fd, ffd, mode;
char *pass;
mode = (*loopro ? O_RDONLY : O_RDWR);
if ((ffd = open(file, mode)) < 0) {
if (!*loopro && errno == EROFS)
ffd = open(file, mode = O_RDONLY);
if (ffd < 0) {
perror(file);
return 1;
}
}
if ((fd = open(device, mode)) < 0) {
perror (device);
return 1;
}
*loopro = (mode == O_RDONLY);
memset(&loopinfo64, 0, sizeof(loopinfo64));
xstrncpy(loopinfo64.lo_file_name, file, LO_NAME_SIZE);
if (encryption && *encryption) {
if (digits_only(encryption)) {
loopinfo64.lo_encrypt_type = atoi(encryption);
} else {
loopinfo64.lo_encrypt_type = LO_CRYPT_CRYPTOAPI;
snprintf(loopinfo64.lo_crypt_name, LO_NAME_SIZE,
"%s", encryption);
}
}
loopinfo64.lo_offset = offset;
#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)) {
perror("memlock");
fprintf(stderr, _("Couldn't lock into memory, exiting.\n"));
exit(1);
}
#endif
switch (loopinfo64.lo_encrypt_type) {
case LO_CRYPT_NONE:
loopinfo64.lo_encrypt_key_size = 0;
break;
case LO_CRYPT_XOR:
pass = getpass(_("Password: "));
xstrncpy(loopinfo64.lo_encrypt_key, pass, LO_KEY_SIZE);
loopinfo64.lo_encrypt_key_size =
strlen(loopinfo64.lo_encrypt_key);
break;
default:
pass = xgetpass(pfd, _("Password: "));
xstrncpy(loopinfo64.lo_encrypt_key, pass, LO_KEY_SIZE);
loopinfo64.lo_encrypt_key_size = LO_KEY_SIZE;
}
if (ioctl(fd, LOOP_SET_FD, ffd) < 0) {
perror("ioctl: LOOP_SET_FD");
return 1;
}
close (ffd);
if (ioctl(fd, LOOP_SET_STATUS64, &loopinfo64) < 0) {
struct loop_info loopinfo;
int errsv = errno;
errno = loop_info64_to_old(&loopinfo64, &loopinfo);
if (errno) {
errno = errsv;
perror("ioctl: LOOP_SET_STATUS64");
goto fail;
}
if (ioctl(fd, LOOP_SET_STATUS, &loopinfo) < 0) {
perror("ioctl: LOOP_SET_STATUS");
goto fail;
}
}
close (fd);
if (verbose > 1)
printf(_("set_loop(%s,%s,%d): success\n"),
device, file, offset);
return 0;
fail:
(void) ioctl (fd, LOOP_CLR_FD, 0);
close (fd);
return 1;
}
int
del_loop (const char *device) {
int fd;
if ((fd = open (device, O_RDONLY)) < 0) {
int errsv = errno;
fprintf(stderr, _("loop: can't delete device %s: %s\n"),
device, strerror (errsv));
return 1;
}
if (ioctl (fd, LOOP_CLR_FD, 0) < 0) {
perror ("ioctl: LOOP_CLR_FD");
return 1;
}
close (fd);
if (verbose > 1)
printf(_("del_loop(%s): success\n"), device);
return 0;
}
#else /* no LOOP_SET_FD defined */
static void
mutter(void) {
fprintf(stderr,
_("This mount was compiled without loop support. "
"Please recompile.\n"));
}
int
set_loop (const char *device, const char *file, int offset,
const char *encryption, int *loopro) {
mutter();
return 1;
}
int
del_loop (const char *device) {
mutter();
return 1;
}
char *
find_unused_loop_device (void) {
mutter();
return 0;
}
#endif
#ifdef MAIN
#ifdef LOOP_SET_FD
#include <getopt.h>
#include <stdarg.h>
int verbose = 0;
static char *progname;
static void
usage(void) {
fprintf(stderr, _("usage:\n\
%s loop_device # give info\n\
%s -d loop_device # delete\n\
%s [ -e encryption ] [ -o offset ] loop_device file # setup\n"),
progname, progname, progname);
exit(1);
}
char *
xstrdup (const char *s) {
char *t;
if (s == NULL)
return NULL;
t = strdup (s);
if (t == NULL) {
fprintf(stderr, _("not enough memory"));
exit(1);
}
return t;
}
void
error (const char *fmt, ...) {
va_list args;
va_start (args, fmt);
vfprintf (stderr, fmt, args);
va_end (args);
fprintf (stderr, "\n");
}
int
main(int argc, char **argv) {
char *offset, *encryption, *passfd;
int delete, off, c;
int res = 0;
int ro = 0;
int pfd = -1;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
delete = off = 0;
offset = encryption = passfd = NULL;
progname = argv[0];
while ((c = getopt(argc,argv,"de:E:o:p:v")) != -1) {
switch (c) {
case 'd':
delete = 1;
break;
case 'E':
case 'e':
encryption = optarg;
break;
case 'o':
offset = optarg;
break;
case 'p':
passfd = optarg;
break;
case 'v':
verbose = 1;
break;
default:
usage();
}
}
if (argc == 1) usage();
if ((delete && (argc != optind+1 || encryption || offset)) ||
(!delete && (argc < optind+1 || argc > optind+2)))
usage();
if (argc == optind+1) {
if (delete)
res = del_loop(argv[optind]);
else
res = show_loop(argv[optind]);
} else {
if (offset && sscanf(offset,"%d",&off) != 1)
usage();
if (passfd && sscanf(passfd,"%d",&pfd) != 1)
usage();
res = set_loop(argv[optind], argv[optind+1], off,
encryption, pfd, &ro);
}
return res;
}
#else /* LOOP_SET_FD not defined */
int
main(int argc, char **argv) {
fprintf(stderr,
_("No loop support was available at compile time. "
"Please recompile.\n"));
return -1;
}
#endif
#endif