]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - mount/fstab.c
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
14 #include "sundries.h" /* for xmalloc() etc */
15 #include "mount_blkid.h"
19 #define streq(s, t) (strcmp ((s), (t)) == 0)
21 #define PROC_MOUNTS "/proc/mounts"
24 /* Information about mtab. ------------------------------------*/
25 static int have_mtab_info
= 0;
26 static int var_mtab_does_not_exist
= 0;
27 static int var_mtab_is_a_symlink
= 0;
31 struct stat mtab_stat
;
33 if (!have_mtab_info
) {
34 if (lstat(MOUNTED
, &mtab_stat
))
35 var_mtab_does_not_exist
= 1;
36 else if (S_ISLNK(mtab_stat
.st_mode
))
37 var_mtab_is_a_symlink
= 1;
43 mtab_does_not_exist(void) {
45 return var_mtab_does_not_exist
;
49 mtab_is_a_symlink(void) {
51 return var_mtab_is_a_symlink
;
58 /* Should we write to /etc/mtab upon an update?
59 Probably not if it is a symlink to /proc/mounts, since that
60 would create a file /proc/mounts in case the proc filesystem
62 if (mtab_is_a_symlink())
66 int fd
= open(MOUNTED
, O_RDWR
| O_CREAT
, 0644);
76 /* Contents of mtab and fstab ---------------------------------*/
78 struct mntentchn mounttable
, fstab
;
79 static int got_mtab
= 0;
80 static int got_fstab
= 0;
82 static void read_mounttable(void), read_fstab(void);
99 my_free(const void *s
) {
105 discard_mntentchn(struct mntentchn
*mc0
) {
106 struct mntentchn
*mc
, *mc1
;
108 for (mc
= mc0
->nxt
; mc
!= mc0
; mc
= mc1
) {
110 my_free(mc
->m
.mnt_fsname
);
111 my_free(mc
->m
.mnt_dir
);
112 my_free(mc
->m
.mnt_type
);
113 my_free(mc
->m
.mnt_opts
);
119 read_mntentchn(mntFILE
*mfp
, const char *fnam
, struct mntentchn
*mc0
) {
120 struct mntentchn
*mc
= mc0
;
121 struct my_mntent
*mnt
;
123 while ((mnt
= my_getmntent(mfp
)) != NULL
) {
124 if (!streq(mnt
->mnt_type
, MNTTYPE_IGNORE
)) {
125 mc
->nxt
= (struct mntentchn
*) xmalloc(sizeof(*mc
));
133 if (ferror(mfp
->mntent_fp
)) {
135 error(_("warning: error reading %s: %s"),
136 fnam
, strerror (errsv
));
137 mc0
->nxt
= mc0
->prev
= NULL
;
143 * Read /etc/mtab. If that fails, try /proc/mounts.
144 * This produces a linked list. The list head mounttable is a dummy.
145 * Return 0 on success.
151 struct mntentchn
*mc
= &mounttable
;
154 mc
->nxt
= mc
->prev
= NULL
;
157 mfp
= my_setmntent (fnam
, "r");
158 if (mfp
== NULL
|| mfp
->mntent_fp
== NULL
) {
161 mfp
= my_setmntent (fnam
, "r");
162 if (mfp
== NULL
|| mfp
->mntent_fp
== NULL
) {
163 error(_("warning: can't open %s: %s"),
164 MOUNTED
, strerror (errsv
));
168 printf (_("mount: could not open %s - "
169 "using %s instead\n"),
170 MOUNTED
, PROC_MOUNTS
);
172 read_mntentchn(mfp
, fnam
, mc
);
179 struct mntentchn
*mc
= &fstab
;
182 mc
->nxt
= mc
->prev
= NULL
;
185 mfp
= my_setmntent (fnam
, "r");
186 if (mfp
== NULL
|| mfp
->mntent_fp
== NULL
) {
188 error(_("warning: can't open %s: %s"),
189 _PATH_FSTAB
, strerror (errsv
));
192 read_mntentchn(mfp
, fnam
, mc
);
196 /* Given the name NAME, try to find it in mtab. */
198 getmntfile (const char *name
) {
199 struct mntentchn
*mc
, *mc0
;
202 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc
->nxt
)
203 if (streq(mc
->m
.mnt_dir
, name
) ||
204 streq(mc
->m
.mnt_fsname
, name
))
210 * Given the directory name NAME, and the place MCPREV we found it last time,
211 * try to find more occurrences.
214 getmntdirbackward (const char *name
, struct mntentchn
*mcprev
) {
215 struct mntentchn
*mc
, *mc0
;
220 for (mc
= mcprev
->prev
; mc
&& mc
!= mc0
; mc
= mc
->prev
)
221 if (streq(mc
->m
.mnt_dir
, name
))
227 * Given the device name NAME, and the place MCPREV we found it last time,
228 * try to find more occurrences.
231 getmntdevbackward (const char *name
, struct mntentchn
*mcprev
) {
232 struct mntentchn
*mc
, *mc0
;
237 for (mc
= mcprev
->prev
; mc
&& mc
!= mc0
; mc
= mc
->prev
)
238 if (streq(mc
->m
.mnt_fsname
, name
))
244 * Given the name NAME, check that it occurs precisely once as dir or dev.
247 is_mounted_once(const char *name
) {
248 struct mntentchn
*mc
, *mc0
;
252 for (mc
= mc0
->prev
; mc
&& mc
!= mc0
; mc
= mc
->prev
)
253 if (streq(mc
->m
.mnt_dir
, name
) ||
254 streq(mc
->m
.mnt_fsname
, name
))
259 /* Given the name FILE, try to find the option "loop=FILE" in mtab. */
261 getmntoptfile (const char *file
) {
262 struct mntentchn
*mc
, *mc0
;
263 const char *opts
, *s
;
272 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc
->nxt
)
273 if ((opts
= mc
->m
.mnt_opts
) != NULL
274 && (s
= strstr(opts
, "loop="))
275 && !strncmp(s
+5, file
, l
)
276 && (s
== opts
|| s
[-1] == ',')
277 && (s
[l
+5] == 0 || s
[l
+5] == ','))
283 has_label(const char *device
, const char *label
) {
284 const char *devlabel
;
287 devlabel
= mount_get_volume_label_by_spec(device
);
288 ret
= !strcmp(label
, devlabel
);
289 /* free(devlabel); */
294 has_uuid(const char *device
, const char *uuid
){
298 devuuid
= mount_get_devname_by_uuid(device
);
299 ret
= !strcmp(uuid
, devuuid
);
304 /* Find the entry (SPEC,FILE) in fstab */
306 getfsspecfile (const char *spec
, const char *file
) {
307 struct mntentchn
*mc
, *mc0
;
311 /* first attempt: names occur precisely as given */
312 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc
->nxt
)
313 if (streq(mc
->m
.mnt_dir
, file
) &&
314 streq(mc
->m
.mnt_fsname
, spec
))
317 /* second attempt: names found after symlink resolution */
318 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc
->nxt
)
319 if ((streq(mc
->m
.mnt_dir
, file
) ||
320 streq(canonicalize(mc
->m
.mnt_dir
), file
))
321 && (streq(mc
->m
.mnt_fsname
, spec
) ||
322 streq(canonicalize(mc
->m
.mnt_fsname
), spec
)))
325 /* third attempt: names found after LABEL= or UUID= resolution */
326 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc
->nxt
) {
327 if (!strncmp (mc
->m
.mnt_fsname
, "LABEL=", 6) &&
328 (streq(mc
->m
.mnt_dir
, file
) ||
329 streq(canonicalize(mc
->m
.mnt_dir
), file
))) {
330 if (has_label(spec
, mc
->m
.mnt_fsname
+6))
333 if (!strncmp (mc
->m
.mnt_fsname
, "UUID=", 5) &&
334 (streq(mc
->m
.mnt_dir
, file
) ||
335 streq(canonicalize(mc
->m
.mnt_dir
), file
))) {
336 if (has_uuid(spec
, mc
->m
.mnt_fsname
+5))
343 /* Find the dir FILE in fstab. */
345 getfsfile (const char *file
) {
346 struct mntentchn
*mc
, *mc0
;
349 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc
->nxt
)
350 if (streq(mc
->m
.mnt_dir
, file
))
355 /* Find the device SPEC in fstab. */
357 getfsspec (const char *spec
) {
358 struct mntentchn
*mc
, *mc0
;
361 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc
->nxt
)
362 if (streq(mc
->m
.mnt_fsname
, spec
))
367 /* Find the uuid UUID in fstab. */
369 getfsuuidspec (const char *uuid
) {
370 struct mntentchn
*mc
, *mc0
;
373 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc
->nxt
)
374 if (strncmp (mc
->m
.mnt_fsname
, "UUID=", 5) == 0
375 && streq(mc
->m
.mnt_fsname
+ 5, uuid
))
380 /* Find the label LABEL in fstab. */
382 getfsvolspec (const char *label
) {
383 struct mntentchn
*mc
, *mc0
;
386 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc
->nxt
)
387 if (strncmp (mc
->m
.mnt_fsname
, "LABEL=", 6) == 0
388 && streq(mc
->m
.mnt_fsname
+ 6, label
))
393 /* Updating mtab ----------------------------------------------*/
395 /* Flag for already existing lock file. */
396 static int we_created_lockfile
= 0;
398 /* Flag to indicate that signals have been set up. */
399 static int signals_have_been_setup
= 0;
401 /* Ensure that the lock is released if we are interrupted. */
404 die (EX_USER
, "%s", sys_siglist
[sig
]);
408 setlkw_timeout (int sig
) {
409 /* nothing, fcntl will fail anyway */
412 /* Create the lock file.
413 The lock file will be removed if we catch a signal or when we exit. */
414 /* The old code here used flock on a lock file /etc/mtab~ and deleted
415 this lock file afterwards. However, as rgooch remarks, that has a
416 race: a second mount may be waiting on the lock and proceed as
417 soon as the lock file is deleted by the first mount, and immediately
418 afterwards a third mount comes, creates a new /etc/mtab~, applies
419 flock to that, and also proceeds, so that the second and third mount
420 now both are scribbling in /etc/mtab.
421 The new code uses a link() instead of a creat(), where we proceed
422 only if it was us that created the lock, and hence we always have
423 to delete the lock afterwards. Now the use of flock() is in principle
424 superfluous, but avoids an arbitrary sleep(). */
426 /* Where does the link point to? Obvious choices are mtab and mtab~~.
427 HJLu points out that the latter leads to races. Right now we use
428 mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
429 #define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
430 #define MOUNTLOCK_LINKTARGET_LTH (sizeof(MOUNTED_LOCK)+20)
435 char linktargetfile
[MOUNTLOCK_LINKTARGET_LTH
];
437 if (!signals_have_been_setup
) {
441 sa
.sa_handler
= handler
;
443 sigfillset (&sa
.sa_mask
);
445 while (sigismember (&sa
.sa_mask
, ++sig
) != -1
448 sa
.sa_handler
= setlkw_timeout
;
450 sa
.sa_handler
= handler
;
451 sigaction (sig
, &sa
, (struct sigaction
*) 0);
453 signals_have_been_setup
= 1;
456 sprintf(linktargetfile
, MOUNTLOCK_LINKTARGET
, getpid ());
458 /* Repeat until it was us who made the link */
459 while (!we_created_lockfile
) {
463 i
= open (linktargetfile
, O_WRONLY
|O_CREAT
, 0);
466 /* linktargetfile does not exist (as a file)
467 and we cannot create it. Read-only filesystem?
468 Too many files open in the system?
470 die (EX_FILEIO
, _("can't create lock file %s: %s "
471 "(use -n flag to override)"),
472 linktargetfile
, strerror (errsv
));
476 j
= link(linktargetfile
, MOUNTED_LOCK
);
479 (void) unlink(linktargetfile
);
482 we_created_lockfile
= 1;
484 if (j
< 0 && errsv
!= EEXIST
) {
485 die (EX_FILEIO
, _("can't link lock file %s: %s "
486 "(use -n flag to override)"),
487 MOUNTED_LOCK
, strerror (errsv
));
490 fd
= open (MOUNTED_LOCK
, O_WRONLY
);
494 /* Strange... Maybe the file was just deleted? */
495 if (errno
== ENOENT
&& tries
-- > 0)
497 die (EX_FILEIO
, _("can't open lock file %s: %s "
498 "(use -n flag to override)"),
499 MOUNTED_LOCK
, strerror (errsv
));
502 flock
.l_type
= F_WRLCK
;
503 flock
.l_whence
= SEEK_SET
;
508 /* We made the link. Now claim the lock. */
509 if (fcntl (fd
, F_SETLK
, &flock
) == -1) {
512 printf(_("Can't lock lock file %s: %s\n"),
513 MOUNTED_LOCK
, strerror (errsv
));
518 static int tries
= 0;
520 /* Someone else made the link. Wait. */
522 if (fcntl (fd
, F_SETLKW
, &flock
) == -1) {
524 die (EX_FILEIO
, _("can't lock lock file %s: %s"),
525 MOUNTED_LOCK
, (errno
== EINTR
) ?
526 _("timed out") : strerror (errsv
));
529 /* Limit the number of iterations - maybe there
530 still is some old /etc/mtab~ */
533 die (EX_FILEIO
, _("Cannot create link %s\n"
534 "Perhaps there is a stale lock file?\n"),
544 /* Remove lock file. */
547 if (we_created_lockfile
) {
548 unlink (MOUNTED_LOCK
);
549 we_created_lockfile
= 0;
555 * Used by umount with null INSTEAD: remove the last DIR entry.
556 * Used by mount upon a remount: update option part,
557 * and complain if a wrong device or type was given.
558 * [Note that often a remount will be a rw remount of /
559 * where there was no entry before, and we'll have to believe
560 * the values given in INSTEAD.]
564 update_mtab (const char *dir
, struct my_mntent
*instead
) {
565 mntFILE
*mfp
, *mftmp
;
566 const char *fnam
= MOUNTED
;
567 struct mntentchn mtabhead
; /* dummy */
568 struct mntentchn
*mc
, *mc0
, *absent
= NULL
;
570 if (mtab_does_not_exist() || mtab_is_a_symlink())
575 /* having locked mtab, read it again */
576 mc0
= mc
= &mtabhead
;
577 mc
->nxt
= mc
->prev
= NULL
;
579 mfp
= my_setmntent(fnam
, "r");
580 if (mfp
== NULL
|| mfp
->mntent_fp
== NULL
) {
582 error (_("cannot open %s (%s) - mtab not updated"),
583 fnam
, strerror (errsv
));
587 read_mntentchn(mfp
, fnam
, mc
);
589 /* find last occurrence of dir */
590 for (mc
= mc0
->prev
; mc
&& mc
!= mc0
; mc
= mc
->prev
)
591 if (streq(mc
->m
.mnt_dir
, dir
))
593 if (mc
&& mc
!= mc0
) {
594 if (instead
== NULL
) {
595 /* An umount - remove entry */
596 if (mc
&& mc
!= mc0
) {
597 mc
->prev
->nxt
= mc
->nxt
;
598 mc
->nxt
->prev
= mc
->prev
;
603 mc
->m
.mnt_opts
= instead
->mnt_opts
;
605 } else if (instead
) {
606 /* not found, add a new entry */
607 absent
= xmalloc(sizeof(*absent
));
608 absent
->m
= *instead
;
610 absent
->prev
= mc0
->prev
;
612 if (mc0
->nxt
== NULL
)
616 /* write chain to mtemp */
617 mftmp
= my_setmntent (MOUNTED_TEMP
, "w");
618 if (mftmp
== NULL
|| mftmp
->mntent_fp
== NULL
) {
620 error (_("cannot open %s (%s) - mtab not updated"),
621 MOUNTED_TEMP
, strerror (errsv
));
625 for (mc
= mc0
->nxt
; mc
&& mc
!= mc0
; mc
= mc
->nxt
) {
626 if (my_addmntent(mftmp
, &(mc
->m
)) == 1) {
628 die (EX_FILEIO
, _("error writing %s: %s"),
629 MOUNTED_TEMP
, strerror (errsv
));
633 discard_mntentchn(mc0
);
635 if (fchmod (fileno (mftmp
->mntent_fp
),
636 S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) < 0) {
638 fprintf(stderr
, _("error changing mode of %s: %s\n"),
639 MOUNTED_TEMP
, strerror (errsv
));
641 my_endmntent (mftmp
);
644 * If mount is setuid and some non-root user mounts sth,
645 * then mtab.tmp might get the group of this user. Copy uid/gid
646 * from the present mtab before renaming.
649 if (stat (MOUNTED
, &sbuf
) == 0)
650 chown (MOUNTED_TEMP
, sbuf
.st_uid
, sbuf
.st_gid
);
653 /* rename mtemp to mtab */
654 if (rename (MOUNTED_TEMP
, MOUNTED
) < 0) {
656 fprintf(stderr
, _("can't rename %s to %s: %s\n"),
657 MOUNTED_TEMP
, MOUNTED
, strerror(errsv
));