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:fdca[!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  Iterate through all loopback devices associated with FILE
  22
  23    existing:
  24    -c  Check capacity (file size changed)
  25    -d  Detach loopback device
  26
  27    new:
  28    -s  Show device name (alias --show)
  29    -o  Start assocation at OFFSET into FILE
  30    -r  Read only
  31    -S  Limit SIZE of loopback association (alias --sizelimit)
  32*/
  33
  34#define FOR_losetup
  35#include "toys.h"
  36#include <linux/loop.h>
  37
  38GLOBALS(
  39  char *jfile;
  40  long offset;
  41  long size;
  42
  43  int openflags;
  44  dev_t jdev;
  45  ino_t jino;
  46)
  47
  48// -f: *device is NULL
  49
  50// Perform requested operation on one device. Returns 1 if handled, 0 if error
  51static void loopback_setup(char *device, char *file)
  52{
  53  struct loop_info64 *loop = (void *)(toybuf+32);
  54  int lfd = -1, ffd = ffd;
  55  unsigned flags = toys.optflags;
  56
  57  // Open file (ffd) and loop device (lfd)
  58
  59  if (file) ffd = xopen(file, TT.openflags);
  60  if (!device) {
  61    int i, cfd = open("/dev/loop-control", O_RDWR);
  62
  63    // We assume /dev is devtmpfs so device creation has no lag. Otherwise
  64    // just preallocate loop devices and stay within them.
  65
  66    // mount -o loop depends on found device being at the start of toybuf.
  67    if (cfd != -1) {
  68      if (0 <= (i = ioctl(cfd, 0x4C82))) { // LOOP_CTL_GET_FREE
  69        sprintf(device = toybuf, "/dev/loop%d", i);
  70        // Fallback for Android
  71        if (access(toybuf, F_OK)) sprintf(toybuf, "/dev/block/loop%d", i);
  72      }
  73      close(cfd);
  74    }
  75  }
  76
  77  if (device) lfd = open(device, TT.openflags);
  78
  79  // Stat the loop device to see if there's a current association.
  80  memset(loop, 0, sizeof(struct loop_info64));
  81  if (-1 == lfd || ioctl(lfd, LOOP_GET_STATUS64, loop)) {
  82    if (errno == ENXIO && (flags & (FLAG_a|FLAG_j))) goto done;
  83    if (errno != ENXIO || !file) {
  84      perror_msg_raw(device ? device : "-f");
  85      goto done;
  86    }
  87  }
  88
  89  // Skip -j filtered devices
  90  if (TT.jfile && (loop->lo_device != TT.jdev || loop->lo_inode != TT.jino))
  91    goto done;
  92
  93  // Check size of file or delete existing association
  94  if (flags & (FLAG_c|FLAG_d)) {
  95    // The constant is LOOP_SET_CAPACITY
  96    if (ioctl(lfd, (flags & FLAG_c) ? 0x4C07 : LOOP_CLR_FD, 0)) {
  97      perror_msg_raw(device);
  98      goto done;
  99    }
 100  // Associate file with this device?
 101  } else if (file) {
 102    char *s = xabspath(file, 1);
 103
 104    if (!s) perror_exit("file"); // already opened, but if deleted since...
 105    if (ioctl(lfd, LOOP_SET_FD, ffd)) perror_exit("%s=%s", device, file);
 106    loop->lo_offset = TT.offset;
 107    loop->lo_sizelimit = TT.size;
 108    xstrncpy((char *)loop->lo_file_name, s, LO_NAME_SIZE);
 109    s[LO_NAME_SIZE-1] = 0;
 110    if (ioctl(lfd, LOOP_SET_STATUS64, loop)) perror_exit("%s=%s", device, file);
 111    if (flags & FLAG_s) printf("%s", device);
 112    free(s);
 113  } else if (flags & FLAG_f) printf("%s", device);
 114  else {
 115    xprintf("%s: [%04llx]:%llu (%s)", device, (long long)loop->lo_device,
 116      (long long)loop->lo_inode, loop->lo_file_name);
 117    if (loop->lo_offset) xprintf(", offset %llu", loop->lo_offset);
 118    if (loop->lo_sizelimit) xprintf(", sizelimit %llu", loop->lo_sizelimit);
 119    xputc('\n');
 120  }
 121
 122done:
 123  if (file) close(ffd);
 124  if (lfd != -1) close(lfd);
 125}
 126
 127// Perform an action on all currently existing loop devices
 128static int dash_a(struct dirtree *node)
 129{
 130  char *s = node->name;
 131
 132  // Initial /dev node needs to recurse down one level, then only loop[0-9]*
 133  if (!node->parent) return DIRTREE_RECURSE;
 134  if (strncmp(s, "loop", 4) || !isdigit(s[4])) return 0;
 135
 136  s = dirtree_path(node, 0);
 137  loopback_setup(s, 0);
 138  free(s);
 139
 140  return 0;
 141}
 142
 143void losetup_main(void)
 144{
 145  char **s;
 146
 147  TT.openflags = (toys.optflags & FLAG_r) ? O_RDONLY : O_RDWR;
 148
 149  if (TT.jfile) {
 150    struct stat st;
 151
 152    xstat(TT.jfile, &st);
 153    TT.jdev = st.st_dev;
 154    TT.jino = st.st_ino;
 155  }
 156
 157  // With just device, display current association
 158  // -a, -f substitute for device
 159  // -j substitute for device
 160
 161  // new association: S size o offset rs - need a file
 162  // existing association: cd
 163
 164  // -f(dc FILE)
 165
 166  if (toys.optflags & FLAG_f) {
 167    if (toys.optc > 1) perror_exit("max 1 arg");
 168    loopback_setup(NULL, *toys.optargs);
 169  } else if (toys.optflags & (FLAG_a|FLAG_j)) {
 170    if (toys.optc) error_exit("bad args");
 171    dirtree_read("/dev", dash_a);
 172  // Do we need one DEVICE argument?
 173  } else {
 174    char *file = (toys.optflags & (FLAG_d|FLAG_c)) ? NULL : toys.optargs[1];
 175
 176    if (!toys.optc || (file && toys.optc != 2)) 
 177      help_exit("needs %d arg%s", 1+!!file, file ? "s" : "");
 178    for (s = toys.optargs; *s; s++) {
 179      loopback_setup(*s, file);
 180      if (file) break;
 181    }
 182  }
 183}
 184