1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2016 The DragonFly Project 5 * Copyright (c) 2014 The FreeBSD Foundation 6 * All rights reserved. 7 * 8 * This software was developed by Edward Tomasz Napierala under sponsorship 9 * from the FreeBSD Foundation. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 */ 33 34 #include <sys/types.h> 35 #include <sys/mount.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include <vfs/autofs/autofs_mount.h> 41 42 #include "common.h" 43 44 static int 45 unmount_by_statfs(const struct statfs *sb, bool force) 46 { 47 int error, flags; 48 49 log_debugx("unmounting %s", sb->f_mntonname); 50 51 flags = 0; 52 if (force) 53 flags |= MNT_FORCE; 54 error = unmount(sb->f_mntonname, flags); 55 if (error != 0) 56 log_warn("cannot unmount %s", sb->f_mntonname); 57 58 return (error); 59 } 60 61 static const struct statfs * 62 find_statfs(const struct statfs *mntbuf, int nitems, const char *mountpoint) 63 { 64 int i; 65 66 for (i = 0; i < nitems; i++) { 67 if (strcmp(mntbuf[i].f_mntonname, mountpoint) == 0) 68 return (mntbuf + i); 69 } 70 71 return (NULL); 72 } 73 74 static void 75 mount_autofs(const char *from, const char *fspath, const char *options, 76 const char *prefix) 77 { 78 struct autofs_mount_info info; 79 int error; 80 char *cwd; 81 82 /* 83 * There is no guarantee we are at /, so chdir to /. 84 */ 85 cwd = getcwd(NULL, 0); 86 if (chdir("/") != 0) 87 log_warn("failed to chdir to /"); 88 create_directory(fspath); 89 if (chdir(cwd) != 0) 90 log_warn("failed to restore cwd"); 91 free(cwd); 92 93 log_debugx("mounting %s on %s, prefix \"%s\", options \"%s\"", 94 from, fspath, prefix, options); 95 96 memset(&info, 0, sizeof(info)); 97 info.from = from; 98 info.master_options = options; 99 info.master_prefix = prefix; 100 101 error = mount("autofs", fspath, 0, &info); 102 if (error != 0) 103 log_err(1, "cannot mount %s on %s", from, fspath); 104 } 105 106 static void 107 mount_if_not_already(const struct node *n, const char *map, const char *options, 108 const char *prefix, const struct statfs *mntbuf, int nitems) 109 { 110 const struct statfs *sb; 111 char *mountpoint; 112 char *from; 113 int ret; 114 115 ret = asprintf(&from, "map %s", map); 116 if (ret < 0) 117 log_err(1, "asprintf"); 118 119 mountpoint = node_path(n); 120 sb = find_statfs(mntbuf, nitems, mountpoint); 121 if (sb != NULL) { 122 if (strcmp(sb->f_fstypename, "autofs") != 0) { 123 log_debugx("unknown filesystem mounted " 124 "on %s; mounting", mountpoint); 125 /* 126 * XXX: Compare options and 'from', 127 * and update the mount if necessary. 128 */ 129 } else { 130 log_debugx("autofs already mounted " 131 "on %s", mountpoint); 132 free(from); 133 free(mountpoint); 134 return; 135 } 136 } else { 137 log_debugx("nothing mounted on %s; mounting", 138 mountpoint); 139 } 140 141 mount_autofs(from, mountpoint, options, prefix); 142 free(from); 143 free(mountpoint); 144 } 145 146 static void 147 mount_unmount(struct node *root) 148 { 149 struct statfs *mntbuf; 150 struct node *n, *n2; 151 int i, nitems; 152 153 nitems = getmntinfo(&mntbuf, MNT_WAIT); 154 if (nitems <= 0) 155 log_err(1, "getmntinfo"); 156 157 log_debugx("unmounting stale autofs mounts"); 158 159 for (i = 0; i < nitems; i++) { 160 if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) { 161 log_debugx("skipping %s, filesystem type is not autofs", 162 mntbuf[i].f_mntonname); 163 continue; 164 } 165 166 n = node_find(root, mntbuf[i].f_mntonname); 167 if (n != NULL) { 168 log_debugx("leaving autofs mounted on %s", 169 mntbuf[i].f_mntonname); 170 continue; 171 } 172 173 log_debugx("autofs mounted on %s not found " 174 "in new configuration; unmounting", mntbuf[i].f_mntonname); 175 unmount_by_statfs(&(mntbuf[i]), false); 176 } 177 178 log_debugx("mounting new autofs mounts"); 179 180 TAILQ_FOREACH(n, &root->n_children, n_next) { 181 if (!node_is_direct_map(n)) { 182 mount_if_not_already(n, n->n_map, n->n_options, 183 n->n_key, mntbuf, nitems); 184 continue; 185 } 186 187 TAILQ_FOREACH(n2, &n->n_children, n_next) { 188 mount_if_not_already(n2, n->n_map, n->n_options, 189 "/", mntbuf, nitems); 190 } 191 } 192 } 193 194 static void 195 flush_autofs(const char *fspath) 196 { 197 int error; 198 199 log_debugx("flushing %s", fspath); 200 201 error = mount("autofs", fspath, MNT_UPDATE, NULL); 202 if (error != 0) 203 log_err(1, "cannot flush %s", fspath); 204 } 205 206 static void 207 flush_caches(void) 208 { 209 struct statfs *mntbuf; 210 int i, nitems; 211 212 nitems = getmntinfo(&mntbuf, MNT_WAIT); 213 if (nitems <= 0) 214 log_err(1, "getmntinfo"); 215 216 log_debugx("flushing autofs caches"); 217 218 for (i = 0; i < nitems; i++) { 219 if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) { 220 log_debugx("skipping %s, filesystem type is not autofs", 221 mntbuf[i].f_mntonname); 222 continue; 223 } 224 225 flush_autofs(mntbuf[i].f_mntonname); 226 } 227 } 228 229 static void 230 unmount_automounted(bool force) 231 { 232 struct statfs *mntbuf; 233 int i, nitems; 234 235 nitems = getmntinfo(&mntbuf, MNT_WAIT); 236 if (nitems <= 0) 237 log_err(1, "getmntinfo"); 238 239 log_debugx("unmounting automounted filesystems"); 240 241 for (i = 0; i < nitems; i++) { 242 if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) { 243 log_debugx("skipping %s, filesystem type is autofs", 244 mntbuf[i].f_mntonname); 245 continue; 246 } 247 248 if ((mntbuf[i].f_flags & MNT_AUTOMOUNTED) == 0) { 249 log_debugx("skipping %s, not automounted", 250 mntbuf[i].f_mntonname); 251 continue; 252 } 253 254 unmount_by_statfs(&(mntbuf[i]), force); 255 } 256 } 257 258 static void 259 usage_automount(void) 260 { 261 262 fprintf(stderr, "usage: automount [-D name=value][-o opts][-Lcfuv]\n"); 263 exit(1); 264 } 265 266 int 267 main_automount(int argc, char **argv) 268 { 269 struct node *root; 270 int ch, debug = 0, show_maps = 0; 271 char *options = NULL; 272 bool do_unmount = false, force_unmount = false, flush = false; 273 274 /* 275 * Note that in automount(8), the only purpose of variable 276 * handling is to aid in debugging maps (automount -L). 277 */ 278 defined_init(); 279 280 while ((ch = getopt(argc, argv, "D:Lfco:uv")) != -1) { 281 switch (ch) { 282 case 'D': 283 defined_parse_and_add(optarg); 284 break; 285 case 'L': 286 show_maps++; 287 break; 288 case 'c': 289 flush = true; 290 break; 291 case 'f': 292 force_unmount = true; 293 break; 294 case 'o': 295 options = concat(options, ',', optarg); 296 break; 297 case 'u': 298 do_unmount = true; 299 break; 300 case 'v': 301 debug++; 302 break; 303 case '?': 304 default: 305 usage_automount(); 306 } 307 } 308 argc -= optind; 309 if (argc != 0) 310 usage_automount(); 311 312 if (force_unmount && !do_unmount) 313 usage_automount(); 314 315 log_init(debug); 316 317 if (flush) { 318 flush_caches(); 319 return (0); 320 } 321 322 if (do_unmount) { 323 unmount_automounted(force_unmount); 324 return (0); 325 } 326 327 root = node_new_root(); 328 parse_master(root, AUTO_MASTER_PATH); 329 330 if (show_maps) { 331 if (show_maps > 1) { 332 node_expand_indirect_maps(root); 333 node_expand_ampersand(root, NULL); 334 } 335 node_expand_defined(root); 336 node_print(root, options); 337 return (0); 338 } 339 340 mount_unmount(root); 341 342 return (0); 343 } 344