mountinfo.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Use pidfds, nsfds, listmount() and statmount() mimic the
  4. * contents of /proc/self/mountinfo.
  5. */
  6. #define _GNU_SOURCE
  7. #define __SANE_USERSPACE_TYPES__
  8. #include <stdio.h>
  9. #include <stdint.h>
  10. #include <unistd.h>
  11. #include <alloca.h>
  12. #include <getopt.h>
  13. #include <stdlib.h>
  14. #include <stdbool.h>
  15. #include <errno.h>
  16. #include "samples-vfs.h"
  17. /* max mounts per listmount call */
  18. #define MAXMOUNTS 1024
  19. /* size of struct statmount (including trailing string buffer) */
  20. #define STATMOUNT_BUFSIZE 4096
  21. static bool ext_format;
  22. #ifndef __NR_pidfd_open
  23. #define __NR_pidfd_open -1
  24. #endif
  25. /*
  26. * There are no bindings in glibc for listmount() and statmount() (yet),
  27. * make our own here.
  28. */
  29. static int statmount(__u64 mnt_id, __u64 mnt_ns_id, __u64 mask,
  30. struct statmount *buf, size_t bufsize,
  31. unsigned int flags)
  32. {
  33. struct mnt_id_req req = {
  34. .size = MNT_ID_REQ_SIZE_VER0,
  35. .mnt_id = mnt_id,
  36. .param = mask,
  37. };
  38. if (mnt_ns_id) {
  39. req.size = MNT_ID_REQ_SIZE_VER1;
  40. req.mnt_ns_id = mnt_ns_id;
  41. }
  42. return syscall(__NR_statmount, &req, buf, bufsize, flags);
  43. }
  44. static ssize_t listmount(__u64 mnt_id, __u64 mnt_ns_id, __u64 last_mnt_id,
  45. __u64 list[], size_t num, unsigned int flags)
  46. {
  47. struct mnt_id_req req = {
  48. .size = MNT_ID_REQ_SIZE_VER0,
  49. .mnt_id = mnt_id,
  50. .param = last_mnt_id,
  51. };
  52. if (mnt_ns_id) {
  53. req.size = MNT_ID_REQ_SIZE_VER1;
  54. req.mnt_ns_id = mnt_ns_id;
  55. }
  56. return syscall(__NR_listmount, &req, list, num, flags);
  57. }
  58. static void show_mnt_attrs(__u64 flags)
  59. {
  60. printf("%s", flags & MOUNT_ATTR_RDONLY ? "ro" : "rw");
  61. if (flags & MOUNT_ATTR_NOSUID)
  62. printf(",nosuid");
  63. if (flags & MOUNT_ATTR_NODEV)
  64. printf(",nodev");
  65. if (flags & MOUNT_ATTR_NOEXEC)
  66. printf(",noexec");
  67. switch (flags & MOUNT_ATTR__ATIME) {
  68. case MOUNT_ATTR_RELATIME:
  69. printf(",relatime");
  70. break;
  71. case MOUNT_ATTR_NOATIME:
  72. printf(",noatime");
  73. break;
  74. case MOUNT_ATTR_STRICTATIME:
  75. /* print nothing */
  76. break;
  77. }
  78. if (flags & MOUNT_ATTR_NODIRATIME)
  79. printf(",nodiratime");
  80. if (flags & MOUNT_ATTR_NOSYMFOLLOW)
  81. printf(",nosymfollow");
  82. if (flags & MOUNT_ATTR_IDMAP)
  83. printf(",idmapped");
  84. }
  85. static void show_propagation(struct statmount *sm)
  86. {
  87. if (sm->mnt_propagation & MS_SHARED)
  88. printf(" shared:%llu", sm->mnt_peer_group);
  89. if (sm->mnt_propagation & MS_SLAVE) {
  90. printf(" master:%llu", sm->mnt_master);
  91. if (sm->propagate_from && sm->propagate_from != sm->mnt_master)
  92. printf(" propagate_from:%llu", sm->propagate_from);
  93. }
  94. if (sm->mnt_propagation & MS_UNBINDABLE)
  95. printf(" unbindable");
  96. }
  97. static void show_sb_flags(__u64 flags)
  98. {
  99. printf("%s", flags & MS_RDONLY ? "ro" : "rw");
  100. if (flags & MS_SYNCHRONOUS)
  101. printf(",sync");
  102. if (flags & MS_DIRSYNC)
  103. printf(",dirsync");
  104. if (flags & MS_MANDLOCK)
  105. printf(",mand");
  106. if (flags & MS_LAZYTIME)
  107. printf(",lazytime");
  108. }
  109. static int dump_mountinfo(__u64 mnt_id, __u64 mnt_ns_id)
  110. {
  111. int ret;
  112. struct statmount *buf = alloca(STATMOUNT_BUFSIZE);
  113. const __u64 mask = STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC |
  114. STATMOUNT_PROPAGATE_FROM | STATMOUNT_FS_TYPE |
  115. STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT |
  116. STATMOUNT_MNT_OPTS | STATMOUNT_FS_SUBTYPE |
  117. STATMOUNT_SB_SOURCE;
  118. ret = statmount(mnt_id, mnt_ns_id, mask, buf, STATMOUNT_BUFSIZE, 0);
  119. if (ret < 0) {
  120. perror("statmount");
  121. return 1;
  122. }
  123. if (ext_format)
  124. printf("0x%llx 0x%llx 0x%llx ", mnt_ns_id, mnt_id, buf->mnt_parent_id);
  125. printf("%u %u %u:%u %s %s ", buf->mnt_id_old, buf->mnt_parent_id_old,
  126. buf->sb_dev_major, buf->sb_dev_minor,
  127. &buf->str[buf->mnt_root],
  128. &buf->str[buf->mnt_point]);
  129. show_mnt_attrs(buf->mnt_attr);
  130. show_propagation(buf);
  131. printf(" - %s", &buf->str[buf->fs_type]);
  132. if (buf->mask & STATMOUNT_FS_SUBTYPE)
  133. printf(".%s", &buf->str[buf->fs_subtype]);
  134. if (buf->mask & STATMOUNT_SB_SOURCE)
  135. printf(" %s ", &buf->str[buf->sb_source]);
  136. else
  137. printf(" :none ");
  138. show_sb_flags(buf->sb_flags);
  139. if (buf->mask & STATMOUNT_MNT_OPTS)
  140. printf(",%s", &buf->str[buf->mnt_opts]);
  141. printf("\n");
  142. return 0;
  143. }
  144. static int dump_mounts(__u64 mnt_ns_id)
  145. {
  146. __u64 mntid[MAXMOUNTS];
  147. __u64 last_mnt_id = 0;
  148. ssize_t count;
  149. int i;
  150. /*
  151. * Get a list of all mntids in mnt_ns_id. If it returns MAXMOUNTS
  152. * mounts, then go again until we get everything.
  153. */
  154. do {
  155. count = listmount(LSMT_ROOT, mnt_ns_id, last_mnt_id, mntid, MAXMOUNTS, 0);
  156. if (count < 0 || count > MAXMOUNTS) {
  157. errno = count < 0 ? errno : count;
  158. perror("listmount");
  159. return 1;
  160. }
  161. /* Walk the returned mntids and print info about each */
  162. for (i = 0; i < count; ++i) {
  163. int ret = dump_mountinfo(mntid[i], mnt_ns_id);
  164. if (ret != 0)
  165. return ret;
  166. }
  167. /* Set up last_mnt_id to pick up where we left off */
  168. last_mnt_id = mntid[count - 1];
  169. } while (count == MAXMOUNTS);
  170. return 0;
  171. }
  172. static void usage(const char * const prog)
  173. {
  174. printf("Usage:\n");
  175. printf("%s [-e] [-p pid] [-r] [-h]\n", prog);
  176. printf(" -e: extended format\n");
  177. printf(" -h: print usage message\n");
  178. printf(" -p: get mount namespace from given pid\n");
  179. printf(" -r: recursively print all mounts in all child namespaces\n");
  180. }
  181. int main(int argc, char * const *argv)
  182. {
  183. struct mnt_ns_info mni = { .size = MNT_NS_INFO_SIZE_VER0 };
  184. int pidfd, mntns, ret, opt;
  185. pid_t pid = getpid();
  186. bool recursive = false;
  187. while ((opt = getopt(argc, argv, "ehp:r")) != -1) {
  188. switch (opt) {
  189. case 'e':
  190. ext_format = true;
  191. break;
  192. case 'h':
  193. usage(argv[0]);
  194. return 0;
  195. case 'p':
  196. pid = atoi(optarg);
  197. break;
  198. case 'r':
  199. recursive = true;
  200. break;
  201. }
  202. }
  203. /* Get a pidfd for pid */
  204. pidfd = syscall(__NR_pidfd_open, pid, 0);
  205. if (pidfd < 0) {
  206. perror("pidfd_open");
  207. return 1;
  208. }
  209. /* Get the mnt namespace for pidfd */
  210. mntns = ioctl(pidfd, PIDFD_GET_MNT_NAMESPACE, NULL);
  211. if (mntns < 0) {
  212. perror("PIDFD_GET_MNT_NAMESPACE");
  213. return 1;
  214. }
  215. close(pidfd);
  216. /* get info about mntns. In particular, the mnt_ns_id */
  217. ret = ioctl(mntns, NS_MNT_GET_INFO, &mni);
  218. if (ret < 0) {
  219. perror("NS_MNT_GET_INFO");
  220. return 1;
  221. }
  222. do {
  223. int ret;
  224. ret = dump_mounts(mni.mnt_ns_id);
  225. if (ret)
  226. return ret;
  227. if (!recursive)
  228. break;
  229. /* get the next mntns (and overwrite the old mount ns info) */
  230. ret = ioctl(mntns, NS_MNT_GET_NEXT, &mni);
  231. close(mntns);
  232. mntns = ret;
  233. } while (mntns >= 0);
  234. return 0;
  235. }