mount.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. /*
  2. * mount.c, by rmk
  3. */
  4. #include <sys/mount.h>
  5. #include <sys/stat.h>
  6. #include <fcntl.h>
  7. #include <errno.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <unistd.h>
  12. #include <linux/loop.h>
  13. #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
  14. #define DEFAULT_LOOP_DEVICE "/dev/block/loop0"
  15. #define LOOPDEV_MAXLEN 64
  16. struct mount_opts {
  17. const char str[16];
  18. unsigned long rwmask;
  19. unsigned long rwset;
  20. unsigned long rwnoset;
  21. };
  22. struct extra_opts {
  23. char *str;
  24. char *end;
  25. int used_size;
  26. int alloc_size;
  27. };
  28. /*
  29. * These options define the function of "mount(2)".
  30. */
  31. #define MS_TYPE (MS_REMOUNT|MS_BIND|MS_MOVE)
  32. static const struct mount_opts options[] = {
  33. /* name mask set noset */
  34. { "async", MS_SYNCHRONOUS, 0, MS_SYNCHRONOUS },
  35. { "atime", MS_NOATIME, 0, MS_NOATIME },
  36. { "bind", MS_TYPE, MS_BIND, 0, },
  37. { "dev", MS_NODEV, 0, MS_NODEV },
  38. { "diratime", MS_NODIRATIME, 0, MS_NODIRATIME },
  39. { "dirsync", MS_DIRSYNC, MS_DIRSYNC, 0 },
  40. { "exec", MS_NOEXEC, 0, MS_NOEXEC },
  41. { "move", MS_TYPE, MS_MOVE, 0 },
  42. { "recurse", MS_REC, MS_REC, 0 },
  43. { "rec", MS_REC, MS_REC, 0 },
  44. { "remount", MS_TYPE, MS_REMOUNT, 0 },
  45. { "ro", MS_RDONLY, MS_RDONLY, 0 },
  46. { "rw", MS_RDONLY, 0, MS_RDONLY },
  47. { "suid", MS_NOSUID, 0, MS_NOSUID },
  48. { "sync", MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0 },
  49. { "verbose", MS_SILENT, MS_SILENT, 0 },
  50. { "unbindable", MS_UNBINDABLE, MS_UNBINDABLE, 0 },
  51. { "private", MS_PRIVATE, MS_PRIVATE, 0 },
  52. { "slave", MS_SLAVE, MS_SLAVE, 0 },
  53. { "shared", MS_SHARED, MS_SHARED, 0 },
  54. };
  55. static void add_extra_option(struct extra_opts *extra, char *s)
  56. {
  57. int len = strlen(s);
  58. int newlen;
  59. if (extra->str)
  60. len++; /* +1 for ',' */
  61. newlen = extra->used_size + len;
  62. if (newlen >= extra->alloc_size) {
  63. char *new;
  64. new = realloc(extra->str, newlen + 1); /* +1 for NUL */
  65. if (!new)
  66. return;
  67. extra->str = new;
  68. extra->end = extra->str + extra->used_size;
  69. extra->alloc_size = newlen + 1;
  70. }
  71. if (extra->used_size) {
  72. *extra->end = ',';
  73. extra->end++;
  74. }
  75. strcpy(extra->end, s);
  76. extra->used_size += len;
  77. }
  78. static unsigned long
  79. parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop, char *loopdev)
  80. {
  81. char *s;
  82. *loop = 0;
  83. while ((s = strsep(&arg, ",")) != NULL) {
  84. char *opt = s;
  85. unsigned int i;
  86. int res, no = s[0] == 'n' && s[1] == 'o';
  87. if (no)
  88. s += 2;
  89. if (strncmp(s, "loop=", 5) == 0) {
  90. *loop = 1;
  91. strlcpy(loopdev, s + 5, LOOPDEV_MAXLEN);
  92. continue;
  93. }
  94. if (strcmp(s, "loop") == 0) {
  95. *loop = 1;
  96. strlcpy(loopdev, DEFAULT_LOOP_DEVICE, LOOPDEV_MAXLEN);
  97. continue;
  98. }
  99. for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) {
  100. res = strcmp(s, options[i].str);
  101. if (res == 0) {
  102. rwflag &= ~options[i].rwmask;
  103. if (no)
  104. rwflag |= options[i].rwnoset;
  105. else
  106. rwflag |= options[i].rwset;
  107. }
  108. if (res <= 0)
  109. break;
  110. }
  111. if (res != 0 && s[0])
  112. add_extra_option(extra, opt);
  113. }
  114. return rwflag;
  115. }
  116. /*
  117. * Mark the given block device as read-write, using the BLKROSET ioctl.
  118. */
  119. static void fs_set_blk_rw(const char *blockdev)
  120. {
  121. int fd;
  122. int OFF = 0;
  123. fd = open(blockdev, O_RDONLY);
  124. if (fd < 0) {
  125. // should never happen
  126. return;
  127. }
  128. ioctl(fd, BLKROSET, &OFF);
  129. close(fd);
  130. }
  131. static char *progname;
  132. static struct extra_opts extra;
  133. static unsigned long rwflag;
  134. static int
  135. do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop,
  136. char *loopdev)
  137. {
  138. char *s;
  139. int error = 0;
  140. if (loop) {
  141. int file_fd, device_fd;
  142. int flags;
  143. flags = (rwflag & MS_RDONLY) ? O_RDONLY : O_RDWR;
  144. file_fd = open(dev, flags);
  145. if (file_fd < 0) {
  146. perror("open backing file failed");
  147. return 1;
  148. }
  149. device_fd = open(loopdev, flags);
  150. if (device_fd < 0) {
  151. perror("open loop device failed");
  152. close(file_fd);
  153. return 1;
  154. }
  155. if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) {
  156. perror("ioctl LOOP_SET_FD failed");
  157. close(file_fd);
  158. close(device_fd);
  159. return 1;
  160. }
  161. close(file_fd);
  162. close(device_fd);
  163. dev = loopdev;
  164. }
  165. if ((rwflag & MS_RDONLY) == 0) {
  166. fs_set_blk_rw(dev);
  167. }
  168. while ((s = strsep(&type, ",")) != NULL) {
  169. retry:
  170. if (mount(dev, dir, s, rwflag, data) == -1) {
  171. error = errno;
  172. /*
  173. * If the filesystem is not found, or the
  174. * superblock is invalid, try the next.
  175. */
  176. if (error == ENODEV || error == EINVAL)
  177. continue;
  178. /*
  179. * If we get EACCESS, and we're trying to
  180. * mount readwrite and this isn't a remount,
  181. * try read only.
  182. */
  183. if (error == EACCES &&
  184. (rwflag & (MS_REMOUNT|MS_RDONLY)) == 0) {
  185. rwflag |= MS_RDONLY;
  186. goto retry;
  187. }
  188. break;
  189. }
  190. }
  191. if (error) {
  192. errno = error;
  193. perror("mount");
  194. return 255;
  195. }
  196. return 0;
  197. }
  198. static int print_mounts()
  199. {
  200. FILE* f;
  201. int length;
  202. char buffer[100];
  203. f = fopen("/proc/mounts", "r");
  204. if (!f) {
  205. fprintf(stdout, "could not open /proc/mounts\n");
  206. return -1;
  207. }
  208. do {
  209. length = fread(buffer, 1, 100, f);
  210. if (length > 0)
  211. fwrite(buffer, 1, length, stdout);
  212. } while (length > 0);
  213. fclose(f);
  214. return 0;
  215. }
  216. static int get_mounts_dev_dir(const char *arg, char **dev, char **dir)
  217. {
  218. FILE *f;
  219. char mount_dev[256];
  220. char mount_dir[256];
  221. char mount_type[256];
  222. char mount_opts[256];
  223. int mount_freq;
  224. int mount_passno;
  225. int match;
  226. f = fopen("/proc/mounts", "r");
  227. if (!f) {
  228. fprintf(stdout, "could not open /proc/mounts\n");
  229. return -1;
  230. }
  231. do {
  232. match = fscanf(f, "%255s %255s %255s %255s %d %d\n",
  233. mount_dev, mount_dir, mount_type,
  234. mount_opts, &mount_freq, &mount_passno);
  235. mount_dev[255] = 0;
  236. mount_dir[255] = 0;
  237. mount_type[255] = 0;
  238. mount_opts[255] = 0;
  239. if (match == 6 &&
  240. (strcmp(arg, mount_dev) == 0 ||
  241. strcmp(arg, mount_dir) == 0)) {
  242. *dev = strdup(mount_dev);
  243. *dir = strdup(mount_dir);
  244. fclose(f);
  245. return 0;
  246. }
  247. } while (match != EOF);
  248. fclose(f);
  249. return -1;
  250. }
  251. int main(int argc, char *argv[])
  252. {
  253. char *type = NULL;
  254. char *dev = NULL;
  255. char *dir = NULL;
  256. int c;
  257. int loop = 0;
  258. char loopdev[LOOPDEV_MAXLEN];
  259. progname = argv[0];
  260. rwflag = MS_SILENT;
  261. // mount with no arguments is equivalent to "cat /proc/mounts"
  262. if (argc == 1) return print_mounts();
  263. do {
  264. c = getopt(argc, argv, "o:rt:w");
  265. if (c == EOF)
  266. break;
  267. switch (c) {
  268. case 'o':
  269. rwflag = parse_mount_options(optarg, rwflag, &extra, &loop, loopdev);
  270. break;
  271. case 'r':
  272. rwflag |= MS_RDONLY;
  273. break;
  274. case 't':
  275. type = optarg;
  276. break;
  277. case 'w':
  278. rwflag &= ~MS_RDONLY;
  279. break;
  280. case '?':
  281. fprintf(stderr, "%s: invalid option -%c\n",
  282. progname, optopt);
  283. exit(1);
  284. }
  285. } while (1);
  286. /*
  287. * If remount, bind or move was specified, then we don't
  288. * have a "type" as such. Use the dummy "none" type.
  289. */
  290. if (rwflag & MS_TYPE)
  291. type = "none";
  292. if (optind + 2 == argc) {
  293. dev = argv[optind];
  294. dir = argv[optind + 1];
  295. } else if (optind + 1 == argc && rwflag & MS_REMOUNT) {
  296. get_mounts_dev_dir(argv[optind], &dev, &dir);
  297. }
  298. if (dev == NULL || dir == NULL || type == NULL) {
  299. fprintf(stderr, "Usage: %s [-r] [-w] [-o options] [-t type] "
  300. "device directory\n", progname);
  301. exit(1);
  302. }
  303. return do_mount(dev, dir, type, rwflag, extra.str, loop, loopdev);
  304. /* We leak dev and dir in some cases, but we're about to exit */
  305. }