Correct PPTP server firewall rules chain.
[tomato/davidwu.git] / release / src / router / nfs-utils / utils / mount / fstab.c
blobe19e58b0d95fa9d246e6a0034adf5480644bf6c2
1 /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
2 * - added Native Language Support
3 * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
4 * - fixed strerr(errno) in gettext calls
6 * 2006-06-08 Amit Gud <agud@redhat.com>
7 * - Moved code to nfs-utils/support/nfs from util-linux/mount.
8 */
10 #include <errno.h>
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <sys/stat.h>
16 #include <mntent.h>
18 #include "fstab.h"
19 #include "xcommon.h"
20 #include "nfs_mntent.h"
21 #include "nfs_paths.h"
22 #include "nls.h"
24 #define LOCK_TIMEOUT 10
25 #define streq(s, t) (strcmp ((s), (t)) == 0)
26 #define PROC_MOUNTS "/proc/mounts"
28 extern char *progname;
29 extern int verbose;
31 /* Information about mtab. ------------------------------------*/
32 static int have_mtab_info = 0;
33 static int var_mtab_does_not_exist = 0;
34 static int var_mtab_is_a_symlink = 0;
36 static void
37 get_mtab_info(void) {
38 struct stat mtab_stat;
40 if (!have_mtab_info) {
41 if (lstat(MOUNTED, &mtab_stat))
42 var_mtab_does_not_exist = 1;
43 else if (S_ISLNK(mtab_stat.st_mode))
44 var_mtab_is_a_symlink = 1;
45 have_mtab_info = 1;
49 void
50 reset_mtab_info(void) {
51 have_mtab_info = 0;
54 int
55 mtab_does_not_exist(void) {
56 get_mtab_info();
57 return var_mtab_does_not_exist;
60 static int
61 mtab_is_a_symlink(void) {
62 get_mtab_info();
63 return var_mtab_is_a_symlink;
66 int
67 mtab_is_writable() {
68 int fd;
70 /* Should we write to /etc/mtab upon an update?
71 Probably not if it is a symlink to /proc/mounts, since that
72 would create a file /proc/mounts in case the proc filesystem
73 is not mounted. */
74 if (mtab_is_a_symlink())
75 return 0;
77 fd = open(MOUNTED, O_RDWR | O_CREAT, 0644);
78 if (fd >= 0) {
79 close(fd);
80 return 1;
81 } else
82 return 0;
85 /* Contents of mtab and fstab ---------------------------------*/
87 struct mntentchn mounttable;
88 static int got_mtab = 0;
89 struct mntentchn fstab;
90 static int got_fstab = 0;
92 static void read_mounttable(void);
93 static void read_fstab(void);
95 static struct mntentchn *
96 mtab_head(void)
98 if (!got_mtab)
99 read_mounttable();
100 return &mounttable;
103 static struct mntentchn *
104 fstab_head(void)
106 if (!got_fstab)
107 read_fstab();
108 return &fstab;
111 #if 0
112 static void
113 my_free(const void *s) {
114 if (s)
115 free((void *) s);
118 static void
119 discard_mntentchn(struct mntentchn *mc0) {
120 struct mntentchn *mc, *mc1;
122 for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) {
123 mc1 = mc->nxt;
124 my_free(mc->m.mnt_fsname);
125 my_free(mc->m.mnt_dir);
126 my_free(mc->m.mnt_type);
127 my_free(mc->m.mnt_opts);
128 free(mc);
131 #endif
133 static void
134 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
135 struct mntentchn *mc = mc0;
136 struct mntent *mnt;
138 while ((mnt = nfs_getmntent(mfp)) != NULL) {
139 if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) {
140 mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
141 mc->nxt->prev = mc;
142 mc = mc->nxt;
143 mc->m = *mnt;
144 mc->nxt = mc0;
147 mc0->prev = mc;
148 if (ferror(mfp->mntent_fp)) {
149 int errsv = errno;
150 nfs_error(_("warning: error reading %s: %s"),
151 fnam, strerror (errsv));
152 mc0->nxt = mc0->prev = NULL;
154 nfs_endmntent(mfp);
158 * Read /etc/mtab. If that fails, try /proc/mounts.
159 * This produces a linked list. The list head mounttable is a dummy.
160 * Return 0 on success.
162 static void
163 read_mounttable() {
164 mntFILE *mfp;
165 const char *fnam;
166 struct mntentchn *mc = &mounttable;
168 got_mtab = 1;
169 mc->nxt = mc->prev = NULL;
171 fnam = MOUNTED;
172 mfp = nfs_setmntent (fnam, "r");
173 if (mfp == NULL || mfp->mntent_fp == NULL) {
174 int errsv = errno;
175 fnam = PROC_MOUNTS;
176 mfp = nfs_setmntent (fnam, "r");
177 if (mfp == NULL || mfp->mntent_fp == NULL) {
178 nfs_error(_("warning: can't open %s: %s"),
179 MOUNTED, strerror (errsv));
180 return;
182 if (verbose)
183 printf(_("%s: could not open %s; using %s instead\n"),
184 progname, MOUNTED, PROC_MOUNTS);
186 read_mntentchn(mfp, fnam, mc);
189 static void
190 read_fstab()
192 mntFILE *mfp = NULL;
193 const char *fnam;
194 struct mntentchn *mc = &fstab;
196 got_fstab = 1;
197 mc->nxt = mc->prev = NULL;
199 fnam = _PATH_FSTAB;
200 mfp = nfs_setmntent (fnam, "r");
201 if (mfp == NULL || mfp->mntent_fp == NULL) {
202 int errsv = errno;
203 nfs_error(_("warning: can't open %s: %s"),
204 _PATH_FSTAB, strerror (errsv));
205 return;
207 read_mntentchn(mfp, fnam, mc);
211 * Given the directory name NAME, and the place MCPREV we found it last time,
212 * try to find more occurrences.
214 struct mntentchn *
215 getmntdirbackward (const char *name, struct mntentchn *mcprev) {
216 struct mntentchn *mc, *mc0;
218 mc0 = mtab_head();
219 if (!mcprev)
220 mcprev = mc0;
221 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
222 if (streq(mc->m.mnt_dir, name))
223 return mc;
224 return NULL;
228 * Given the device name NAME, and the place MCPREV we found it last time,
229 * try to find more occurrences.
231 struct mntentchn *
232 getmntdevbackward (const char *name, struct mntentchn *mcprev) {
233 struct mntentchn *mc, *mc0;
235 mc0 = mtab_head();
236 if (!mcprev)
237 mcprev = mc0;
238 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
239 if (streq(mc->m.mnt_fsname, name))
240 return mc;
241 return NULL;
244 /* Find the dir FILE in fstab. */
245 struct mntentchn *
246 getfsfile (const char *file)
248 struct mntentchn *mc, *mc0;
250 mc0 = fstab_head();
251 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
252 if (streq(mc->m.mnt_dir, file))
253 return mc;
254 return NULL;
257 /* Find the device SPEC in fstab. */
258 struct mntentchn *
259 getfsspec (const char *spec)
261 struct mntentchn *mc, *mc0;
263 mc0 = fstab_head();
264 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
265 if (streq(mc->m.mnt_fsname, spec))
266 return mc;
267 return NULL;
270 /* Updating mtab ----------------------------------------------*/
272 /* Flag for already existing lock file. */
273 static int we_created_lockfile = 0;
274 static int lockfile_fd = -1;
276 /* Flag to indicate that signals have been set up. */
277 static int signals_have_been_setup = 0;
279 /* Ensure that the lock is released if we are interrupted. */
280 extern char *strsignal(int sig); /* not always in <string.h> */
282 static void
283 handler (int sig) {
284 die(EX_USER, "%s", strsignal(sig));
287 static void
288 setlkw_timeout (int sig) {
289 /* nothing, fcntl will fail anyway */
292 /* Remove lock file. */
293 void
294 unlock_mtab (void) {
295 if (we_created_lockfile) {
296 close(lockfile_fd);
297 lockfile_fd = -1;
298 unlink (MOUNTED_LOCK);
299 we_created_lockfile = 0;
303 /* Create the lock file.
304 The lock file will be removed if we catch a signal or when we exit. */
305 /* The old code here used flock on a lock file /etc/mtab~ and deleted
306 this lock file afterwards. However, as rgooch remarks, that has a
307 race: a second mount may be waiting on the lock and proceed as
308 soon as the lock file is deleted by the first mount, and immediately
309 afterwards a third mount comes, creates a new /etc/mtab~, applies
310 flock to that, and also proceeds, so that the second and third mount
311 now both are scribbling in /etc/mtab.
312 The new code uses a link() instead of a creat(), where we proceed
313 only if it was us that created the lock, and hence we always have
314 to delete the lock afterwards. Now the use of flock() is in principle
315 superfluous, but avoids an arbitrary sleep(). */
317 /* Where does the link point to? Obvious choices are mtab and mtab~~.
318 HJLu points out that the latter leads to races. Right now we use
319 mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
320 #define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
321 #define MOUNTLOCK_LINKTARGET_LTH (sizeof(MOUNTED_LOCK)+20)
323 void
324 lock_mtab (void) {
325 int tries = 100000, i;
326 char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
328 at_die = unlock_mtab;
330 if (!signals_have_been_setup) {
331 int sig = 0;
332 struct sigaction sa;
334 sa.sa_handler = handler;
335 sa.sa_flags = 0;
336 sigfillset (&sa.sa_mask);
338 while (sigismember (&sa.sa_mask, ++sig) != -1
339 && sig != SIGCHLD) {
340 if (sig == SIGALRM)
341 sa.sa_handler = setlkw_timeout;
342 else
343 sa.sa_handler = handler;
344 sigaction (sig, &sa, (struct sigaction *) 0);
346 signals_have_been_setup = 1;
349 sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
351 i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
352 if (i < 0) {
353 int errsv = errno;
354 /* linktargetfile does not exist (as a file)
355 and we cannot create it. Read-only filesystem?
356 Too many files open in the system?
357 Filesystem full? */
358 die (EX_FILEIO, _("can't create lock file %s: %s "
359 "(use -n flag to override)"),
360 linktargetfile, strerror (errsv));
362 close(i);
364 /* Repeat until it was us who made the link */
365 while (!we_created_lockfile) {
366 struct flock flock;
367 int errsv, j;
369 j = link(linktargetfile, MOUNTED_LOCK);
370 errsv = errno;
372 if (j == 0)
373 we_created_lockfile = 1;
375 if (j < 0 && errsv != EEXIST) {
376 (void) unlink(linktargetfile);
377 die (EX_FILEIO, _("can't link lock file %s: %s "
378 "(use -n flag to override)"),
379 MOUNTED_LOCK, strerror (errsv));
382 lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
384 if (lockfile_fd < 0) {
385 int errsv = errno;
386 /* Strange... Maybe the file was just deleted? */
387 if (errno == ENOENT && tries-- > 0) {
388 if (tries % 200 == 0)
389 usleep(30);
390 continue;
392 (void) unlink(linktargetfile);
393 die (EX_FILEIO, _("can't open lock file %s: %s "
394 "(use -n flag to override)"),
395 MOUNTED_LOCK, strerror (errsv));
398 flock.l_type = F_WRLCK;
399 flock.l_whence = SEEK_SET;
400 flock.l_start = 0;
401 flock.l_len = 0;
403 if (j == 0) {
404 /* We made the link. Now claim the lock. */
405 if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
406 if (verbose) {
407 int errsv = errno;
408 nfs_error(_("%s: Can't lock lock file "
409 "%s: %s"), progname,
410 MOUNTED_LOCK,
411 strerror (errsv));
413 /* proceed anyway */
415 (void) unlink(linktargetfile);
416 } else {
417 static int tries = 0;
419 /* Someone else made the link. Wait. */
420 alarm(LOCK_TIMEOUT);
421 if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
422 int errsv = errno;
423 (void) unlink(linktargetfile);
424 die (EX_FILEIO, _("can't lock lock file %s: %s"),
425 MOUNTED_LOCK, (errno == EINTR) ?
426 _("timed out") : strerror (errsv));
428 alarm(0);
429 /* Limit the number of iterations - maybe there
430 still is some old /etc/mtab~ */
431 ++tries;
432 if (tries % 200 == 0)
433 usleep(30);
434 if (tries > 100000) {
435 (void) unlink(linktargetfile);
436 close(lockfile_fd);
437 die (EX_FILEIO, _("Cannot create link %s\n"
438 "Perhaps there is a stale lock file?\n"),
439 MOUNTED_LOCK);
441 close(lockfile_fd);
447 * Update the mtab.
448 * Used by umount with null INSTEAD: remove the last DIR entry.
449 * Used by mount upon a remount: update option part,
450 * and complain if a wrong device or type was given.
451 * [Note that often a remount will be a rw remount of /
452 * where there was no entry before, and we'll have to believe
453 * the values given in INSTEAD.]
456 void
457 update_mtab (const char *dir, struct mntent *instead)
459 mntFILE *mfp, *mftmp;
460 const char *fnam = MOUNTED;
461 struct mntentchn mtabhead; /* dummy */
462 struct mntentchn *mc, *mc0, *absent = NULL;
464 if (mtab_does_not_exist() || !mtab_is_writable())
465 return;
467 lock_mtab();
469 /* having locked mtab, read it again */
470 mc0 = mc = &mtabhead;
471 mc->nxt = mc->prev = NULL;
473 mfp = nfs_setmntent(fnam, "r");
474 if (mfp == NULL || mfp->mntent_fp == NULL) {
475 int errsv = errno;
476 nfs_error (_("cannot open %s (%s) - mtab not updated"),
477 fnam, strerror (errsv));
478 goto leave;
481 read_mntentchn(mfp, fnam, mc);
483 /* find last occurrence of dir */
484 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
485 if (streq(mc->m.mnt_dir, dir))
486 break;
487 if (mc && mc != mc0) {
488 if (instead == NULL) {
489 /* An umount - remove entry */
490 if (mc && mc != mc0) {
491 mc->prev->nxt = mc->nxt;
492 mc->nxt->prev = mc->prev;
493 free(mc);
495 } else {
496 /* A remount */
497 mc->m.mnt_opts = instead->mnt_opts;
499 } else if (instead) {
500 /* not found, add a new entry */
501 absent = xmalloc(sizeof(*absent));
502 absent->m = *instead;
503 absent->nxt = mc0;
504 absent->prev = mc0->prev;
505 mc0->prev = absent;
506 if (mc0->nxt == NULL)
507 mc0->nxt = absent;
510 /* write chain to mtemp */
511 mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
512 if (mftmp == NULL || mftmp->mntent_fp == NULL) {
513 int errsv = errno;
514 nfs_error (_("cannot open %s (%s) - mtab not updated"),
515 MOUNTED_TEMP, strerror (errsv));
516 goto leave;
519 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
520 if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
521 int errsv = errno;
522 die (EX_FILEIO, _("error writing %s: %s"),
523 MOUNTED_TEMP, strerror (errsv));
527 #if 0
528 /* the chain might have strings copied from 'instead',
529 * so we cannot safely free it.
530 * And there is no need anyway because we are going to exit
531 * shortly. So just don't call discard_mntentchn....
533 discard_mntentchn(mc0);
534 #endif
535 if (fchmod (fileno (mftmp->mntent_fp),
536 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
537 int errsv = errno;
538 nfs_error(_("%s: error changing mode of %s: %s"),
539 progname, MOUNTED_TEMP, strerror (errsv));
541 nfs_endmntent (mftmp);
543 { /*
544 * If mount is setuid and some non-root user mounts sth,
545 * then mtab.tmp might get the group of this user. Copy uid/gid
546 * from the present mtab before renaming.
548 struct stat sbuf;
549 if (stat (MOUNTED, &sbuf) == 0)
550 chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid);
553 /* rename mtemp to mtab */
554 if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
555 int errsv = errno;
556 nfs_error(_("%s: can't rename %s to %s: %s\n"),
557 progname, MOUNTED_TEMP, MOUNTED,
558 strerror(errsv));
561 leave:
562 unlock_mtab();