1 /* 2 * Copyright (c) 1990 Jan-Simon Pendry 3 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 4 * Copyright (c) 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Jan-Simon Pendry at Imperial College, London. 9 * 10 * %sccs.include.redist.c% 11 * 12 * @(#)mtab_file.c 8.1 (Berkeley) 06/06/93 13 * 14 * $Id: mtab_file.c,v 5.2.2.1 1992/02/09 15:10:42 jsp beta $ 15 * 16 */ 17 18 #include "am.h" 19 20 #ifdef READ_MTAB_FROM_FILE 21 22 #ifdef USE_FCNTL 23 #include <fcntl.h> 24 #else 25 #include <sys/file.h> 26 #endif /* USE_FCNTL */ 27 28 #ifdef UPDATE_MTAB 29 30 /* 31 * Do strict /etc/mtab locking 32 */ 33 #define MTAB_LOCKING 34 35 /* 36 * Firewall mtab entries 37 */ 38 #define MTAB_STRIPNL 39 40 #include <sys/stat.h> 41 static FILE *mnt_file; 42 43 /* 44 * If the system is being trashed by something, then 45 * opening mtab may fail with ENFILE. So, go to sleep 46 * for a second and try again. (Yes - this has happened to me.) 47 * 48 * Note that this *may* block the automounter, oh well. 49 * If we get to this state then things are badly wrong anyway... 50 * 51 * Give the system 10 seconds to recover but then give up. 52 * Hopefully something else will exit and free up some file 53 * table slots in that time. 54 */ 55 #define NFILE_RETRIES 10 /* seconds */ 56 57 #ifdef MTAB_LOCKING 58 #ifdef LOCK_FCNTL 59 static int lock(fd) 60 { 61 int rc; 62 struct flock lk; 63 64 lk.l_type = F_WRLCK; 65 lk.l_whence = 0; 66 lk.l_start = 0; 67 lk.l_len = 0; 68 69 again: 70 rc = fcntl(fd, F_SETLKW, (caddr_t) &lk); 71 if (rc < 0 && (errno == EACCES || errno == EAGAIN)) { 72 #ifdef DEBUG 73 dlog("Blocked, trying to obtain exclusive mtab lock"); 74 #endif /* DEBUG */ 75 sleep(1); 76 goto again; 77 } 78 return rc; 79 } 80 #else 81 #define lock(fd) (flock((fd), LOCK_EX)) 82 #endif /* LOCK_FCNTL */ 83 #endif /* MTAB_LOCKING */ 84 85 static FILE *open_locked_mtab(mtab_file, mode, fs) 86 char *mtab_file; 87 char *mode; 88 char *fs; 89 { 90 FILE *mfp = 0; 91 92 #ifdef UPDATE_MTAB 93 /* 94 * There is a possible race condition if two processes enter 95 * this routine at the same time. One will be blocked by the 96 * exclusive lock below (or by the shared lock in setmntent) 97 * and by the time the second process has the exclusive lock 98 * it will be on the wrong underlying object. To check for this 99 * the mtab file is stat'ed before and after all the locking 100 * sequence, and if it is a different file then we assume that 101 * it may be the wrong file (only "may", since there is another 102 * race between the initial stat and the setmntent). 103 * 104 * Simpler solutions to this problem are invited... 105 */ 106 int racing = 2; 107 #ifdef MTAB_LOCKING 108 int rc; 109 int retries = 0; 110 struct stat st_before, st_after; 111 #endif /* MTAB_LOCKING */ 112 113 if (mnt_file) { 114 #ifdef DEBUG 115 dlog("Forced close on %s in read_mtab", mtab_file); 116 #endif /* DEBUG */ 117 endmntent(mnt_file); 118 mnt_file = 0; 119 } 120 121 #ifdef MTAB_LOCKING 122 again: 123 if (mfp) { 124 endmntent(mfp); 125 mfp = 0; 126 } 127 128 clock_valid = 0; 129 if (stat(mtab_file, &st_before) < 0) { 130 plog(XLOG_ERROR, "%s: stat: %m", mtab_file); 131 if (errno == ESTALE) { 132 /* happens occasionally */ 133 sleep(1); 134 goto again; 135 } 136 return 0; 137 } 138 #endif /* MTAB_LOCKING */ 139 #endif /* UPDATE_MTAB */ 140 141 eacces: 142 mfp = setmntent(mtab_file, mode); 143 if (!mfp) { 144 /* 145 * Since setmntent locks the descriptor, it 146 * is possible it can fail... so retry if 147 * needed. 148 */ 149 if (errno == EACCES || errno == EAGAIN) { 150 #ifdef DEBUG 151 dlog("Blocked, trying to obtain exclusive mtab lock"); 152 #endif /* DEBUG */ 153 goto eacces; 154 } else if (errno == ENFILE && retries++ < NFILE_RETRIES) { 155 sleep(1); 156 goto eacces; 157 } 158 159 plog(XLOG_ERROR, "setmntent(\"%s\", \"%s\"): %m", mtab_file, mode); 160 return 0; 161 } 162 163 #ifdef MTAB_LOCKING 164 #ifdef UPDATE_MTAB 165 /* 166 * At this point we have an exclusive lock on the mount list, 167 * but it may be the wrong one so... 168 */ 169 170 /* 171 * Need to get an exclusive lock on the current 172 * mount table until we have a new copy written 173 * out, when the lock is released in free_mntlist. 174 * flock is good enough since the mount table is 175 * not shared between machines. 176 */ 177 do 178 rc = lock(fileno(mfp)); 179 while (rc < 0 && errno == EINTR); 180 if (rc < 0) { 181 plog(XLOG_ERROR, "Couldn't lock %s: %m", mtab_file); 182 endmntent(mfp); 183 return 0; 184 } 185 /* 186 * Now check whether the mtab file has changed under our feet 187 */ 188 if (stat(mtab_file, &st_after) < 0) { 189 plog(XLOG_ERROR, "%s: stat", mtab_file); 190 goto again; 191 } 192 193 if (st_before.st_dev != st_after.st_dev || 194 st_before.st_ino != st_after.st_ino) { 195 struct timeval tv; 196 if (racing == 0) { 197 /* Sometimes print a warning */ 198 plog(XLOG_WARNING, 199 "Possible mount table race - retrying %s", fs); 200 } 201 racing = (racing+1) & 3; 202 /* 203 * Take a nap. From: Doug Kingston <dpk@morgan.com> 204 */ 205 tv.tv_sec = 0; 206 tv.tv_usec = (mypid & 0x07) << 17; 207 if (tv.tv_usec) 208 if (select(0, (voidp) 0, (voidp) 0, (voidp) 0, &tv) < 0) 209 plog(XLOG_WARNING, "mtab nap failed: %m"); 210 211 goto again; 212 } 213 #endif /* UPDATE_MTAB */ 214 #endif /* MTAB_LOCKING */ 215 216 return mfp; 217 } 218 219 /* 220 * Unlock the mount table 221 */ 222 void unlock_mntlist P((void)); 223 void unlock_mntlist() 224 { 225 /* 226 * Release file lock, by closing the file 227 */ 228 if (mnt_file) { 229 endmntent(mnt_file); 230 mnt_file = 0; 231 } 232 } 233 234 /* 235 * Write out a mount list 236 */ 237 void rewrite_mtab(mp) 238 mntlist *mp; 239 { 240 FILE *mfp; 241 int error = 0; 242 243 /* 244 * Concoct a temporary name in the same 245 * directory as the target mount table 246 * so that rename() will work. 247 */ 248 char tmpname[64]; 249 int retries; 250 int tmpfd; 251 char *cp; 252 char *mcp = mtab; 253 cp = strrchr(mcp, '/'); 254 if (cp) { 255 bcopy(mcp, tmpname, cp - mcp); 256 tmpname[cp-mcp] = '\0'; 257 } else { 258 plog(XLOG_WARNING, "No '/' in mtab (%s), using \".\" as tmp directory", mtab); 259 tmpname[0] = '.'; tmpname[1] = '\0'; 260 } 261 strcat(tmpname, "/mtabXXXXXX"); 262 mktemp(tmpname); 263 retries = 0; 264 enfile1: 265 if ((tmpfd = open(tmpname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) { 266 if (errno == ENFILE && retries++ < NFILE_RETRIES) { 267 sleep(1); 268 goto enfile1; 269 } 270 plog(XLOG_ERROR, "%s: open: %m", tmpname); 271 return; 272 } 273 if (close(tmpfd) < 0) 274 plog(XLOG_ERROR, "Couldn't close tmp file descriptor: %m"); 275 276 retries = 0; 277 enfile2: 278 mfp = setmntent(tmpname, "w"); 279 if (!mfp) { 280 if (errno == ENFILE && retries++ < NFILE_RETRIES) { 281 sleep(1); 282 goto enfile2; 283 } 284 plog(XLOG_ERROR, "setmntent(\"%s\", \"w\"): %m", tmpname); 285 error = 1; 286 goto out; 287 } 288 289 while (mp) { 290 if (mp->mnt) { 291 if (addmntent(mfp, mp->mnt)) { 292 plog(XLOG_ERROR, "Can't write entry to %s", tmpname); 293 error = 1; 294 goto out; 295 } 296 } 297 mp = mp->mnext; 298 } 299 300 /* 301 * SunOS 4.1 manuals say that the return code from entmntent() 302 * is always 1 and to treat as a void. That means we need to 303 * call fflush() to make sure the new mtab file got written. 304 */ 305 if (fflush(mfp)) { 306 plog(XLOG_ERROR, "flush new mtab file: %m"); 307 error = 1; 308 goto out; 309 } 310 311 (void) endmntent(mfp); 312 313 /* 314 * Rename temporary mtab to real mtab 315 */ 316 if (rename(tmpname, mtab) < 0) { 317 plog(XLOG_ERROR, "rename %s to %s: %m", tmpname, mtab); 318 error = 1; 319 goto out; 320 } 321 322 out: 323 if (error) 324 (void) unlink(tmpname); 325 } 326 327 #ifdef MTAB_STRIPNL 328 static void mtab_stripnl(s) 329 char *s; 330 { 331 do { 332 s = strchr(s, '\n'); 333 if (s) 334 *s++ = ' '; 335 } while (s); 336 } 337 #endif /* MTAB_STRIPNL */ 338 339 /* 340 * Append a mntent structure to the 341 * current mount table. 342 */ 343 void write_mntent(mp) 344 struct mntent *mp; 345 { 346 int retries = 0; 347 FILE *mfp; 348 enfile: 349 mfp = open_locked_mtab(mtab, "a", mp->mnt_dir); 350 if (mfp) { 351 #ifdef MTAB_STRIPNL 352 mtab_stripnl(mp->mnt_opts); 353 #endif /* MTAB_STRIPNL */ 354 if (addmntent(mfp, mp)) 355 plog(XLOG_ERROR, "Couldn't write %s: %m", mtab); 356 if (fflush(mfp)) 357 plog(XLOG_ERROR, "Couldn't flush %s: %m", mtab); 358 (void) endmntent(mfp); 359 } else { 360 if (errno == ENFILE && retries < NFILE_RETRIES) { 361 sleep(1); 362 goto enfile; 363 } 364 plog(XLOG_ERROR, "setmntent(\"%s\", \"a\"): %m", mtab); 365 } 366 } 367 368 #endif /* UPDATE_MTAB */ 369 370 static struct mntent *mnt_dup(mp) 371 struct mntent *mp; 372 { 373 struct mntent *new_mp = ALLOC(mntent); 374 375 new_mp->mnt_fsname = strdup(mp->mnt_fsname); 376 new_mp->mnt_dir = strdup(mp->mnt_dir); 377 new_mp->mnt_type = strdup(mp->mnt_type); 378 new_mp->mnt_opts = strdup(mp->mnt_opts); 379 380 new_mp->mnt_freq = mp->mnt_freq; 381 new_mp->mnt_passno = mp->mnt_passno; 382 383 #ifdef FIXUP_MNTENT_DUP 384 /* 385 * Additional fields get dup'ed here 386 */ 387 FIXUP_MNTENT_DUP(new_mp, mp); 388 #endif 389 390 return new_mp; 391 } 392 393 /* 394 * Read a mount table into memory 395 */ 396 mntlist *read_mtab(fs) 397 char *fs; 398 { 399 mntlist **mpp, *mhp; 400 401 struct mntent *mep; 402 FILE *mfp = open_locked_mtab(mtab, "r+", fs); 403 404 if (!mfp) 405 return 0; 406 407 mpp = &mhp; 408 409 /* 410 * XXX - In SunOS 4 there is (yet another) memory leak 411 * which loses 1K the first time getmntent is called. 412 * (jsp) 413 */ 414 while (mep = getmntent(mfp)) { 415 /* 416 * Allocate a new slot 417 */ 418 *mpp = ALLOC(mntlist); 419 420 /* 421 * Copy the data returned by getmntent 422 */ 423 (*mpp)->mnt = mnt_dup(mep); 424 425 /* 426 * Move to next pointer 427 */ 428 mpp = &(*mpp)->mnext; 429 } 430 *mpp = 0; 431 432 #ifdef UPDATE_MTAB 433 /* 434 * If we are not updating the mount table then we 435 * can free the resources held here, otherwise they 436 * must be held until the mount table update is complete 437 */ 438 mnt_file = mfp; 439 #else 440 endmntent(mfp); 441 #endif /* UPDATE_MTAB */ 442 443 return mhp; 444 } 445 446 #endif /* READ_MTAB_FROM_FILE */ 447