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