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