]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/fstab.c
Imported Upstream version 1.2.4
[nfs-utils.git] / utils / mount / fstab.c
1 /* 1999-02-22 Arkadiusz Miskiewicz <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
5  *
6  * 2006-06-08 Amit Gud <agud@redhat.com>
7  * - Moved code to nfs-utils/support/nfs from util-linux/mount.
8  */
9
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>
17
18 #include "fstab.h"
19 #include "xcommon.h"
20 #include "nfs_mntent.h"
21 #include "nfs_paths.h"
22 #include "nls.h"
23
24 #define LOCK_TIMEOUT    10
25 #define streq(s, t)     (strcmp ((s), (t)) == 0)
26 #define PROC_MOUNTS             "/proc/mounts"
27
28 extern char *progname;
29 extern int verbose;
30
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;
35
36 static void
37 get_mtab_info(void) {
38         struct stat mtab_stat;
39
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;
46         }
47 }
48
49 void
50 reset_mtab_info(void) {
51         have_mtab_info = 0;
52 }
53
54 int
55 mtab_does_not_exist(void) {
56         get_mtab_info();
57         return var_mtab_does_not_exist;
58 }
59
60 static int
61 mtab_is_a_symlink(void) {
62         get_mtab_info();
63         return var_mtab_is_a_symlink;
64 }
65
66 int
67 mtab_is_writable() {
68         int fd;
69
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;
76
77         fd = open(MOUNTED, O_RDWR | O_CREAT, 0644);
78         if (fd >= 0) {
79                 close(fd);
80                 return 1;
81         } else
82                 return 0;
83 }
84
85 /* Contents of mtab and fstab ---------------------------------*/
86
87 struct mntentchn mounttable;
88 static int got_mtab = 0;
89 struct mntentchn fstab;
90 static int got_fstab = 0;
91
92 static void read_mounttable(void);
93 static void read_fstab(void);
94
95 static struct mntentchn *
96 mtab_head(void)
97 {
98         if (!got_mtab)
99                 read_mounttable();
100         return &mounttable;
101 }
102
103 static struct mntentchn *
104 fstab_head(void)
105 {
106         if (!got_fstab)
107                 read_fstab();
108         return &fstab;
109 }
110
111 #if 0
112 static void
113 my_free(const void *s) {
114         if (s)
115                 free((void *) s);
116 }
117
118 static void
119 discard_mntentchn(struct mntentchn *mc0) {
120         struct mntentchn *mc, *mc1;
121
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);
129         }
130 }
131 #endif
132
133 static void
134 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
135         struct mntentchn *mc = mc0;
136         struct mntent *mnt;
137
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;
145                 }
146         }
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;
153         }
154         nfs_endmntent(mfp);
155 }
156
157 /*
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.
161  */
162 static void
163 read_mounttable() {
164         mntFILE *mfp;
165         const char *fnam;
166         struct mntentchn *mc = &mounttable;
167
168         got_mtab = 1;
169         mc->nxt = mc->prev = NULL;
170
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;
181                 }
182                 if (verbose)
183                         printf(_("%s: could not open %s; using %s instead\n"),
184                                 progname, MOUNTED, PROC_MOUNTS);
185         }
186         read_mntentchn(mfp, fnam, mc);
187 }
188
189 static void
190 read_fstab()
191 {
192         mntFILE *mfp = NULL;
193         const char *fnam;
194         struct mntentchn *mc = &fstab;
195
196         got_fstab = 1;
197         mc->nxt = mc->prev = NULL;
198
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;
206         }
207         read_mntentchn(mfp, fnam, mc);
208 }
209
210 /*
211  * Given the directory name NAME, and the place MCPREV we found it last time,
212  * try to find more occurrences.
213  */ 
214 struct mntentchn *
215 getmntdirbackward (const char *name, struct mntentchn *mcprev) {
216         struct mntentchn *mc, *mc0;
217
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;
225 }
226
227 /*
228  * Given the device name NAME, and the place MCPREV we found it last time,
229  * try to find more occurrences.
230  */ 
231 struct mntentchn *
232 getmntdevbackward (const char *name, struct mntentchn *mcprev) {
233         struct mntentchn *mc, *mc0;
234
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;
242 }
243
244 /* Find the dir FILE in fstab.  */
245 struct mntentchn *
246 getfsfile (const char *file)
247 {
248         struct mntentchn *mc, *mc0;
249
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;
255 }
256
257 /* Find the device SPEC in fstab.  */
258 struct mntentchn *
259 getfsspec (const char *spec)
260 {
261         struct mntentchn *mc, *mc0;
262
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;
268 }
269
270 /* Updating mtab ----------------------------------------------*/
271
272 /* Flag for already existing lock file. */
273 static int we_created_lockfile = 0;
274 static int lockfile_fd = -1;
275
276 /* Flag to indicate that signals have been set up. */
277 static int signals_have_been_setup = 0;
278
279 /* Ensure that the lock is released if we are interrupted.  */
280 extern char *strsignal(int sig);        /* not always in <string.h> */
281
282 static void
283 handler (int sig) {
284      die(EX_USER, "%s", strsignal(sig));
285 }
286
287 static void
288 setlkw_timeout (__attribute__((unused)) int sig) {
289      /* nothing, fcntl will fail anyway */
290 }
291
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;
300         }
301 }
302
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(). */
316
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)
322
323 void
324 lock_mtab (void) {
325         int tries = 100000, i;
326         char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
327
328         at_die = unlock_mtab;
329
330         if (!signals_have_been_setup) {
331                 int sig = 0;
332                 struct sigaction sa;
333
334                 sa.sa_flags = 0;
335                 sigfillset (&sa.sa_mask);
336   
337                 while (sigismember (&sa.sa_mask, ++sig) != -1) {
338                         switch(sig) {
339                         case SIGCHLD:
340                         case SIGKILL:
341                         case SIGCONT:
342                         case SIGSTOP:
343                                 /* The cannot be caught, or should not,
344                                  * so don't even try.
345                                  */
346                                 continue;
347                         case SIGALRM:
348                                 sa.sa_handler = setlkw_timeout;
349                                 break;
350                         case SIGHUP:
351                         case SIGINT:
352                         case SIGQUIT:
353                         case SIGWINCH:
354                         case SIGTSTP:
355                         case SIGTTIN:
356                         case SIGTTOU:
357                         case SIGPIPE:
358                         case SIGXFSZ:
359                         case SIGXCPU:
360                                 /* non-priv user can cause these to be
361                                  * generated, so ignore them.
362                                  */
363                                 sa.sa_handler = SIG_IGN;
364                                 break;
365                         default:
366                                 /* The rest should not be possible, so just
367                                  * print a message and unlock mtab.
368                                  */
369                                 sa.sa_handler = handler;
370                         }
371                         sigaction (sig, &sa, (struct sigaction *) 0);
372                 }
373                 signals_have_been_setup = 1;
374         }
375
376         sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
377
378         i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
379         if (i < 0) {
380                 int errsv = errno;
381                 /* linktargetfile does not exist (as a file)
382                    and we cannot create it. Read-only filesystem?
383                    Too many files open in the system?
384                    Filesystem full? */
385                 die (EX_FILEIO, _("can't create lock file %s: %s "
386                                                   "(use -n flag to override)"),
387                          linktargetfile, strerror (errsv));
388         }
389         close(i);
390         
391         /* Repeat until it was us who made the link */
392         while (!we_created_lockfile) {
393                 struct flock flock;
394                 int j;
395
396                 j = link(linktargetfile, MOUNTED_LOCK);
397
398                 {
399                         int errsv = errno;
400
401                         if (j == 0)
402                                 we_created_lockfile = 1;
403
404                         if (j < 0 && errsv != EEXIST) {
405                                 (void) unlink(linktargetfile);
406                                 die (EX_FILEIO, _("can't link lock file %s: %s "
407                                      "(use -n flag to override)"),
408                                      MOUNTED_LOCK, strerror (errsv));
409                         }
410                 }
411
412                 lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
413
414                 if (lockfile_fd < 0) {
415                         int errsv = errno;
416                         /* Strange... Maybe the file was just deleted? */
417                         if (errno == ENOENT && tries-- > 0) {
418                                 if (tries % 200 == 0)
419                                         usleep(30);
420                                 continue;
421                         }
422                         (void) unlink(linktargetfile);
423                         die (EX_FILEIO, _("can't open lock file %s: %s "
424                              "(use -n flag to override)"),
425                              MOUNTED_LOCK, strerror (errsv));
426                 }
427
428                 flock.l_type = F_WRLCK;
429                 flock.l_whence = SEEK_SET;
430                 flock.l_start = 0;
431                 flock.l_len = 0;
432
433                 if (j == 0) {
434                         /* We made the link. Now claim the lock. */
435                         if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
436                                 if (verbose) {
437                                     int errsv = errno;
438                                     nfs_error(_("%s: Can't lock lock file "
439                                                 "%s: %s"), progname,
440                                                 MOUNTED_LOCK,
441                                                 strerror (errsv));
442                                 }
443                                 /* proceed anyway */
444                         }
445                         (void) unlink(linktargetfile);
446                 } else {
447                         static int retries = 0;
448
449                         /* Someone else made the link. Wait. */
450                         alarm(LOCK_TIMEOUT);
451                         if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
452                                 int errsv = errno;
453                                 (void) unlink(linktargetfile);
454                                 die (EX_FILEIO, _("can't lock lock file %s: %s"),
455                                      MOUNTED_LOCK, (errno == EINTR) ?
456                                      _("timed out") : strerror (errsv));
457                         }
458                         alarm(0);
459                         /* Limit the number of iterations - maybe there
460                            still is some old /etc/mtab~ */
461                         ++retries;
462                         if (retries % 200 == 0)
463                            usleep(30);
464                         if (retries > 100000) {
465                                 (void) unlink(linktargetfile);
466                                 close(lockfile_fd);
467                                 die (EX_FILEIO, _("Cannot create link %s\n"
468                                                   "Perhaps there is a stale lock file?\n"),
469                                          MOUNTED_LOCK);
470                         }
471                         close(lockfile_fd);
472                 }
473         }
474 }
475
476 /*
477  * Update the mtab.
478  *  Used by umount with null INSTEAD: remove the last DIR entry.
479  *  Used by mount upon a remount: update option part,
480  *   and complain if a wrong device or type was given.
481  *   [Note that often a remount will be a rw remount of /
482  *    where there was no entry before, and we'll have to believe
483  *    the values given in INSTEAD.]
484  */
485
486 void
487 update_mtab (const char *dir, struct mntent *instead)
488 {
489         mntFILE *mfp, *mftmp;
490         const char *fnam = MOUNTED;
491         struct mntentchn mtabhead;      /* dummy */
492         struct mntentchn *mc, *mc0, *absent = NULL;
493
494         if (mtab_does_not_exist() || !mtab_is_writable())
495                 return;
496
497         lock_mtab();
498
499         /* having locked mtab, read it again */
500         mc0 = mc = &mtabhead;
501         mc->nxt = mc->prev = NULL;
502
503         mfp = nfs_setmntent(fnam, "r");
504         if (mfp == NULL || mfp->mntent_fp == NULL) {
505                 int errsv = errno;
506                 nfs_error (_("cannot open %s (%s) - mtab not updated"),
507                        fnam, strerror (errsv));
508                 goto leave;
509         }
510
511         read_mntentchn(mfp, fnam, mc);
512
513         /* find last occurrence of dir */
514         for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
515                 if (streq(mc->m.mnt_dir, dir))
516                         break;
517         if (mc && mc != mc0) {
518                 if (instead == NULL) {
519                         /* An umount - remove entry */
520                         if (mc && mc != mc0) {
521                                 mc->prev->nxt = mc->nxt;
522                                 mc->nxt->prev = mc->prev;
523                                 free(mc);
524                         }
525                 } else {
526                         /* A remount */
527                         mc->m.mnt_opts = instead->mnt_opts;
528                 }
529         } else if (instead) {
530                 /* not found, add a new entry */
531                 absent = xmalloc(sizeof(*absent));
532                 absent->m = *instead;
533                 absent->nxt = mc0;
534                 absent->prev = mc0->prev;
535                 mc0->prev = absent;
536                 if (mc0->nxt == NULL)
537                         mc0->nxt = absent;
538         }
539
540         /* write chain to mtemp */
541         mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
542         if (mftmp == NULL || mftmp->mntent_fp == NULL) {
543                 int errsv = errno;
544                 nfs_error (_("cannot open %s (%s) - mtab not updated"),
545                        MOUNTED_TEMP, strerror (errsv));
546                 goto leave;
547         }
548
549         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
550                 if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
551                         int errsv = errno;
552                         die (EX_FILEIO, _("error writing %s: %s"),
553                              MOUNTED_TEMP, strerror (errsv));
554                 }
555         }
556
557 #if 0
558         /* the chain might have strings copied from 'instead',
559          * so we cannot safely free it.
560          * And there is no need anyway because we are going to exit
561          * shortly.  So just don't call discard_mntentchn....
562          */
563         discard_mntentchn(mc0);
564 #endif
565         if (fchmod (fileno (mftmp->mntent_fp),
566                     S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
567                 int errsv = errno;
568                 nfs_error(_("%s: error changing mode of %s: %s"),
569                                 progname, MOUNTED_TEMP, strerror (errsv));
570         }
571         nfs_endmntent (mftmp);
572
573         { /*
574            * If mount is setuid and some non-root user mounts sth,
575            * then mtab.tmp might get the group of this user. Copy uid/gid
576            * from the present mtab before renaming.
577            */
578             struct stat sbuf;
579             if (stat (MOUNTED, &sbuf) == 0) {
580                         if (chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid) < 0) {
581                                 nfs_error(_("%s: error changing owner of %s: %s"),
582                                         progname, MOUNTED_TEMP, strerror (errno));
583                         }
584                 }
585         }
586
587         /* rename mtemp to mtab */
588         if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
589                 int errsv = errno;
590                 nfs_error(_("%s: can't rename %s to %s: %s\n"),
591                                 progname, MOUNTED_TEMP, MOUNTED,
592                                 strerror(errsv));
593         }
594
595  leave:
596         unlock_mtab();
597 }