1/* 2 * Copyright (c) 2004 Alfred Perlstein <alfred@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $Id: autodriver.c,v 1.9 2004/09/08 08:12:21 bright Exp $ 27 * $FreeBSD$ 28 */ 29#include <ctype.h> 30#include <err.h> 31#include <errno.h> 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35#include <unistd.h> 36#include <fcntl.h> 37 38#include <sys/dirent.h> 39#include <sys/types.h> 40#include <sys/param.h> 41#include <sys/mount.h> 42#include <sys/poll.h> 43#include <sys/stat.h> 44 45#include <libautofs.h> 46 47struct autoentry { 48 char *ae_mnt; /* autofs mountpoint. */ 49 char *ae_path; /* path under mount. */ 50 char *ae_type; /* fs to be mounted type. */ 51 char *ae_opts; /* options passed to mount. */ 52 char *ae_rpath; /* remote path */ 53 char *ae_free; /* freeme! */ 54 char *ae_fullpath; /* full path to mount */ 55 int ae_line; /* line it came from in the conf. */ 56 int ae_indirect; /* is this an indirect mount? */ 57 int ae_direct; /* is this a direct mount? */ 58 int ae_browse; /* browseable? */ 59 struct autoentry *ae_next; /* next. */ 60}; 61 62struct autoentry *entries; 63const char *mount_prog = "mount"; 64const char *fstype = "autofs"; 65 66void *xmalloc(size_t); 67void *xcalloc(size_t number, size_t size); 68void parsetab(void); 69void populate_tab(void); 70void doreq(autoh_t, autoreq_t); 71void dotheneedful(autoh_t); 72void eventloop(void); 73int poll_handles(autoh_t *array, int cnt); 74int mount_indirect(struct autofs_userreq *req, struct autoentry *ent); 75int mount_direct(struct autofs_userreq *req, struct autoentry *ent); 76int mount_browse(struct autofs_userreq *req, struct autoentry *ent); 77 78#define DSTR(s) sizeof(s) - 1, s 79 80struct dirent dumbents[] = { 81 {50, sizeof(struct dirent), DT_DIR, DSTR("one") }, 82 {51, sizeof(struct dirent), DT_DIR, DSTR(".") }, 83 {52, sizeof(struct dirent), DT_DIR, DSTR("..") }, 84 {50, sizeof(struct dirent), DT_DIR, DSTR("two") }, 85}; 86 87void * 88xmalloc(size_t size) 89{ 90 void *ret; 91 92 ret = malloc(size); 93 if (ret == NULL) 94 err(1, "malloc %d", (int) size); 95 return (ret); 96} 97 98void * 99xcalloc(size_t number, size_t size) 100{ 101 void *ret; 102 103 ret = calloc(number, size); 104 if (ret == NULL) 105 err(1, "calloc %d %d", (int)number, (int)size); 106 return (ret); 107} 108 109void 110parsetab(void) 111{ 112 FILE *fp; 113 const char *tab; 114 char *cp, *p, *line, *opt; 115 size_t len; 116 struct autoentry *ent; 117 int i, lineno, x, gotopt; 118 const char *expecting = "expecting 'direct', 'indirect' or 'browse'"; 119 const char *tabfiles[] = { 120 "/etc/autotab", "/usr/local/etc/autotab", "./autotab", NULL 121 }; 122 123 lineno = 0; 124 for (i = 0; (tab = tabfiles[i]) != NULL; i++) { 125 tab = tabfiles[i]; 126 fp = fopen(tab, "r"); 127 if (fp == NULL) 128 warn("fopen %s", tab); 129 if (fp != NULL) 130 break; 131 } 132 if (fp == NULL) { 133 err(1, "no config file available."); 134 } 135 136 fprintf(stderr, "using config file: %s\n", tab); 137 138 while ((cp = fgetln(fp, &len)) != NULL) { 139 lineno++; 140 while (len > 0 && isspace(cp[len - 1])) 141 len--; 142 line = xmalloc(len + 1); 143 bcopy(cp, line, len); 144 line[len] = '\0'; 145 cp = line; 146 if ((cp = strchr(line, '#')) != NULL) 147 *cp = '\0'; 148 cp = line; 149 while (isspace(*cp)) 150 cp++; 151 if (*cp == '\0') { 152 free(line); 153 continue; 154 } 155 ent = xcalloc(1, sizeof(*ent)); 156 if ((p = strsep(&cp, " \t")) == NULL) 157 goto bad; 158 ent->ae_mnt = p; 159 if ((p = strsep(&cp, " \t")) == NULL) 160 goto bad; 161 ent->ae_path = p; 162 if ((p = strsep(&cp, " \t")) == NULL) 163 goto bad; 164 ent->ae_type = p; 165 if ((p = strsep(&cp, " \t")) == NULL) 166 goto bad; 167 ent->ae_opts = p; 168 if ((p = strsep(&cp, " \t")) == NULL) 169 goto bad; 170 ent->ae_rpath = p; 171 if ((p = strsep(&cp, " \t")) == NULL) 172 goto bad; 173 gotopt = 0; 174 opt = p; 175 while ((p = strsep(&opt, ",")) != NULL) { 176 if (strcmp(p, "indirect") == 0) { 177 ent->ae_indirect = 1; 178 gotopt = 1; 179 } else if (strcmp(p, "direct") == 0) { 180 ent->ae_direct = 1; 181 gotopt = 1; 182 } else if (strcmp(p, "browse") == 0) { 183 ent->ae_browse = 1; 184 gotopt = 1; 185 } else { 186 warnx("unreconized option '%s', %s", 187 p, expecting); 188 goto bad2; 189 } 190 } 191 if (!gotopt) { 192 warnx("no options specified %s", expecting); 193 goto bad2; 194 } 195 if (ent->ae_direct && ent->ae_indirect) { 196 warnx("direct and indirect are mutually exclusive"); 197 goto bad2; 198 199 } 200 x = asprintf(&ent->ae_fullpath, "%s/%s", 201 ent->ae_mnt, ent->ae_path); 202 if (x == -1) 203 err(1, "asprintf"); 204 205 if (strlen(ent->ae_fullpath) + 1 > PATH_MAX) { 206 warnx("Error in file %s, line %d, " 207 "mountpath (%s) exceeds PATH_MAX (%d)", 208 tab, lineno, ent->ae_fullpath, PATH_MAX); 209 goto bad2; 210 } 211 ent->ae_line = lineno; 212 ent->ae_free = line; 213 ent->ae_next = entries; 214 entries = ent; 215 continue; 216bad: 217 warnx("Parse error in file %s, line %d", tab, lineno); 218bad2: 219 free(ent->ae_fullpath); 220 free(line); 221 free(ent); 222 } 223 if (ferror(fp)) 224 err(1, "error with file %s", tab); 225} 226 227void 228populate_tab(void) 229{ 230 struct autoentry *ent; 231 char *path, *cmd; 232 int error; 233 autoh_t ah; 234 235 path = cmd = NULL; 236 237 for (ent = entries; ent != NULL; ent = ent->ae_next) { 238 free(path); 239 free(cmd); 240 error = asprintf(&path, "%s/%s", ent->ae_mnt, ent->ae_path); 241 if (error == -1) 242 err(1, "asprintf"); 243 error = asprintf(&cmd, "mkdir -p %s", path); 244 if (error == -1) 245 err(1, "asprintf"); 246 error = system(cmd); 247 if (error) { 248 warn("system: %s", cmd); 249 continue; 250 } 251 if (autoh_get(ent->ae_mnt, &ah)) { 252 warn("autoh_get %s", path); 253 continue; 254 } 255 error = autoh_togglepath(ah, AUTO_MOUNTER, getpid(), path); 256 if (error) { 257 err(1, "AUTO_MOUNTER %s", path); 258 continue; 259 } 260 if (ent->ae_browse) { 261 error = autoh_togglepath(ah, AUTO_BROWSE, getpid(), 262 path); 263 if (error) 264 err(1, "AUTO_BROWSE %s", path); 265 } 266 if (ent->ae_direct) { 267 error = autoh_togglepath(ah, AUTO_DIRECT, getpid(), 268 path); 269 if (error) 270 err(1, "AUTO_DIRECT %s", path); 271 } 272 if (ent->ae_indirect) { 273 error = autoh_togglepath(ah, AUTO_INDIRECT, getpid(), 274 path); 275 if (error) 276 err(1, "AUTO_INDIRECT %s", path); 277 } 278 autoh_free(ah); 279 } 280 free(path); 281 free(cmd); 282} 283 284/* 285 * Process an autofs request, scan the list of entries in the config 286 * looking for our node, if found mount it. 287 */ 288void 289doreq(autoh_t ah, autoreq_t req) 290{ 291 struct autoentry *ent; 292 int error; 293 int mcmp; 294 int xid; 295 const char *mnt; 296 297 mnt = autoh_mp(ah); 298 299 autoreq_seterrno(req, 0); 300 for (ent = entries; ent != NULL; ent = ent->ae_next) { 301 fprintf(stderr, "comparing {%s,%s} to {%s,%s}\n", 302 mnt, ent->ae_mnt, autoreq_getpath(req), ent->ae_path); 303 fprintf(stderr, "comparing {%d,%d} to {%d,%d}\n", 304 (int)strlen(mnt), 305 (int)strlen(ent->ae_mnt), 306 (int)strlen(autoreq_getpath(req)), 307 (int)strlen(ent->ae_path)); 308 autoreq_getxid(req, &xid); 309 fprintf(stderr, "req xid %d\n", xid); 310 if ((mcmp = strcmp(mnt, ent->ae_mnt)) != 0) { 311 fprintf(stderr, "mcmp = %d\n", mcmp); 312 continue; 313 } 314 if (mount_direct(req, ent)) 315 goto serve; 316 if (mount_indirect(req, ent)) 317 goto serve; 318 if (mount_browse(req, ent)) 319 goto serve; 320 } 321 fprintf(stderr, "no entry found...\n"); 322 autoreq_seterrno(req, ENOENT); 323serve: 324 error = autoreq_serv(ah, req); 325 if (error == -1) { 326 warn("AUTOFS_CTL_SERVREQ"); 327 } 328} 329 330int 331mount_indirect(req, ent) 332 struct autofs_userreq *req; 333 struct autoentry *ent; 334{ 335 struct stat sb; 336 char *path, *cmd; 337 int error, x; 338 339 if (ent->ae_indirect != 1) { 340 fprintf(stderr, "not indirect.\n"); 341 return (0); 342 } 343 fprintf(stderr, "indirect mount...\n"); 344 /* 345 * handle lookups, fake all stat(2) requests... this is bad, 346 * but we're a driver so we don't care... 347 * If we don't care about the type of request, then just return. 348 */ 349 switch (autoreq_getop(req)) { 350 case AUTOREQ_OP_LOOKUP: 351 break; 352 case AUTOREQ_OP_STAT: 353 fprintf(stderr, "stat\n"); 354 return (1); 355 default: 356 fprintf(stderr, "unknown\n"); 357 return (0); 358 } 359 if (stat(ent->ae_fullpath, &sb)) 360 return (0); 361 if (sb.st_ino != autoreq_getdirino(req)) { 362 fprintf(stderr, "st_ino %d != dirino %d\n", 363 (int)sb.st_ino, (int)autoreq_getdirino(req)); 364 return (0); 365 } 366 x = asprintf(&path, "%s/%s", ent->ae_fullpath, autoreq_getpath(req)); 367 if (x > PATH_MAX) { 368 autoreq_seterrno(req, ENAMETOOLONG); 369 return (1); 370 } 371 if (mkdir(path, 0555) == -1) 372 warn("mkdir %s", path); 373 error = asprintf(&cmd, "%s -t %s -o %s %s/%s %s", mount_prog, 374 ent->ae_type, ent->ae_opts, ent->ae_rpath, autoreq_getpath(req), path); 375 fprintf(stderr, "running:\n\t%s\n", cmd); 376 error = system(cmd); 377 fprintf(stderr, "error = %d\n", error); 378 free(cmd); 379 if (error) { 380 if (rmdir(path) == -1) 381 warn("rmdir %s", path); 382 autoreq_seterrno(req, ENOENT); 383 } else { 384 if (stat(path, &sb) != -1) 385 autoreq_setino(req, sb.st_ino); 386 /* XXX !!! */ 387 /* req->au_flags = 1; */ 388 } 389 free(path); 390 return (1); 391} 392 393int 394mount_direct(req, ent) 395 struct autofs_userreq *req; 396 struct autoentry *ent; 397{ 398 struct stat sb; 399 char *cmd; 400 int error; 401 402 if (ent->ae_direct != 1) { 403 fprintf(stderr, "not direct.\n"); 404 return (0); 405 } 406 fprintf(stderr, "direct mount...\n"); 407 /* 408 * handle lookups, fake all stat(2) requests... this is bad, 409 * but we're a driver so we don't care... 410 * If we don't care about the type of request, then just return. 411 */ 412 switch (autoreq_getop(req)) { 413 case AUTOREQ_OP_LOOKUP: 414 break; 415 case AUTOREQ_OP_STAT: 416 return (1); 417 default: 418 return (0); 419 } 420 if (stat(ent->ae_fullpath, &sb)) 421 return (0); 422 if (sb.st_ino != autoreq_getino(req)) 423 return (0); 424 error = asprintf(&cmd, "%s -t %s -o %s %s %s", mount_prog, 425 ent->ae_type, ent->ae_opts, ent->ae_rpath, ent->ae_fullpath); 426 if (error == -1) 427 err(1, "asprintf"); 428 fprintf(stderr, "running:\n\t%s\n", cmd); 429 error = system(cmd); 430 fprintf(stderr, "error = %d\n", error); 431 free(cmd); 432 if (error) { 433 autoreq_seterrno(req, ENOENT); 434 return (1); 435 } 436 /* XXX: fix ONLIST in kernel */ 437 /* req->au_flags = 1; */ 438 return (1); 439} 440 441int 442mount_browse(req, ent) 443 struct autofs_userreq *req; 444 struct autoentry *ent; 445{ 446 off_t off; 447 448 if (ent->ae_browse != 1) 449 return (0); 450 if (autoreq_getop(req) != AUTOREQ_OP_READDIR) 451 return (0); 452 autoreq_getoffset(req, &off); 453 if (off < sizeof(dumbents)) 454 autoreq_setaux(req, dumbents, sizeof(dumbents)); 455 fprintf(stderr, "mount_browse: offset %d, size %d\n", 456 (int)off, (int)sizeof(dumbents)); 457 autoreq_seteof(req, 1); 458 return (1); 459} 460 461/* 462 * Ask the filesystem passed in if it has a pending request. 463 * if so process them. 464 */ 465void 466dotheneedful(autoh_t ah) 467{ 468 int cnt, i; 469 autoreq_t *reqs; 470 471 if (autoreq_get(ah, &reqs, &cnt)) 472 err(1, "autoreq_get"); 473 474 for (i = 0; i < cnt; i++) { 475 fprintf(stderr, "processing request for '%s' '%s'\n", 476 autoh_mp(ah), autoreq_getpath(reqs[i])); 477 doreq(ah, reqs[i]); 478 } 479 free(reqs); 480} 481 482int 483poll_handles(autoh_t *array, int cnt) 484{ 485 int i, saved_errno, x; 486 static struct pollfd *pfd = NULL; 487 488 pfd = reallocf(pfd, cnt * sizeof(*pfd)); 489 if (pfd == NULL) 490 return (-1); 491 for (i = 0; i < cnt; i++) { 492 pfd[i].fd = autoh_fd(array[i]); 493 pfd[i].events = POLLPRI; 494 pfd[i].revents = 0; 495 } 496 fprintf(stderr, "start polling...\n"); 497 x = poll(pfd, cnt, 10000); 498 saved_errno = errno; 499 fprintf(stderr, "done polling...\n"); 500 errno = saved_errno; 501 if (x == -1) 502 return (-1); 503 /* at least one fs is ready... */ 504 if (x > 0) 505 return (0); 506 return (0); 507} 508 509void 510eventloop(void) 511{ 512 autoh_t *array; 513 int cnt, i; 514 515 fprintf(stderr, "starting event loop...\n"); 516 for ( ;; ) { 517 if (autoh_getall(&array, &cnt)) 518 err(1, "autoh_getall"); 519 if (poll_handles(array, cnt)) 520 err(1, "poll_handles"); 521 for (i = 0; i < cnt; i++) { 522 dotheneedful(array[i]); 523 } 524 autoh_freeall(array); 525 } 526} 527 528int 529main(int argc __unused, char **argv __unused) 530{ 531 532 if (getuid() != 0) 533 errx(1, "autodriver needs to be run as root to work."); 534 parsetab(); 535 populate_tab(); 536 eventloop(); 537 return (0); 538} 539