toybox/toys/other/losetup.c
<<
>>
Prefs
   1/* losetup.c - Loopback setup
   2 *
   3 * Copyright 2012 Rob Landley <rob@landley.net>
   4 *
   5 * No standard. (Sigh.)
   6
   7USE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdcaD[!afj]", TOYFLAG_SBIN))
   8
   9config LOSETUP
  10  bool "losetup"
  11  default y
  12  help
  13    usage: losetup [-cdrs] [-o OFFSET] [-S SIZE] {-d DEVICE...|-j FILE|-af|{DEVICE FILE}}
  14
  15    Associate a loopback device with a file, or show current file (if any)
  16    associated with a loop device.
  17
  18    Instead of a device:
  19    -a  Iterate through all loopback devices
  20    -f  Find first unused loop device (may create one)
  21    -j FILE     Iterate through all loopback devices associated with FILE
  22
  23    existing:
  24    -c  Check capacity (file size changed)
  25    -d DEV      Detach loopback device
  26    -D  Detach all loopback devices
  27
  28    new:
  29    -s  Show device name (alias --show)
  30    -o OFF      Start association at offset OFF into FILE
  31    -r  Read only
  32    -S SIZE     Limit SIZE of loopback association (alias --sizelimit)
  33*/
  34
  35#define FOR_losetup
  36#include "toys.h"
  37#include <linux/loop.h>
  38
  39GLOBALS(
  40  char *j;
  41  long o, S;
  42
  43  int openflags;
  44  dev_t jdev;
  45  ino_t jino;
  46  char *dir;
  47)
  48
  49// -f: *device is NULL
  50
  51// Perform requested operation on one device. Returns 1 if handled, 0 if error
  52static int loopback_setup(char *device, char *file)
  53{
  54  struct loop_info64 *loop = (void *)(toybuf+32);
  55  int lfd = -1, ffd = -1;
  56  int racy = !device;
  57
  58  // Open file (ffd) and loop device (lfd)
  59
  60  if (file) ffd = xopen(file, TT.openflags);
  61  if (!device) {
  62    int i, cfd = open("/dev/loop-control", O_RDWR);
  63
  64    // We assume /dev is devtmpfs so device creation has no lag. Otherwise
  65    // just preallocate loop devices and stay within them.
  66
  67    // mount -o loop depends on found device being at the start of toybuf.
  68    if (cfd != -1) {
  69      if (0 <= (i = ioctl(cfd, LOOP_CTL_GET_FREE))) {
  70        sprintf(device = toybuf, "%s/loop%d", TT.dir, i);
  71      }
  72      close(cfd);
  73    }
  74  }
  75
  76  if (device) lfd = open(device, TT.openflags);
  77
  78  // Stat the loop device to see if there's a current association.
  79  memset(loop, 0, sizeof(struct loop_info64));
  80  if (-1 == lfd || ioctl(lfd, LOOP_GET_STATUS64, loop)) {
  81    if (errno == ENXIO && (FLAG(a) || FLAG(j))) goto done;
  82    // ENXIO expected if we're just trying to print the first unused device.
  83    if (errno == ENXIO && FLAG(f) && !file) {
  84      puts(device);
  85      goto done;
  86    }
  87    if (errno != ENXIO || !file) {
  88      perror_msg_raw(device ? device : "-f");
  89      goto done;
  90    }
  91  }
  92
  93  // Skip -j filtered devices
  94  if (TT.j && (loop->lo_device != TT.jdev || loop->lo_inode != TT.jino))
  95    goto done;
  96
  97  // Check size of file or delete existing association
  98  if (FLAG(c) || FLAG(d)) {
  99    // The constant is LOOP_SET_CAPACITY
 100    if (ioctl(lfd, FLAG(c) ? 0x4C07 : LOOP_CLR_FD, 0)) {
 101      perror_msg_raw(device);
 102      goto done;
 103    }
 104  // Associate file with this device?
 105  } else if (file) {
 106    char *f_path = xabspath(file, ABS_PATH);
 107
 108    if (!f_path) perror_exit("%s", file); // already opened but if deleted since
 109    if (ioctl(lfd, LOOP_SET_FD, ffd)) {
 110      free(f_path);
 111      if (racy && errno == EBUSY) return 1;
 112      perror_exit("%s=%s", device, file);
 113    }
 114    xstrncpy((char *)loop->lo_file_name, f_path, LO_NAME_SIZE);
 115    free(f_path);
 116    loop->lo_offset = TT.o;
 117    loop->lo_sizelimit = TT.S;
 118    if (ioctl(lfd, LOOP_SET_STATUS64, loop)) perror_exit("%s=%s", device, file);
 119    if (FLAG(s)) puts(device);
 120  }
 121  else {
 122    xprintf("%s: [%lld]:%llu (%s)", device, (long long)loop->lo_device,
 123      (long long)loop->lo_inode, loop->lo_file_name);
 124    if (loop->lo_offset) xprintf(", offset %llu",
 125      (unsigned long long)loop->lo_offset);
 126    if (loop->lo_sizelimit) xprintf(", sizelimit %llu",
 127      (unsigned long long)loop->lo_sizelimit);
 128    xputc('\n');
 129  }
 130
 131done:
 132  xclose(ffd);
 133  xclose(lfd);
 134  return 0;
 135}
 136
 137// Perform an action on all currently existing loop devices
 138static int dash_a(struct dirtree *node)
 139{
 140  char *s = node->name;
 141
 142  // Initial /dev node needs to recurse down one level, then only loop[0-9]*
 143  if (!node->parent) return DIRTREE_RECURSE;
 144  if (strncmp(s, "loop", 4) || !isdigit(s[4])) return 0;
 145
 146  s = dirtree_path(node, 0);
 147  loopback_setup(s, 0);
 148  free(s);
 149
 150  return 0;
 151}
 152
 153void losetup_main(void)
 154{
 155  char **s;
 156
 157  TT.dir = CFG_TOYBOX_ON_ANDROID ? "/dev/block" : "/dev";
 158  TT.openflags = FLAG(r) ? O_RDONLY : O_RDWR;
 159
 160  if (TT.j) {
 161    struct stat st;
 162
 163    xstat(TT.j, &st);
 164    TT.jdev = st.st_dev;
 165    TT.jino = st.st_ino;
 166  }
 167
 168  // With just device, display current association
 169  // -a, -f substitute for device
 170  // -j substitute for device
 171
 172  // new association: S size o offset rs - need a file
 173  // existing association: cd
 174
 175  // -f(dc FILE)
 176
 177  if (FLAG(D)) toys.optflags |= FLAG_a | FLAG_d;
 178
 179  if (FLAG(f)) {
 180    if (toys.optc > 1) perror_exit("max 1 arg");
 181    while (loopback_setup(NULL, *toys.optargs));
 182  } else if (FLAG(a) || FLAG(j)) {
 183    if (toys.optc) error_exit("bad args");
 184    dirtree_read(TT.dir, dash_a);
 185  // Do we need one DEVICE argument?
 186  } else {
 187    char *file = (FLAG(c) || FLAG(d)) ? NULL : toys.optargs[1];
 188
 189    if (!toys.optc || (file && toys.optc != 2))
 190      help_exit("needs %d arg%s", 1+!!file, file ? "s" : "");
 191    for (s = toys.optargs; *s; s++) {
 192      loopback_setup(*s, file);
 193      if (file) break;
 194    }
 195  }
 196}
 197