10f66f451Sopenharmony_ci/* losetup.c - Loopback setup
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2012 Rob Landley <rob@landley.net>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * No standard. (Sigh.)
60f66f451Sopenharmony_ci
70f66f451Sopenharmony_ciUSE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdcaD[!afj]", TOYFLAG_SBIN))
80f66f451Sopenharmony_ci
90f66f451Sopenharmony_ciconfig LOSETUP
100f66f451Sopenharmony_ci  bool "losetup"
110f66f451Sopenharmony_ci  default y
120f66f451Sopenharmony_ci  help
130f66f451Sopenharmony_ci    usage: losetup [-cdrs] [-o OFFSET] [-S SIZE] {-d DEVICE...|-j FILE|-af|{DEVICE FILE}}
140f66f451Sopenharmony_ci
150f66f451Sopenharmony_ci    Associate a loopback device with a file, or show current file (if any)
160f66f451Sopenharmony_ci    associated with a loop device.
170f66f451Sopenharmony_ci
180f66f451Sopenharmony_ci    Instead of a device:
190f66f451Sopenharmony_ci    -a	Iterate through all loopback devices
200f66f451Sopenharmony_ci    -f	Find first unused loop device (may create one)
210f66f451Sopenharmony_ci    -j FILE	Iterate through all loopback devices associated with FILE
220f66f451Sopenharmony_ci
230f66f451Sopenharmony_ci    existing:
240f66f451Sopenharmony_ci    -c	Check capacity (file size changed)
250f66f451Sopenharmony_ci    -d DEV	Detach loopback device
260f66f451Sopenharmony_ci    -D	Detach all loopback devices
270f66f451Sopenharmony_ci
280f66f451Sopenharmony_ci    new:
290f66f451Sopenharmony_ci    -s	Show device name (alias --show)
300f66f451Sopenharmony_ci    -o OFF	Start association at offset OFF into FILE
310f66f451Sopenharmony_ci    -r	Read only
320f66f451Sopenharmony_ci    -S SIZE	Limit SIZE of loopback association (alias --sizelimit)
330f66f451Sopenharmony_ci*/
340f66f451Sopenharmony_ci
350f66f451Sopenharmony_ci#define FOR_losetup
360f66f451Sopenharmony_ci#include "toys.h"
370f66f451Sopenharmony_ci#include <linux/loop.h>
380f66f451Sopenharmony_ci
390f66f451Sopenharmony_ciGLOBALS(
400f66f451Sopenharmony_ci  char *j;
410f66f451Sopenharmony_ci  long o, S;
420f66f451Sopenharmony_ci
430f66f451Sopenharmony_ci  int openflags;
440f66f451Sopenharmony_ci  dev_t jdev;
450f66f451Sopenharmony_ci  ino_t jino;
460f66f451Sopenharmony_ci  char *dir;
470f66f451Sopenharmony_ci)
480f66f451Sopenharmony_ci
490f66f451Sopenharmony_ci// -f: *device is NULL
500f66f451Sopenharmony_ci
510f66f451Sopenharmony_ci// Perform requested operation on one device. Returns 1 if handled, 0 if error
520f66f451Sopenharmony_cistatic int loopback_setup(char *device, char *file)
530f66f451Sopenharmony_ci{
540f66f451Sopenharmony_ci  struct loop_info64 *loop = (void *)(toybuf+32);
550f66f451Sopenharmony_ci  int lfd = -1, ffd = -1;
560f66f451Sopenharmony_ci  int racy = !device;
570f66f451Sopenharmony_ci
580f66f451Sopenharmony_ci  // Open file (ffd) and loop device (lfd)
590f66f451Sopenharmony_ci
600f66f451Sopenharmony_ci  if (file) ffd = xopen(file, TT.openflags);
610f66f451Sopenharmony_ci  if (!device) {
620f66f451Sopenharmony_ci    int i, cfd = open("/dev/loop-control", O_RDWR);
630f66f451Sopenharmony_ci
640f66f451Sopenharmony_ci    // We assume /dev is devtmpfs so device creation has no lag. Otherwise
650f66f451Sopenharmony_ci    // just preallocate loop devices and stay within them.
660f66f451Sopenharmony_ci
670f66f451Sopenharmony_ci    // mount -o loop depends on found device being at the start of toybuf.
680f66f451Sopenharmony_ci    if (cfd != -1) {
690f66f451Sopenharmony_ci      if (0 <= (i = ioctl(cfd, LOOP_CTL_GET_FREE))) {
700f66f451Sopenharmony_ci        sprintf(device = toybuf, "%s/loop%d", TT.dir, i);
710f66f451Sopenharmony_ci      }
720f66f451Sopenharmony_ci      close(cfd);
730f66f451Sopenharmony_ci    }
740f66f451Sopenharmony_ci  }
750f66f451Sopenharmony_ci
760f66f451Sopenharmony_ci  if (device) lfd = open(device, TT.openflags);
770f66f451Sopenharmony_ci
780f66f451Sopenharmony_ci  // Stat the loop device to see if there's a current association.
790f66f451Sopenharmony_ci  memset(loop, 0, sizeof(struct loop_info64));
800f66f451Sopenharmony_ci  if (-1 == lfd || ioctl(lfd, LOOP_GET_STATUS64, loop)) {
810f66f451Sopenharmony_ci    if (errno == ENXIO && (FLAG(a) || FLAG(j))) goto done;
820f66f451Sopenharmony_ci    // ENXIO expected if we're just trying to print the first unused device.
830f66f451Sopenharmony_ci    if (errno == ENXIO && FLAG(f) && !file) {
840f66f451Sopenharmony_ci      puts(device);
850f66f451Sopenharmony_ci      goto done;
860f66f451Sopenharmony_ci    }
870f66f451Sopenharmony_ci    if (errno != ENXIO || !file) {
880f66f451Sopenharmony_ci      perror_msg_raw(device ? device : "-f");
890f66f451Sopenharmony_ci      goto done;
900f66f451Sopenharmony_ci    }
910f66f451Sopenharmony_ci  }
920f66f451Sopenharmony_ci
930f66f451Sopenharmony_ci  // Skip -j filtered devices
940f66f451Sopenharmony_ci  if (TT.j && (loop->lo_device != TT.jdev || loop->lo_inode != TT.jino))
950f66f451Sopenharmony_ci    goto done;
960f66f451Sopenharmony_ci
970f66f451Sopenharmony_ci  // Check size of file or delete existing association
980f66f451Sopenharmony_ci  if (FLAG(c) || FLAG(d)) {
990f66f451Sopenharmony_ci    // The constant is LOOP_SET_CAPACITY
1000f66f451Sopenharmony_ci    if (ioctl(lfd, FLAG(c) ? 0x4C07 : LOOP_CLR_FD, 0)) {
1010f66f451Sopenharmony_ci      perror_msg_raw(device);
1020f66f451Sopenharmony_ci      goto done;
1030f66f451Sopenharmony_ci    }
1040f66f451Sopenharmony_ci  // Associate file with this device?
1050f66f451Sopenharmony_ci  } else if (file) {
1060f66f451Sopenharmony_ci    char *f_path = xabspath(file, ABS_PATH);
1070f66f451Sopenharmony_ci
1080f66f451Sopenharmony_ci    if (!f_path) perror_exit("%s", file); // already opened but if deleted since
1090f66f451Sopenharmony_ci    if (ioctl(lfd, LOOP_SET_FD, ffd)) {
1100f66f451Sopenharmony_ci      free(f_path);
1110f66f451Sopenharmony_ci      if (racy && errno == EBUSY) return 1;
1120f66f451Sopenharmony_ci      perror_exit("%s=%s", device, file);
1130f66f451Sopenharmony_ci    }
1140f66f451Sopenharmony_ci    xstrncpy((char *)loop->lo_file_name, f_path, LO_NAME_SIZE);
1150f66f451Sopenharmony_ci    free(f_path);
1160f66f451Sopenharmony_ci    loop->lo_offset = TT.o;
1170f66f451Sopenharmony_ci    loop->lo_sizelimit = TT.S;
1180f66f451Sopenharmony_ci    if (ioctl(lfd, LOOP_SET_STATUS64, loop)) perror_exit("%s=%s", device, file);
1190f66f451Sopenharmony_ci    if (FLAG(s)) puts(device);
1200f66f451Sopenharmony_ci  }
1210f66f451Sopenharmony_ci  else {
1220f66f451Sopenharmony_ci    xprintf("%s: [%lld]:%llu (%s)", device, (long long)loop->lo_device,
1230f66f451Sopenharmony_ci      (long long)loop->lo_inode, loop->lo_file_name);
1240f66f451Sopenharmony_ci    if (loop->lo_offset) xprintf(", offset %llu",
1250f66f451Sopenharmony_ci      (unsigned long long)loop->lo_offset);
1260f66f451Sopenharmony_ci    if (loop->lo_sizelimit) xprintf(", sizelimit %llu",
1270f66f451Sopenharmony_ci      (unsigned long long)loop->lo_sizelimit);
1280f66f451Sopenharmony_ci    xputc('\n');
1290f66f451Sopenharmony_ci  }
1300f66f451Sopenharmony_ci
1310f66f451Sopenharmony_cidone:
1320f66f451Sopenharmony_ci  xclose(ffd);
1330f66f451Sopenharmony_ci  xclose(lfd);
1340f66f451Sopenharmony_ci  return 0;
1350f66f451Sopenharmony_ci}
1360f66f451Sopenharmony_ci
1370f66f451Sopenharmony_ci// Perform an action on all currently existing loop devices
1380f66f451Sopenharmony_cistatic int dash_a(struct dirtree *node)
1390f66f451Sopenharmony_ci{
1400f66f451Sopenharmony_ci  char *s = node->name;
1410f66f451Sopenharmony_ci
1420f66f451Sopenharmony_ci  // Initial /dev node needs to recurse down one level, then only loop[0-9]*
1430f66f451Sopenharmony_ci  if (!node->parent) return DIRTREE_RECURSE;
1440f66f451Sopenharmony_ci  if (strncmp(s, "loop", 4) || !isdigit(s[4])) return 0;
1450f66f451Sopenharmony_ci
1460f66f451Sopenharmony_ci  s = dirtree_path(node, 0);
1470f66f451Sopenharmony_ci  loopback_setup(s, 0);
1480f66f451Sopenharmony_ci  free(s);
1490f66f451Sopenharmony_ci
1500f66f451Sopenharmony_ci  return 0;
1510f66f451Sopenharmony_ci}
1520f66f451Sopenharmony_ci
1530f66f451Sopenharmony_civoid losetup_main(void)
1540f66f451Sopenharmony_ci{
1550f66f451Sopenharmony_ci  char **s;
1560f66f451Sopenharmony_ci
1570f66f451Sopenharmony_ci  TT.dir = CFG_TOYBOX_ON_ANDROID ? "/dev/block" : "/dev";
1580f66f451Sopenharmony_ci  TT.openflags = FLAG(r) ? O_RDONLY : O_RDWR;
1590f66f451Sopenharmony_ci
1600f66f451Sopenharmony_ci  if (TT.j) {
1610f66f451Sopenharmony_ci    struct stat st;
1620f66f451Sopenharmony_ci
1630f66f451Sopenharmony_ci    xstat(TT.j, &st);
1640f66f451Sopenharmony_ci    TT.jdev = st.st_dev;
1650f66f451Sopenharmony_ci    TT.jino = st.st_ino;
1660f66f451Sopenharmony_ci  }
1670f66f451Sopenharmony_ci
1680f66f451Sopenharmony_ci  // With just device, display current association
1690f66f451Sopenharmony_ci  // -a, -f substitute for device
1700f66f451Sopenharmony_ci  // -j substitute for device
1710f66f451Sopenharmony_ci
1720f66f451Sopenharmony_ci  // new association: S size o offset rs - need a file
1730f66f451Sopenharmony_ci  // existing association: cd
1740f66f451Sopenharmony_ci
1750f66f451Sopenharmony_ci  // -f(dc FILE)
1760f66f451Sopenharmony_ci
1770f66f451Sopenharmony_ci  if (FLAG(D)) toys.optflags |= FLAG_a | FLAG_d;
1780f66f451Sopenharmony_ci
1790f66f451Sopenharmony_ci  if (FLAG(f)) {
1800f66f451Sopenharmony_ci    if (toys.optc > 1) perror_exit("max 1 arg");
1810f66f451Sopenharmony_ci    while (loopback_setup(NULL, *toys.optargs));
1820f66f451Sopenharmony_ci  } else if (FLAG(a) || FLAG(j)) {
1830f66f451Sopenharmony_ci    if (toys.optc) error_exit("bad args");
1840f66f451Sopenharmony_ci    dirtree_read(TT.dir, dash_a);
1850f66f451Sopenharmony_ci  // Do we need one DEVICE argument?
1860f66f451Sopenharmony_ci  } else {
1870f66f451Sopenharmony_ci    char *file = (FLAG(c) || FLAG(d)) ? NULL : toys.optargs[1];
1880f66f451Sopenharmony_ci
1890f66f451Sopenharmony_ci    if (!toys.optc || (file && toys.optc != 2))
1900f66f451Sopenharmony_ci      help_exit("needs %d arg%s", 1+!!file, file ? "s" : "");
1910f66f451Sopenharmony_ci    for (s = toys.optargs; *s; s++) {
1920f66f451Sopenharmony_ci      loopback_setup(*s, file);
1930f66f451Sopenharmony_ci      if (file) break;
1940f66f451Sopenharmony_ci    }
1950f66f451Sopenharmony_ci  }
1960f66f451Sopenharmony_ci}
197