1/* $NetBSD$ */ 2 3/* 4 * Copyright (c) 1997-2009 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. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgment: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * 42 * File: am-utils/conf/mtab/mtab_mach3.c 43 * 44 */ 45 46#ifdef HAVE_CONFIG_H 47# include <config.h> 48#endif /* HAVE_CONFIG_H */ 49#include <am_defs.h> 50#include <amu.h> 51 52#define NFILE_RETRIES 10 /* number of retries (seconds) */ 53 54static FILE *mnt_file; 55 56 57/* 58 * If the system is being trashed by something, then 59 * opening mtab may fail with ENFILE. So, go to sleep 60 * for a second and try again. (Yes - this has happened to me.) 61 * 62 * Note that this *may* block the automounter, oh well. 63 * If we get to this state then things are badly wrong anyway... 64 * 65 * Give the system 10 seconds to recover but then give up. 66 * Hopefully something else will exit and free up some file 67 * table slots in that time. 68 */ 69#ifdef HAVE_FCNTL_H 70static int 71lock(int fd) 72{ 73 int rc; 74 struct flock lk; 75 76 lk.l_type = F_WRLCK; 77 lk.l_whence = 0; 78 lk.l_start = 0; 79 lk.l_len = 0; 80 81again: 82 rc = fcntl(fd, F_SETLKW, (caddr_t) & lk); 83 if (rc < 0 && (errno == EACCES || errno == EAGAIN)) { 84# ifdef DEBUG 85 dlog("Blocked, trying to obtain exclusive mtab lock"); 86# endif /* DEBUG */ 87 sleep(1); 88 goto again; 89 } 90 return rc; 91} 92#else /* not HAVE_FCNTL_H */ 93# define lock(fd) (flock((fd), LOCK_EX)) 94#endif /* not HAVE_FCNTL_H */ 95 96 97static FILE * 98open_locked_mtab(char *mnttabname, char *mode, char *fs) 99{ 100 FILE *mfp = NULL; 101 102 /* 103 * There is a possible race condition if two processes enter 104 * this routine at the same time. One will be blocked by the 105 * exclusive lock below (or by the shared lock in setmntent) 106 * and by the time the second process has the exclusive lock 107 * it will be on the wrong underlying object. To check for this 108 * the mtab file is stat'ed before and after all the locking 109 * sequence, and if it is a different file then we assume that 110 * it may be the wrong file (only "may", since there is another 111 * race between the initial stat and the setmntent). 112 * 113 * Simpler solutions to this problem are invited... 114 */ 115 int racing = 2; 116 int rc; 117 int retries = 0; 118 struct stat st_before, st_after; 119 120 if (mnt_file) { 121#ifdef DEBUG 122 dlog("Forced close on %s in read_mtab", mnttabname); 123#endif /* DEBUG */ 124 endmntent(mnt_file); 125 mnt_file = NULL; 126 } 127again: 128 if (mfp) { 129 endmntent(mfp); 130 mfp = NULL; 131 } 132 if (stat(mnttabname, &st_before) < 0) { 133 plog(XLOG_ERROR, "%s: stat: %m", mnttabname); 134 if (errno == ESTALE) { 135 /* happens occasionally */ 136 sleep(1); 137 goto again; 138 } 139 /* 140 * If 'mnttabname' file does not exist give setmntent() a 141 * chance to create it (depending on the mode). 142 * Otherwise, bail out. 143 */ 144 else if (errno != ENOENT) { 145 return 0; 146 } 147 } 148eacces: 149 mfp = setmntent(mnttabname, mode); 150 if (!mfp) { 151 /* 152 * Since setmntent locks the descriptor, it 153 * is possible it can fail... so retry if 154 * needed. 155 */ 156 if (errno == EACCES || errno == EAGAIN) { 157#ifdef DEBUG 158 dlog("Blocked, trying to obtain exclusive mtab lock"); 159#endif /* DEBUG */ 160 goto eacces; 161 } else if (errno == ENFILE && retries++ < NFILE_RETRIES) { 162 sleep(1); 163 goto eacces; 164 } 165 plog(XLOG_ERROR, "setmntent(\"%s\", \"%s\"): %m", mnttabname, mode); 166 return 0; 167 } 168 /* 169 * At this point we have an exclusive lock on the mount list, 170 * but it may be the wrong one so... 171 */ 172 173 /* 174 * Need to get an exclusive lock on the current 175 * mount table until we have a new copy written 176 * out, when the lock is released in free_mntlist. 177 * flock is good enough since the mount table is 178 * not shared between machines. 179 */ 180 do 181 rc = lock(fileno(mfp)); 182 while (rc < 0 && errno == EINTR); 183 if (rc < 0) { 184 plog(XLOG_ERROR, "Couldn't lock %s: %m", mnttabname); 185 endmntent(mfp); 186 return 0; 187 } 188 /* 189 * Now check whether the mtab file has changed under our feet 190 */ 191 if (stat(mnttabname, &st_after) < 0) { 192 plog(XLOG_ERROR, "%s: stat", mnttabname); 193 goto again; 194 } 195 if (st_before.st_dev != st_after.st_dev || 196 st_before.st_ino != st_after.st_ino) { 197 struct timeval tv; 198 if (racing == 0) { 199 /* Sometimes print a warning */ 200 plog(XLOG_WARNING, 201 "Possible mount table race - retrying %s", fs); 202 } 203 racing = (racing + 1) & 3; 204 /* 205 * Take a nap. From: Doug Kingston <dpk@morgan.com> 206 */ 207 tv.tv_sec = 0; 208 tv.tv_usec = (am_mypid & 0x07) << 17; 209 if (tv.tv_usec) 210 if (select(0, (voidp) 0, (voidp) 0, (voidp) 0, &tv) < 0) 211 plog(XLOG_WARNING, "mtab nap failed: %m"); 212 213 goto again; 214 } 215 return mfp; 216} 217 218 219/* 220 * Unlock the mount table 221 */ 222void 223unlock_mntlist(void) 224{ 225 /* 226 * Release file lock, by closing the file 227 */ 228 if (mnt_file) { 229 dlog("unlock_mntlist: releasing"); 230 endmntent(mnt_file); 231 mnt_file = NULL; 232 } 233} 234 235 236/* 237 * routine to convert notation "/@honeydew" to the notation 238 * honeydew:/ and vice versa (honeydew:/ to /@honeydew) 239 * This lets you put /@honeydew in /etc/fstab without changing 240 * fstab.c and it lets you use EITHER notation on the command line! 241 */ 242static char * 243convert(register char *s, char bad, char good) 244{ 245 char *index(); 246 register char *t, *p; 247 register int len1, len2, i; 248 char *ptr; 249 250 if ((p = index(s, bad)) == NULL) { 251 return (s); 252 } 253 ptr = t = (char *) xzalloc(MAXPATHLEN * sizeof(char)); 254 len1 = p - s; 255 len2 = strlen(s) - len1 - 1; 256 p++; 257 for (i = 0; i < len2; i++) 258 *t++ = p[i]; 259 *t++ = good; 260 for (i = 0; i < len1; i++) 261 *t++ = s[i]; 262 return (ptr); 263} 264 265 266static 267mntprtent3(FILE *mnttabp, register mntent_t *mnt) 268{ 269 char *cvtd = convert(mnt->mnt_fsname, ':', '@'); 270 271 dlog("%x:%s:%s:%s:%d:%d:%s:%s:\n", 272 mnttabp, 273 (cvtd ? cvtd : ""), 274 (mnt->mnt_dir ? mnt->mnt_dir : ""), 275 (mnt->mnt_opts ? mnt->mnt_opts : ""), 276 mnt->mnt_freq, 277 mnt->mnt_passno, 278 (mnt->mnt_type ? mnt->mnt_type : ""), 279 (mnt->mnt_opts2 ? mnt->mnt_opts2 : "") 280 ); 281 fprintf(mnttabp, "%s:%s:%s:%d:%d:%s:%s:\n", 282 (cvtd ? cvtd : ""), 283 (mnt->mnt_dir ? mnt->mnt_dir : ""), 284 (mnt->mnt_opts ? mnt->mnt_opts : ""), 285 mnt->mnt_freq, 286 mnt->mnt_passno, 287 (mnt->mnt_type ? mnt->mnt_type : ""), 288 (mnt->mnt_opts2 ? mnt->mnt_opts2 : "") 289 ); 290 XFREE(cvtd); 291 cvtd = NULL; 292 return (0); 293} 294 295 296addmntent3(FILE *mnttabp, register mntent_t *mnt) 297{ 298 if (fseek(mnttabp, 0, 2) < 0) { 299 return (1); 300 } 301 mntprtent3(mnttabp, mnt); 302 return (0); 303} 304 305 306/* 307 * Write out a mount list 308 */ 309void 310rewrite_mtab(mntlist *mp, const char *mnttabname) 311{ 312 FILE *mfp; 313 int error = 0; 314 /* 315 * Concoct a temporary name in the same directory as the target mount 316 * table so that rename() will work. 317 */ 318 char tmpname[64]; 319 int retries; 320 int tmpfd; 321 char *cp; 322 char *mcp = mnttabname; 323 324 cp = strrchr(mcp, '/'); 325 if (cp) { 326 memmove(tmpname, mcp, cp - mcp); 327 tmpname[cp - mcp] = '\0'; 328 } else { 329 plog(XLOG_WARNING, "No '/' in mtab (%s), using \".\" as tmp directory", mnttabname); 330 tmpname[0] = '.'; 331 tmpname[1] = '\0'; 332 } 333 xstrlcat(tmpname, "/mtabXXXXXX", sizeof(tmpname)); 334 retries = 0; 335enfile1: 336#ifdef HAVE_MKSTEMP 337 tmpfd = mkstemp(tmpname); 338 fchmod(tmpfd, 0644); 339#else /* not HAVE_MKSTEMP */ 340 mktemp(tmpname); 341 tmpfd = open(tmpname, O_RDWR | O_CREAT | O_TRUNC, 0644); 342#endif /* not HAVE_MKSTEMP */ 343 if (tmpfd < 0) { 344 if (errno == ENFILE && retries++ < NFILE_RETRIES) { 345 sleep(1); 346 goto enfile1; 347 } 348 plog(XLOG_ERROR, "%s: open: %m", tmpname); 349 return; 350 } 351 if (close(tmpfd) < 0) 352 plog(XLOG_ERROR, "Couldn't close tmp file descriptor: %m"); 353 354 retries = 0; 355enfile2: 356 mfp = setmntent(tmpname, "w"); 357 if (!mfp) { 358 if (errno == ENFILE && retries++ < NFILE_RETRIES) { 359 sleep(1); 360 goto enfile2; 361 } 362 plog(XLOG_ERROR, "setmntent(\"%s\", \"w\"): %m", tmpname); 363 error = 1; 364 goto out; 365 } 366 while (mp) { 367 if (mp->mnt) { 368 if (addmntent3(mfp, mp->mnt)) { 369 plog(XLOG_ERROR, "Can't write entry to %s", tmpname); 370 error = 1; 371 goto out; 372 } 373 } 374 mp = mp->mnext; 375 } 376 377 /* 378 * SunOS 4.1 manuals say that the return code from endmntent() 379 * is always 1 and to treat as a void. That means we need to 380 * call fflush() to make sure the new mtab file got written. 381 */ 382 if (fflush(mfp)) { 383 plog(XLOG_ERROR, "flush new mtab file: %m"); 384 error = 1; 385 goto out; 386 } 387 (void) endmntent(mfp); 388 389 /* 390 * Rename temporary mtab to real mtab 391 */ 392 if (rename(tmpname, mnttabname) < 0) { 393 plog(XLOG_ERROR, "rename %s to %s: %m", tmpname, mnttabname); 394 error = 1; 395 goto out; 396 } 397out: 398 if (error) 399 (void) unlink(tmpname); 400} 401 402 403static void 404mtab_stripnl(char *s) 405{ 406 do { 407 s = strchr(s, '\n'); 408 if (s) 409 *s++ = ' '; 410 } while (s); 411} 412 413 414/* 415 * Append a mntent structure to the 416 * current mount table. 417 */ 418void 419write_mntent(mntent_t *mp, const char *mnttabname) 420{ 421 int retries = 0; 422 FILE *mfp; 423enfile: 424 mfp = open_locked_mtab(mnttabname, "a", mp->mnt_dir); 425 if (mfp) { 426 mtab_stripnl(mp->mnt_opts); 427 if (addmntent3(mfp, mp)) 428 plog(XLOG_ERROR, "Couldn't write %s: %m", mnttabname); 429 if (fflush(mfp)) 430 plog(XLOG_ERROR, "Couldn't flush %s: %m", mnttabname); 431 (void) endmntent(mfp); 432 } else { 433 if (errno == ENFILE && retries < NFILE_RETRIES) { 434 sleep(1); 435 goto enfile; 436 } 437 plog(XLOG_ERROR, "setmntent(\"%s\", \"a\"): %m", mnttabname); 438 } 439} 440 441 442static mntent_t * 443mnt_dup(mntent_t *mp) 444{ 445 mntent_t *new_mp = ALLOC(mntent_t); 446 447 new_mp->mnt_fsname = convert(mp->mnt_fsname, '@', ':'); 448 449 new_mp->mnt_dir = strdup(mp->mnt_dir); 450 new_mp->mnt_type = strdup(mp->mnt_type); 451 new_mp->mnt_opts = strdup(mp->mnt_opts); 452 453 new_mp->mnt_freq = mp->mnt_freq; 454 new_mp->mnt_passno = mp->mnt_passno; 455 456 return new_mp; 457} 458 459 460/* 461 * Read a mount table into memory 462 */ 463mntlist * 464read_mtab(char *fs, const char *mnttabname) 465{ 466 mntlist **mpp, *mhp; 467 468 mntent_t *mep; 469 FILE *mfp = open_locked_mtab(mnttabname, "r+", fs); 470 471 if (!mfp) 472 return 0; 473 474 mpp = &mhp; 475 476/* 477 * XXX - In SunOS 4 there is (yet another) memory leak 478 * which loses 1K the first time getmntent is called. 479 * (jsp) 480 */ 481 while (mep = getmntent(mfp)) { 482 /* 483 * Allocate a new slot 484 */ 485 *mpp = ALLOC(struct mntlist); 486 487 /* 488 * Copy the data returned by getmntent 489 */ 490 (*mpp)->mnt = mnt_dup(mep); 491 492 /* 493 * Move to next pointer 494 */ 495 mpp = &(*mpp)->mnext; 496 } 497 *mpp = NULL; 498 499 /* 500 * If we are not updating the mount table then we 501 * can free the resources held here, otherwise they 502 * must be held until the mount table update is complete 503 */ 504 mnt_file = mfp; 505 506 return mhp; 507} 508