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.
20 #include "nfs_mntent.h"
21 #include "nfs_paths.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
;
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;
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;
50 reset_mtab_info(void) {
55 mtab_does_not_exist(void) {
57 return var_mtab_does_not_exist
;
61 mtab_is_a_symlink(void) {
63 return var_mtab_is_a_symlink
;
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
74 if (mtab_is_a_symlink())
77 fd
= open(MOUNTED
, O_RDWR
| O_CREAT
, 0644);
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
*
103 static struct mntentchn
*
113 my_free(const void *s
) {
119 discard_mntentchn(struct mntentchn
*mc0
) {
120 struct mntentchn
*mc
, *mc1
;
122 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc1
) {
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
);
134 read_mntentchn(mntFILE
*mfp
, const char *fnam
, struct mntentchn
*mc0
) {
135 struct mntentchn
*mc
= mc0
;
138 while ((mnt
= nfs_getmntent(mfp
)) != NULL
) {
139 if (!streq(mnt
->mnt_type
, MNTTYPE_IGNORE
)) {
140 mc
->nxt
= (struct mntentchn
*) xmalloc(sizeof(*mc
));
148 if (ferror(mfp
->mntent_fp
)) {
150 nfs_error(_("warning: error reading %s: %s"),
151 fnam
, strerror (errsv
));
152 mc0
->nxt
= mc0
->prev
= NULL
;
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.
166 struct mntentchn
*mc
= &mounttable
;
169 mc
->nxt
= mc
->prev
= NULL
;
172 mfp
= nfs_setmntent (fnam
, "r");
173 if (mfp
== NULL
|| mfp
->mntent_fp
== NULL
) {
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
));
183 printf(_("%s: could not open %s; using %s instead\n"),
184 progname
, MOUNTED
, PROC_MOUNTS
);
186 read_mntentchn(mfp
, fnam
, mc
);
194 struct mntentchn
*mc
= &fstab
;
197 mc
->nxt
= mc
->prev
= NULL
;
200 mfp
= nfs_setmntent (fnam
, "r");
201 if (mfp
== NULL
|| mfp
->mntent_fp
== NULL
) {
203 nfs_error(_("warning: can't open %s: %s"),
204 _PATH_FSTAB
, strerror (errsv
));
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.
215 getmntdirbackward (const char *name
, struct mntentchn
*mcprev
) {
216 struct mntentchn
*mc
, *mc0
;
221 for (mc
= mcprev
->prev
; mc
&& mc
!= mc0
; mc
= mc
->prev
)
222 if (streq(mc
->m
.mnt_dir
, name
))
228 * Given the device name NAME, and the place MCPREV we found it last time,
229 * try to find more occurrences.
232 getmntdevbackward (const char *name
, struct mntentchn
*mcprev
) {
233 struct mntentchn
*mc
, *mc0
;
238 for (mc
= mcprev
->prev
; mc
&& mc
!= mc0
; mc
= mc
->prev
)
239 if (streq(mc
->m
.mnt_fsname
, name
))
244 /* Find the dir FILE in fstab. */
246 getfsfile (const char *file
)
248 struct mntentchn
*mc
, *mc0
;
251 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc
->nxt
)
252 if (streq(mc
->m
.mnt_dir
, file
))
257 /* Find the device SPEC in fstab. */
259 getfsspec (const char *spec
)
261 struct mntentchn
*mc
, *mc0
;
264 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc
->nxt
)
265 if (streq(mc
->m
.mnt_fsname
, spec
))
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> */
284 die(EX_USER
, "%s", strsignal(sig
));
288 setlkw_timeout (int sig
) {
289 /* nothing, fcntl will fail anyway */
292 /* Remove lock file. */
295 if (we_created_lockfile
) {
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)
325 int tries
= 100000, i
;
326 char linktargetfile
[MOUNTLOCK_LINKTARGET_LTH
];
328 at_die
= unlock_mtab
;
330 if (!signals_have_been_setup
) {
334 sa
.sa_handler
= handler
;
336 sigfillset (&sa
.sa_mask
);
338 while (sigismember (&sa
.sa_mask
, ++sig
) != -1
341 sa
.sa_handler
= setlkw_timeout
;
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);
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?
358 die (EX_FILEIO
, _("can't create lock file %s: %s "
359 "(use -n flag to override)"),
360 linktargetfile
, strerror (errsv
));
364 /* Repeat until it was us who made the link */
365 while (!we_created_lockfile
) {
369 j
= link(linktargetfile
, MOUNTED_LOCK
);
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) {
386 /* Strange... Maybe the file was just deleted? */
387 if (errno
== ENOENT
&& tries
-- > 0) {
388 if (tries
% 200 == 0)
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
;
404 /* We made the link. Now claim the lock. */
405 if (fcntl (lockfile_fd
, F_SETLK
, &flock
) == -1) {
408 nfs_error(_("%s: Can't lock lock file "
415 (void) unlink(linktargetfile
);
417 static int tries
= 0;
419 /* Someone else made the link. Wait. */
421 if (fcntl (lockfile_fd
, F_SETLKW
, &flock
) == -1) {
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
));
429 /* Limit the number of iterations - maybe there
430 still is some old /etc/mtab~ */
432 if (tries
% 200 == 0)
434 if (tries
> 100000) {
435 (void) unlink(linktargetfile
);
437 die (EX_FILEIO
, _("Cannot create link %s\n"
438 "Perhaps there is a stale lock file?\n"),
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.]
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())
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
) {
476 nfs_error (_("cannot open %s (%s) - mtab not updated"),
477 fnam
, strerror (errsv
));
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
))
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
;
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
;
504 absent
->prev
= mc0
->prev
;
506 if (mc0
->nxt
== NULL
)
510 /* write chain to mtemp */
511 mftmp
= nfs_setmntent (MOUNTED_TEMP
, "w");
512 if (mftmp
== NULL
|| mftmp
->mntent_fp
== NULL
) {
514 nfs_error (_("cannot open %s (%s) - mtab not updated"),
515 MOUNTED_TEMP
, strerror (errsv
));
519 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc
->nxt
) {
520 if (nfs_addmntent(mftmp
, &(mc
->m
)) == 1) {
522 die (EX_FILEIO
, _("error writing %s: %s"),
523 MOUNTED_TEMP
, strerror (errsv
));
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
);
535 if (fchmod (fileno (mftmp
->mntent_fp
),
536 S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) < 0) {
538 nfs_error(_("%s: error changing mode of %s: %s"),
539 progname
, MOUNTED_TEMP
, strerror (errsv
));
541 nfs_endmntent (mftmp
);
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.
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) {
556 nfs_error(_("%s: can't rename %s to %s: %s\n"),
557 progname
, MOUNTED_TEMP
, MOUNTED
,