losetup.c 5.2 KB

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