1 /* 2 * $Id: mtab_file.c,v 5.2 90/06/23 22:20:54 jsp Rel $ 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.1 (Berkeley) 06/29/90 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 /* 85 * Unlock the mount table 86 */ 87 void unlock_mntlist() 88 { 89 /* 90 * Release file lock, by closing the file 91 */ 92 if (mnt_file) { 93 endmntent(mnt_file); 94 mnt_file = 0; 95 } 96 } 97 98 /* 99 * Write out a mount list 100 */ 101 void rewrite_mtab(mp) 102 mntlist *mp; 103 { 104 FILE *mfp; 105 106 /* 107 * Concoct a temporary name in the same 108 * directory as the target mount table 109 * so that rename() will work. 110 */ 111 char tmpname[64]; 112 int retries; 113 int tmpfd; 114 char *cp; 115 char *mcp = mtab; 116 cp = strrchr(mcp, '/'); 117 if (cp) { 118 bcopy(mcp, tmpname, cp - mcp); 119 tmpname[cp-mcp] = '\0'; 120 } else { 121 plog(XLOG_WARNING, "No '/' in mtab (%s), using \".\" as tmp directory", mtab); 122 tmpname[0] = '.'; tmpname[1] = '\0'; 123 } 124 strcat(tmpname, "/mtabXXXXXX"); 125 mktemp(tmpname); 126 retries = 0; 127 enfile1: 128 if ((tmpfd = open(tmpname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) { 129 if (errno == ENFILE && retries++ < NFILE_RETRIES) { 130 sleep(1); 131 goto enfile1; 132 } 133 plog(XLOG_ERROR, "%s: open: %m", tmpname); 134 return; 135 } 136 if (close(tmpfd) < 0) 137 plog(XLOG_ERROR, "Couldn't close tmp file descriptor: %m"); 138 139 retries = 0; 140 enfile2: 141 mfp = setmntent(tmpname, "w"); 142 if (!mfp) { 143 if (errno == ENFILE && retries++ < NFILE_RETRIES) { 144 sleep(1); 145 goto enfile2; 146 } 147 plog(XLOG_ERROR, "setmntent(\"%s\", \"w\"): %m", tmpname); 148 return; 149 } 150 151 while (mp) { 152 if (mp->mnt) 153 if (addmntent(mfp, mp->mnt)) 154 plog(XLOG_ERROR, "Can't write entry to %s", tmpname); 155 mp = mp->mnext; 156 } 157 158 endmntent(mfp); 159 160 /* 161 * Rename temporary mtab to real mtab 162 */ 163 if (rename(tmpname, mtab) < 0) 164 plog(XLOG_ERROR, "rename %s to %s: %m", tmpname, mtab); 165 } 166 167 #ifdef MTAB_STRIPNL 168 static void mtab_stripnl(s) 169 char *s; 170 { 171 do { 172 s = strchr(s, '\n'); 173 if (s) 174 *s++ = ' '; 175 } while (s); 176 } 177 #endif /* MTAB_STRIPNL */ 178 179 /* 180 * Append a mntent structure to the 181 * current mount table. 182 */ 183 void write_mntent(mp) 184 struct mntent *mp; 185 { 186 int retries = 0; 187 FILE *mfp; 188 enfile: 189 mfp = setmntent(mtab, "a"); 190 if (mfp) { 191 #ifdef MTAB_STRIPNL 192 mtab_stripnl(mp->mnt_opts); 193 #endif /* MTAB_STRIPNL */ 194 if (addmntent(mfp, mp)) 195 plog(XLOG_ERROR, "Couldn't write %s: %m", mtab); 196 endmntent(mfp); 197 } else { 198 if (errno == ENFILE && retries < NFILE_RETRIES) { 199 sleep(1); 200 goto enfile; 201 } 202 plog(XLOG_ERROR, "setmntent(\"%s\", \"a\"): %m", mtab); 203 } 204 } 205 206 #endif /* UPDATE_MTAB */ 207 208 static struct mntent *mnt_dup(mp) 209 struct mntent *mp; 210 { 211 struct mntent *new_mp = ALLOC(mntent); 212 213 new_mp->mnt_fsname = strdup(mp->mnt_fsname); 214 new_mp->mnt_dir = strdup(mp->mnt_dir); 215 new_mp->mnt_type = strdup(mp->mnt_type); 216 new_mp->mnt_opts = strdup(mp->mnt_opts); 217 218 new_mp->mnt_freq = mp->mnt_freq; 219 new_mp->mnt_passno = mp->mnt_passno; 220 221 return new_mp; 222 } 223 224 /* 225 * Read a mount table into memory 226 */ 227 mntlist *read_mtab(fs) 228 char *fs; 229 { 230 mntlist **mpp, *mhp; 231 232 struct mntent *mep; 233 FILE *mfp = 0; 234 235 #ifdef UPDATE_MTAB 236 /* 237 * There is a possible race condition if two processes enter 238 * this routine at the same time. One will be blocked by the 239 * exclusive lock below (or by the shared lock in setmntent) 240 * and by the time the second process has the exclusive lock 241 * it will be on the wrong underlying object. To check for this 242 * the mtab file is stat'ed before and after all the locking 243 * sequence, and if it is a different file then we assume that 244 * it may be the wrong file (only "may", since there is another 245 * race between the initial stat and the setmntent). 246 * 247 * Simpler solutions to this problem are invited... 248 */ 249 int racing = 0; 250 #ifdef MTAB_LOCKING 251 int rc; 252 int retries = 0; 253 struct stat st_before, st_after; 254 #endif /* MTAB_LOCKING */ 255 256 if (mnt_file) { 257 #ifdef DEBUG 258 dlog("Forced close on %s in read_mtab", mtab); 259 #endif /* DEBUG */ 260 endmntent(mnt_file); 261 mnt_file = 0; 262 } 263 264 #ifdef MTAB_LOCKING 265 again: 266 if (mfp) { 267 endmntent(mfp); 268 mfp = 0; 269 } 270 271 clock_valid = 0; 272 if (stat(mtab, &st_before) < 0) { 273 plog(XLOG_ERROR, "%s: stat: %m", mtab); 274 if (errno == ESTALE) { 275 /* happens occasionally */ 276 sleep(1); 277 goto again; 278 } 279 return 0; 280 } 281 #endif /* MTAB_LOCKING */ 282 #endif /* UPDATE_MTAB */ 283 284 eacces: 285 mfp = setmntent(mtab, "r+"); 286 if (!mfp) { 287 /* 288 * Since setmntent locks the descriptor, it 289 * is possible it can fail... so retry if 290 * needed. 291 */ 292 if (errno == EACCES || errno == EAGAIN) { 293 #ifdef DEBUG 294 dlog("Blocked, trying to obtain exclusive mtab lock"); 295 #endif /* DEBUG */ 296 goto eacces; 297 } else if (errno == ENFILE && retries++ < NFILE_RETRIES) { 298 sleep(1); 299 goto eacces; 300 } 301 302 plog(XLOG_ERROR, "setmntent(\"%s\", \"r+\"): %m", mtab); 303 return 0; 304 } 305 306 #ifdef MTAB_LOCKING 307 #ifdef UPDATE_MTAB 308 /* 309 * At this point we have an exclusive lock on the mount list, 310 * but it may be the wrong one so... 311 */ 312 313 /* 314 * Need to get an exclusive lock on the current 315 * mount table until we have a new copy written 316 * out, when the lock is released in free_mntlist. 317 * flock is good enough since the mount table is 318 * not shared between machines. 319 */ 320 do 321 rc = lock(fileno(mfp)); 322 while (rc < 0 && errno == EINTR); 323 if (rc < 0) { 324 plog(XLOG_ERROR, "Couldn't lock %s: %m", mtab); 325 endmntent(mfp); 326 return 0; 327 } 328 /* 329 * Now check whether the mtab file has changed under our feet 330 */ 331 if (stat(mtab, &st_after) < 0) { 332 plog(XLOG_ERROR, "%s: stat", mtab); 333 goto again; 334 } 335 336 if (st_before.st_dev != st_after.st_dev || 337 st_before.st_ino != st_after.st_ino) { 338 if (racing == 0) { 339 /* Sometimes print a warning */ 340 plog(XLOG_WARNING, 341 "Possible mount table race - retrying %s", fs); 342 } 343 racing = (racing+1) & 3; 344 goto again; 345 } 346 #endif /* UPDATE_MTAB */ 347 #endif /* MTAB_LOCKING */ 348 349 mpp = &mhp; 350 351 /* 352 * XXX - In SunOS 4 there is (yet another) memory leak 353 * which loses 1K the first time getmntent is called. 354 * (jsp) 355 */ 356 while (mep = getmntent(mfp)) { 357 /* 358 * Allocate a new slot 359 */ 360 *mpp = ALLOC(mntlist); 361 362 /* 363 * Copy the data returned by getmntent 364 */ 365 (*mpp)->mnt = mnt_dup(mep); 366 367 /* 368 * Move to next pointer 369 */ 370 mpp = &(*mpp)->mnext; 371 } 372 *mpp = 0; 373 374 #ifdef UPDATE_MTAB 375 /* 376 * If we are not updating the mount table then we 377 * can free the resources held here, otherwise they 378 * must be held until the mount table update is complete 379 */ 380 mnt_file = mfp; 381 #else 382 endmntent(mfp); 383 #endif /* UPDATE_MTAB */ 384 385 return mhp; 386 } 387 388 #endif /* READ_MTAB_FROM_FILE */ 389