1/* $NetBSD$ */ 2 3/* 4 * Copyright (c) 1997-2014 Erez Zadok 5 * Copyright (c) 1990 Jan-Simon Pendry 6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 7 * Copyright (c) 1990 The Regents of the University of California. 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Jan-Simon Pendry at Imperial College, London. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * 38 * File: am-utils/conf/mtab/mtab_linux.c 39 * 40 */ 41 42/* This file was adapted by Red Hat for Linux from mtab_file.c */ 43 44/* 45 * The locking code must be kept in sync with that used 46 * by the mount command in util-linux, otherwise you'll 47 * end with with race conditions leading to a corrupt 48 * /etc/mtab, particularly when AutoFS is used on same 49 * machine as AMD. 50 */ 51 52#ifdef HAVE_CONFIG_H 53# include <config.h> 54#endif /* HAVE_CONFIG_H */ 55#include <am_defs.h> 56#include <amu.h> 57 58#define NFILE_RETRIES 10 /* number of retries (seconds) */ 59#define LOCK_TIMEOUT 10 60 61#ifdef MOUNT_TABLE_ON_FILE 62 63# define PROC_MOUNTS "/proc/mounts" 64 65static FILE *mnt_file = NULL; 66/* Information about mtab. ------------------------------------*/ 67static int have_mtab_info = 0; 68static int var_mtab_does_not_exist = 0; 69static int var_mtab_is_a_symlink = 0; 70/* Flag for already existing lock file. */ 71static int we_created_lockfile = 0; 72static int lockfile_fd = -1; 73 74 75static void 76get_mtab_info(void) 77{ 78 struct stat mtab_stat; 79 80 if (!have_mtab_info) { 81 if (lstat(MOUNTED, &mtab_stat)) 82 var_mtab_does_not_exist = 1; 83 else if (S_ISLNK(mtab_stat.st_mode)) 84 var_mtab_is_a_symlink = 1; 85 have_mtab_info = 1; 86 } 87} 88 89 90static int 91mtab_is_a_symlink(void) 92{ 93 get_mtab_info(); 94 return var_mtab_is_a_symlink; 95} 96 97 98static int 99mtab_is_writable() 100{ 101 static int ret = -1; 102 103 /* 104 * Should we write to /etc/mtab upon an update? Probably not if it is a 105 * symlink to /proc/mounts, since that would create a file /proc/mounts in 106 * case the proc filesystem is not mounted. 107 */ 108 if (mtab_is_a_symlink()) 109 return 0; 110 111 if (ret == -1) { 112 int fd = open(MOUNTED, O_RDWR | O_CREAT, 0644); 113 if (fd >= 0) { 114 close(fd); 115 ret = 1; 116 } else 117 ret = 0; 118 } 119 return ret; 120} 121 122 123static void 124setlkw_timeout(int sig) 125{ 126 /* nothing, fcntl will fail anyway */ 127} 128 129 130/* 131 * Create the lock file. 132 * The lock file will be removed if we catch a signal or when we exit. 133 * 134 * The old code here used flock on a lock file /etc/mtab~ and deleted 135 * this lock file afterwards. However, as rgooch remarks, that has a 136 * race: a second mount may be waiting on the lock and proceed as 137 * soon as the lock file is deleted by the first mount, and immediately 138 * afterwards a third mount comes, creates a new /etc/mtab~, applies 139 * flock to that, and also proceeds, so that the second and third mount 140 * now both are scribbling in /etc/mtab. 141 * The new code uses a link() instead of a creat(), where we proceed 142 * only if it was us that created the lock, and hence we always have 143 * to delete the lock afterwards. Now the use of flock() is in principle 144 * superfluous, but avoids an arbitrary sleep(). 145 */ 146 147/* 148 * Where does the link point to? Obvious choices are mtab and mtab~~. 149 * HJLu points out that the latter leads to races. Right now we use 150 * mtab~.<pid> instead. 151 */ 152#define MOUNTED_LOCK "/etc/mtab~" 153#define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d" 154 155int 156lock_mtab(void) 157{ 158 int tries = 100000, i; 159 char *linktargetfile; 160 size_t l; 161 int rc = 1; 162 163 /* 164 * Redhat's original code set a signal handler called "handler()" for all 165 * non-ALRM signals. The handler called unlock_mntlist(), plog'ed the 166 * signal name, and then exit(1)! Never, ever, exit() from inside a 167 * utility function. This messed up Amd's careful signal-handling code, 168 * and caused Amd to abort uncleanly only any other "innocent" signal 169 * (even simple SIGUSR1), leaving behind a hung Amd mnt point. That code 170 * should have at least restored the signal handlers' states upon a 171 * successful mtab unlocking. Anyway, that handler was unnecessary, 172 * because will call unlock_mntlist() properly anyway on exit. 173 */ 174 setup_sighandler(SIGALRM, setlkw_timeout); 175 176 /* somewhat clumsy, but some ancient systems do not have snprintf() */ 177 /* use 20 as upper bound for the length of %d output */ 178 l = strlen(MOUNTLOCK_LINKTARGET) + 20; 179 linktargetfile = xmalloc(l); 180 xsnprintf(linktargetfile, l, MOUNTLOCK_LINKTARGET, getpid()); 181 182 i = open(linktargetfile, O_WRONLY|O_CREAT, 0); 183 if (i < 0) { 184 int errsv = errno; 185 /* 186 * linktargetfile does not exist (as a file) and we cannot create 187 * it. Read-only filesystem? Too many files open in the system? 188 * Filesystem full? 189 */ 190 plog(XLOG_ERROR, "%s: can't create lock file %s: %s " 191 "(use -n flag to override)", __func__, 192 linktargetfile, strerror(errsv)); 193 goto error; 194 } 195 close(i); 196 197 198 /* Repeat until it was us who made the link */ 199 while (!we_created_lockfile) { 200 struct flock flock; 201 int errsv, j; 202 203 j = link(linktargetfile, MOUNTED_LOCK); 204 errsv = errno; 205 206 if (j < 0 && errsv != EEXIST) { 207 (void) unlink(linktargetfile); 208 plog(XLOG_ERROR, "can't link lock file %s: %s ", 209 MOUNTED_LOCK, strerror(errsv)); 210 rc = 0; 211 goto error; 212 } 213 214 lockfile_fd = open(MOUNTED_LOCK, O_WRONLY); 215 if (lockfile_fd < 0) { 216 int errsv = errno; 217 /* Strange... Maybe the file was just deleted? */ 218 if (errno == ENOENT && tries-- > 0) { 219 if (tries % 200 == 0) 220 usleep(30); 221 continue; 222 } 223 (void) unlink(linktargetfile); 224 plog(XLOG_ERROR,"%s: can't open lock file %s: %s ", __func__, 225 MOUNTED_LOCK, strerror(errsv)); 226 rc = 0; 227 goto error; 228 } 229 230 flock.l_type = F_WRLCK; 231 flock.l_whence = SEEK_SET; 232 flock.l_start = 0; 233 flock.l_len = 0; 234 235 if (j == 0) { 236 /* We made the link. Now claim the lock. */ 237 if (fcntl(lockfile_fd, F_SETLK, &flock) == -1) { 238 int errsv = errno; 239 plog(XLOG_ERROR, "%s: Can't lock lock file %s: %s", __func__, 240 MOUNTED_LOCK, strerror(errsv)); 241 /* proceed, since it was us who created the lockfile anyway */ 242 } 243 we_created_lockfile = 1; 244 (void) unlink(linktargetfile); 245 } else { 246 static int tries = 0; 247 248 /* Someone else made the link. Wait. */ 249 alarm(LOCK_TIMEOUT); 250 251 if (fcntl(lockfile_fd, F_SETLKW, &flock) == -1) { 252 int errsv = errno; 253 (void) unlink(linktargetfile); 254 plog(XLOG_ERROR, "%s: can't lock lock file %s: %s", __func__, 255 MOUNTED_LOCK, (errno == EINTR) ? 256 "timed out" : strerror(errsv)); 257 rc = 0; 258 goto error; 259 } 260 alarm(0); 261 /* 262 * Limit the number of iterations - maybe there 263 * still is some old /etc/mtab~ 264 */ 265 ++tries; 266 if (tries % 200 == 0) 267 usleep(30); 268 if (tries > 100000) { 269 (void) unlink(linktargetfile); 270 close(lockfile_fd); 271 plog(XLOG_ERROR, 272 "%s: Cannot create link %s; Perhaps there is a stale lock file?", 273 __func__, MOUNTED_LOCK); 274 rc = 0; 275 goto error; 276 } 277 close(lockfile_fd); 278 } 279 } 280 281error: 282 XFREE(linktargetfile); 283 284 return rc; 285} 286 287 288static FILE * 289open_locked_mtab(const char *mnttabname, char *mode, char *fs) 290{ 291 FILE *mfp = NULL; 292 293 if (mnt_file) { 294 dlog("Forced close on %s in read_mtab", mnttabname); 295 endmntent(mnt_file); 296 mnt_file = NULL; 297 } 298 299 if (!mtab_is_a_symlink() && 300 !lock_mtab()) { 301 plog(XLOG_ERROR, "%s: Couldn't lock mtab", __func__); 302 return 0; 303 } 304 305 mfp = setmntent((char *)mnttabname, mode); 306 if (!mfp) { 307 plog(XLOG_ERROR, "%s: setmntent(\"%s\", \"%s\"): %m", __func__, mnttabname, 308 mode); 309 return 0; 310 } 311 return mfp; 312} 313 314 315/* 316 * Unlock the mount table 317 */ 318void 319unlock_mntlist(void) 320{ 321 if (mnt_file || we_created_lockfile) 322 dlog("unlock_mntlist: releasing"); 323 if (mnt_file) { 324 endmntent(mnt_file); 325 mnt_file = NULL; 326 } 327 if (we_created_lockfile) { 328 close(lockfile_fd); 329 lockfile_fd = -1; 330 unlink(MOUNTED_LOCK); 331 we_created_lockfile = 0; 332 } 333} 334 335 336/* 337 * Write out a mount list 338 */ 339void 340rewrite_mtab(mntlist *mp, const char *mnttabname) 341{ 342 FILE *mfp; 343 int error = 0; 344 char tmpname[64]; 345 int retries; 346 int tmpfd; 347 char *cp; 348 char mcp[128]; 349 350 if (!mtab_is_writable()) { 351 return; 352 } 353 354 /* 355 * Concoct a temporary name in the same directory as the target mount 356 * table so that rename() will work. 357 */ 358 xstrlcpy(mcp, mnttabname, sizeof(mcp)); 359 cp = strrchr(mcp, '/'); 360 if (cp) { 361 memmove(tmpname, mcp, cp - mcp); 362 tmpname[cp - mcp] = '\0'; 363 } else { 364 plog(XLOG_WARNING, "No '/' in mtab (%s), using \".\" as tmp directory", mnttabname); 365 tmpname[0] = '.'; 366 tmpname[1] = '\0'; 367 } 368 xstrlcat(tmpname, "/mtabXXXXXX", sizeof(tmpname)); 369 retries = 0; 370 enfile1: 371#ifdef HAVE_MKSTEMP 372 tmpfd = mkstemp(tmpname); 373 fchmod(tmpfd, 0644); 374#else /* not HAVE_MKSTEMP */ 375 mktemp(tmpname); 376 tmpfd = open(tmpname, O_RDWR | O_CREAT | O_TRUNC, 0644); 377#endif /* not HAVE_MKSTEMP */ 378 if (tmpfd < 0) { 379 if (errno == ENFILE && retries++ < NFILE_RETRIES) { 380 sleep(1); 381 goto enfile1; 382 } 383 plog(XLOG_ERROR, "%s: open: %m", tmpname); 384 return; 385 } 386 if (close(tmpfd) < 0) 387 plog(XLOG_ERROR, "%s: Couldn't close tmp file descriptor: %m", __func__); 388 389 retries = 0; 390 enfile2: 391 mfp = setmntent(tmpname, "w"); 392 if (!mfp) { 393 if (errno == ENFILE && retries++ < NFILE_RETRIES) { 394 sleep(1); 395 goto enfile2; 396 } 397 plog(XLOG_ERROR, "%s: setmntent(\"%s\", \"w\"): %m", __func__, tmpname); 398 error = 1; 399 goto out; 400 } 401 while (mp) { 402 if (mp->mnt) { 403 if (addmntent(mfp, mp->mnt)) { 404 plog(XLOG_ERROR, "%s: Can't write entry to %s", __func__, tmpname); 405 error = 1; 406 goto out; 407 } 408 } 409 mp = mp->mnext; 410 } 411 412 /* 413 * SunOS 4.1 manuals say that the return code from entmntent() 414 * is always 1 and to treat as a void. That means we need to 415 * call fflush() to make sure the new mtab file got written. 416 */ 417 if (fflush(mfp)) { 418 plog(XLOG_ERROR, "flush new mtab file: %m"); 419 error = 1; 420 goto out; 421 } 422 (void) endmntent(mfp); 423 424 /* 425 * Rename temporary mtab to real mtab 426 */ 427 if (rename(tmpname, mnttabname) < 0) { 428 plog(XLOG_ERROR, "rename %s to %s: %m", tmpname, mnttabname); 429 error = 1; 430 goto out; 431 } 432 out: 433 if (error) 434 (void) unlink(tmpname); 435} 436 437 438static void 439mtab_stripnl(char *s) 440{ 441 do { 442 s = strchr(s, '\n'); 443 if (s) 444 *s++ = ' '; 445 } while (s); 446} 447 448 449/* 450 * Append a mntent structure to the 451 * current mount table. 452 */ 453void 454write_mntent(mntent_t *mp, const char *mnttabname) 455{ 456 int retries = 0; 457 FILE *mfp; 458 459 if (!mtab_is_writable()) { 460 return; 461 } 462 463 enfile: 464 mfp = open_locked_mtab(mnttabname, "a", mp->mnt_dir); 465 if (mfp) { 466 mtab_stripnl(mp->mnt_opts); 467 if (addmntent(mfp, mp)) 468 plog(XLOG_ERROR, "%s: Couldn't write %s: %m", __func__, mnttabname); 469 if (fflush(mfp)) 470 plog(XLOG_ERROR, "%s: Couldn't flush %s: %m", __func__, mnttabname); 471 (void) endmntent(mfp); 472 } else { 473 if (errno == ENFILE && retries < NFILE_RETRIES) { 474 sleep(1); 475 goto enfile; 476 } 477 plog(XLOG_ERROR, "%s: setmntent(\"%s\", \"a\"): %m", __func__, mnttabname); 478 } 479 480 unlock_mntlist(); 481} 482 483#endif /* MOUNT_TABLE_ON_FILE */ 484 485 486static mntent_t * 487mnt_dup(mntent_t *mp) 488{ 489 mntent_t *new_mp = ALLOC(mntent_t); 490 491 new_mp->mnt_fsname = xstrdup(mp->mnt_fsname); 492 new_mp->mnt_dir = xstrdup(mp->mnt_dir); 493 new_mp->mnt_type = xstrdup(mp->mnt_type); 494 new_mp->mnt_opts = xstrdup(mp->mnt_opts); 495 496 new_mp->mnt_freq = mp->mnt_freq; 497 new_mp->mnt_passno = mp->mnt_passno; 498 499#ifdef HAVE_MNTENT_T_MNT_TIME 500# ifdef HAVE_MNTENT_T_MNT_TIME_STRING 501 new_mp->mnt_time = xstrdup(mp->mnt_time); 502# else /* not HAVE_MNTENT_T_MNT_TIME_STRING */ 503 new_mp->mnt_time = mp->mnt_time; 504# endif /* not HAVE_MNTENT_T_MNT_TIME_STRING */ 505#endif /* HAVE_MNTENT_T_MNT_TIME */ 506 507#ifdef HAVE_MNTENT_T_MNT_CNODE 508 new_mp->mnt_cnode = mp->mnt_cnode; 509#endif /* HAVE_MNTENT_T_MNT_CNODE */ 510 511 return new_mp; 512} 513 514 515/* 516 * Read a mount table into memory 517 */ 518mntlist * 519read_mtab(char *fs, const char *mnttabname) 520{ 521 mntlist **mpp, *mhp; 522 523 mntent_t *mep; 524 525 FILE *mfp = open_locked_mtab(mnttabname, "r+", fs); 526 527 if (!mfp) 528 return 0; 529 530 mpp = &mhp; 531 532 /* 533 * XXX - In SunOS 4 there is (yet another) memory leak 534 * which loses 1K the first time getmntent is called. 535 * (jsp) 536 */ 537 while ((mep = getmntent(mfp))) { 538 /* 539 * Allocate a new slot 540 */ 541 *mpp = ALLOC(struct mntlist); 542 543 /* 544 * Copy the data returned by getmntent 545 */ 546 (*mpp)->mnt = mnt_dup(mep); 547 548 /* 549 * Move to next pointer 550 */ 551 mpp = &(*mpp)->mnext; 552 } 553 *mpp = NULL; 554 555#ifdef MOUNT_TABLE_ON_FILE 556 /* 557 * If we are not updating the mount table then we 558 * can free the resources held here, otherwise they 559 * must be held until the mount table update is complete 560 */ 561 mnt_file = mfp; 562#else /* not MOUNT_TABLE_ON_FILE */ 563 endmntent(mfp); 564#endif /* not MOUNT_TABLE_ON_FILE */ 565 566 return mhp; 567} 568