1 /*
2  * Copyright (c) 1990 Jan-Simon Pendry
3  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
4  * Copyright (c) 1990 The Regents of the University of California.
5  * 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	5.3 (Berkeley) 05/12/91
13  *
14  * $Id: mtab_file.c,v 5.2.1.3 91/05/07 22:19:58 jsp Alpha $
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 
242 	/*
243 	 * Concoct a temporary name in the same
244 	 * directory as the target mount table
245 	 * so that rename() will work.
246 	 */
247 	char tmpname[64];
248 	int retries;
249 	int tmpfd;
250 	char *cp;
251 	char *mcp = mtab;
252 	cp = strrchr(mcp, '/');
253 	if (cp) {
254 		bcopy(mcp, tmpname, cp - mcp);
255 		tmpname[cp-mcp] = '\0';
256 	} else {
257 		plog(XLOG_WARNING, "No '/' in mtab (%s), using \".\" as tmp directory", mtab);
258 		tmpname[0] = '.'; tmpname[1] = '\0';
259 	}
260 	strcat(tmpname, "/mtabXXXXXX");
261 	mktemp(tmpname);
262 	retries = 0;
263 enfile1:
264 	if ((tmpfd = open(tmpname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) {
265 		if (errno == ENFILE && retries++ < NFILE_RETRIES) {
266 			sleep(1);
267 			goto enfile1;
268 		}
269 		plog(XLOG_ERROR, "%s: open: %m", tmpname);
270 		return;
271 	}
272 	if (close(tmpfd) < 0)
273 		plog(XLOG_ERROR, "Couldn't close tmp file descriptor: %m");
274 
275 	retries = 0;
276 enfile2:
277 	mfp = setmntent(tmpname, "w");
278 	if (!mfp) {
279 		if (errno == ENFILE && retries++ < NFILE_RETRIES) {
280 			sleep(1);
281 			goto enfile2;
282 		}
283 		plog(XLOG_ERROR, "setmntent(\"%s\", \"w\"): %m", tmpname);
284 		return;
285 	}
286 
287 	while (mp) {
288 		if (mp->mnt)
289 			if (addmntent(mfp, mp->mnt))
290 				plog(XLOG_ERROR, "Can't write entry to %s", tmpname);
291 		mp = mp->mnext;
292 	}
293 
294 	endmntent(mfp);
295 
296 	/*
297 	 * Rename temporary mtab to real mtab
298 	 */
299 	if (rename(tmpname, mtab) < 0)
300 		plog(XLOG_ERROR, "rename %s to %s: %m", tmpname, mtab);
301 }
302 
303 #ifdef MTAB_STRIPNL
304 static void mtab_stripnl(s)
305 char *s;
306 {
307 	do {
308 		s = strchr(s, '\n');
309 		if (s)
310 			*s++ = ' ';
311 	} while (s);
312 }
313 #endif /* MTAB_STRIPNL */
314 
315 /*
316  * Append a mntent structure to the
317  * current mount table.
318  */
319 void write_mntent(mp)
320 struct mntent *mp;
321 {
322 	int retries = 0;
323 	FILE *mfp;
324 enfile:
325 	mfp = open_locked_mtab(mtab, "a", mp->mnt_dir);
326 	if (mfp) {
327 #ifdef MTAB_STRIPNL
328 		mtab_stripnl(mp->mnt_opts);
329 #endif /* MTAB_STRIPNL */
330 		if (addmntent(mfp, mp))
331 			plog(XLOG_ERROR, "Couldn't write %s: %m", mtab);
332 		endmntent(mfp);
333 	} else {
334 		if (errno == ENFILE && retries < NFILE_RETRIES) {
335 			sleep(1);
336 			goto enfile;
337 		}
338 		plog(XLOG_ERROR, "setmntent(\"%s\", \"a\"): %m", mtab);
339 	}
340 }
341 
342 #endif /* UPDATE_MTAB */
343 
344 static struct mntent *mnt_dup(mp)
345 struct mntent *mp;
346 {
347 	struct mntent *new_mp = ALLOC(mntent);
348 
349 	new_mp->mnt_fsname = strdup(mp->mnt_fsname);
350 	new_mp->mnt_dir = strdup(mp->mnt_dir);
351 	new_mp->mnt_type = strdup(mp->mnt_type);
352 	new_mp->mnt_opts = strdup(mp->mnt_opts);
353 
354 	new_mp->mnt_freq = mp->mnt_freq;
355 	new_mp->mnt_passno = mp->mnt_passno;
356 
357 #ifdef FIXUP_MNTENT_DUP
358 	/*
359 	 * Additional fields get dup'ed here
360 	 */
361 	FIXUP_MNTENT_DUP(new_mp, mp);
362 #endif
363 
364 	return new_mp;
365 }
366 
367 /*
368  * Read a mount table into memory
369  */
370 mntlist *read_mtab(fs)
371 char *fs;
372 {
373 	mntlist **mpp, *mhp;
374 
375 	struct mntent *mep;
376 	FILE *mfp = open_locked_mtab(mtab, "r+", fs);
377 
378 	if (!mfp)
379 		return 0;
380 
381 	mpp = &mhp;
382 
383 /*
384  * XXX - In SunOS 4 there is (yet another) memory leak
385  * which loses 1K the first time getmntent is called.
386  * (jsp)
387  */
388 	while (mep = getmntent(mfp)) {
389 		/*
390 		 * Allocate a new slot
391 		 */
392 		*mpp = ALLOC(mntlist);
393 
394 		/*
395 		 * Copy the data returned by getmntent
396 		 */
397 		(*mpp)->mnt = mnt_dup(mep);
398 
399 		/*
400 		 * Move to next pointer
401 		 */
402 		mpp = &(*mpp)->mnext;
403 	}
404 	*mpp = 0;
405 
406 #ifdef UPDATE_MTAB
407 	/*
408 	 * If we are not updating the mount table then we
409 	 * can free the resources held here, otherwise they
410 	 * must be held until the mount table update is complete
411 	 */
412 	mnt_file = mfp;
413 #else
414 	endmntent(mfp);
415 #endif /* UPDATE_MTAB */
416 
417 	return mhp;
418 }
419 
420 #endif /* READ_MTAB_FROM_FILE */
421