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 81 create_directory(fspath); 82 83 log_debugx("mounting %s on %s, prefix \"%s\", options \"%s\"", 84 from, fspath, prefix, options); 85 86 memset(&info, 0, sizeof(info)); 87 info.from = from; 88 info.master_options = options; 89 info.master_prefix = prefix; 90 91 error = mount("autofs", fspath, 0, &info); 92 if (error != 0) 93 log_err(1, "cannot mount %s on %s", from, fspath); 94 } 95 96 static void 97 mount_if_not_already(const struct node *n, const char *map, const char *options, 98 const char *prefix, const struct statfs *mntbuf, int nitems) 99 { 100 const struct statfs *sb; 101 char *mountpoint; 102 char *from; 103 int ret; 104 105 ret = asprintf(&from, "map %s", map); 106 if (ret < 0) 107 log_err(1, "asprintf"); 108 109 mountpoint = node_path(n); 110 sb = find_statfs(mntbuf, nitems, mountpoint); 111 if (sb != NULL) { 112 if (strcmp(sb->f_fstypename, "autofs") != 0) { 113 log_debugx("unknown filesystem mounted " 114 "on %s; mounting", mountpoint); 115 /* 116 * XXX: Compare options and 'from', 117 * and update the mount if necessary. 118 */ 119 } else { 120 log_debugx("autofs already mounted " 121 "on %s", mountpoint); 122 free(from); 123 free(mountpoint); 124 return; 125 } 126 } else { 127 log_debugx("nothing mounted on %s; mounting", 128 mountpoint); 129 } 130 131 mount_autofs(from, mountpoint, options, prefix); 132 free(from); 133 free(mountpoint); 134 } 135 136 static void 137 mount_unmount(struct node *root) 138 { 139 struct statfs *mntbuf; 140 struct node *n, *n2; 141 int i, nitems; 142 143 nitems = getmntinfo(&mntbuf, MNT_WAIT); 144 if (nitems <= 0) 145 log_err(1, "getmntinfo"); 146 147 log_debugx("unmounting stale autofs mounts"); 148 149 for (i = 0; i < nitems; i++) { 150 if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) { 151 log_debugx("skipping %s, filesystem type is not autofs", 152 mntbuf[i].f_mntonname); 153 continue; 154 } 155 156 n = node_find(root, mntbuf[i].f_mntonname); 157 if (n != NULL) { 158 log_debugx("leaving autofs mounted on %s", 159 mntbuf[i].f_mntonname); 160 continue; 161 } 162 163 log_debugx("autofs mounted on %s not found " 164 "in new configuration; unmounting", mntbuf[i].f_mntonname); 165 unmount_by_statfs(&(mntbuf[i]), false); 166 } 167 168 log_debugx("mounting new autofs mounts"); 169 170 TAILQ_FOREACH(n, &root->n_children, n_next) { 171 if (!node_is_direct_map(n)) { 172 mount_if_not_already(n, n->n_map, n->n_options, 173 n->n_key, mntbuf, nitems); 174 continue; 175 } 176 177 TAILQ_FOREACH(n2, &n->n_children, n_next) { 178 mount_if_not_already(n2, n->n_map, n->n_options, 179 "/", mntbuf, nitems); 180 } 181 } 182 } 183 184 static void 185 flush_autofs(const char *fspath) 186 { 187 int error; 188 189 log_debugx("flushing %s", fspath); 190 191 error = mount("autofs", fspath, MNT_UPDATE, NULL); 192 if (error != 0) 193 log_err(1, "cannot flush %s", fspath); 194 } 195 196 static void 197 flush_caches(void) 198 { 199 struct statfs *mntbuf; 200 int i, nitems; 201 202 nitems = getmntinfo(&mntbuf, MNT_WAIT); 203 if (nitems <= 0) 204 log_err(1, "getmntinfo"); 205 206 log_debugx("flushing autofs caches"); 207 208 for (i = 0; i < nitems; i++) { 209 if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) { 210 log_debugx("skipping %s, filesystem type is not autofs", 211 mntbuf[i].f_mntonname); 212 continue; 213 } 214 215 flush_autofs(mntbuf[i].f_mntonname); 216 } 217 } 218 219 static void 220 unmount_automounted(bool force) 221 { 222 struct statfs *mntbuf; 223 int i, nitems; 224 225 nitems = getmntinfo(&mntbuf, MNT_WAIT); 226 if (nitems <= 0) 227 log_err(1, "getmntinfo"); 228 229 log_debugx("unmounting automounted filesystems"); 230 231 for (i = 0; i < nitems; i++) { 232 if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) { 233 log_debugx("skipping %s, filesystem type is autofs", 234 mntbuf[i].f_mntonname); 235 continue; 236 } 237 238 if ((mntbuf[i].f_flags & MNT_AUTOMOUNTED) == 0) { 239 log_debugx("skipping %s, not automounted", 240 mntbuf[i].f_mntonname); 241 continue; 242 } 243 244 unmount_by_statfs(&(mntbuf[i]), force); 245 } 246 } 247 248 static void 249 usage_automount(void) 250 { 251 252 fprintf(stderr, "usage: automount [-D name=value][-o opts][-Lcfuv]\n"); 253 exit(1); 254 } 255 256 int 257 main_automount(int argc, char **argv) 258 { 259 struct node *root; 260 int ch, debug = 0, show_maps = 0; 261 char *options = NULL; 262 bool do_unmount = false, force_unmount = false, flush = false; 263 264 /* 265 * Note that in automount(8), the only purpose of variable 266 * handling is to aid in debugging maps (automount -L). 267 */ 268 defined_init(); 269 270 while ((ch = getopt(argc, argv, "D:Lfco:uv")) != -1) { 271 switch (ch) { 272 case 'D': 273 defined_parse_and_add(optarg); 274 break; 275 case 'L': 276 show_maps++; 277 break; 278 case 'c': 279 flush = true; 280 break; 281 case 'f': 282 force_unmount = true; 283 break; 284 case 'o': 285 options = concat(options, ',', optarg); 286 break; 287 case 'u': 288 do_unmount = true; 289 break; 290 case 'v': 291 debug++; 292 break; 293 case '?': 294 default: 295 usage_automount(); 296 } 297 } 298 argc -= optind; 299 if (argc != 0) 300 usage_automount(); 301 302 if (force_unmount && !do_unmount) 303 usage_automount(); 304 305 log_init(debug); 306 307 if (flush) { 308 flush_caches(); 309 return (0); 310 } 311 312 if (do_unmount) { 313 unmount_automounted(force_unmount); 314 return (0); 315 } 316 317 root = node_new_root(); 318 parse_master(root, AUTO_MASTER_PATH); 319 320 if (show_maps) { 321 if (show_maps > 1) { 322 node_expand_indirect_maps(root); 323 node_expand_ampersand(root, NULL); 324 } 325 node_expand_defined(root); 326 node_print(root, options); 327 return (0); 328 } 329 330 mount_unmount(root); 331 332 return (0); 333 } 334