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