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