date and time created 91/03/17 13:41:25 by pendry
[unix-history] / usr / src / usr.sbin / amd / config / mtab_file.c
CommitLineData
e1a31032
KM
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) %G%
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>
40static 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
58static 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
68again:
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 */
87void 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 */
101void rewrite_mtab(mp)
102mntlist *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;
127enfile1:
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;
140enfile2:
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
168static void mtab_stripnl(s)
169char *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 */
183void write_mntent(mp)
184struct mntent *mp;
185{
186 int retries = 0;
187 FILE *mfp;
188enfile:
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
208static struct mntent *mnt_dup(mp)
209struct 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 */
227mntlist *read_mtab(fs)
228char *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
265again:
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
284eacces:
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 */