7ebbf4b29e54f5e2fbd3aaece919b2dfeeb4c28d
[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 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 procmounts;
90 static int got_procmounts = 0;
91 struct mntentchn fstab;
92 static int got_fstab = 0;
93
94 static void read_mounttable(void);
95 static void read_procmounts(void);
96 static void read_fstab(void);
97
98 static struct mntentchn *
99 mtab_head(void)
100 {
101         if (!got_mtab)
102                 read_mounttable();
103         return &mounttable;
104 }
105
106 static struct mntentchn *
107 procmounts_head(void)
108 {
109         if (!got_procmounts)
110                 read_procmounts();
111         return &procmounts;
112 }
113
114 static struct mntentchn *
115 fstab_head(void)
116 {
117         if (!got_fstab)
118                 read_fstab();
119         return &fstab;
120 }
121
122 #if 0
123 static void
124 my_free(const void *s) {
125         if (s)
126                 free((void *) s);
127 }
128
129 static void
130 discard_mntentchn(struct mntentchn *mc0) {
131         struct mntentchn *mc, *mc1;
132
133         for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) {
134                 mc1 = mc->nxt;
135                 my_free(mc->m.mnt_fsname);
136                 my_free(mc->m.mnt_dir);
137                 my_free(mc->m.mnt_type);
138                 my_free(mc->m.mnt_opts);
139                 free(mc);
140         }
141 }
142 #endif
143
144 static void
145 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
146         struct mntentchn *mc = mc0;
147         struct mntent *mnt;
148
149         while ((mnt = nfs_getmntent(mfp)) != NULL) {
150                 if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) {
151                         mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
152                         mc->nxt->prev = mc;
153                         mc = mc->nxt;
154                         mc->m = *mnt;
155                         mc->nxt = mc0;
156                 }
157         }
158         mc0->prev = mc;
159         if (ferror(mfp->mntent_fp)) {
160                 int errsv = errno;
161                 nfs_error(_("warning: error reading %s: %s"),
162                       fnam, strerror (errsv));
163                 mc0->nxt = mc0->prev = NULL;
164         }
165         nfs_endmntent(mfp);
166 }
167
168 /*
169  * Read /etc/mtab.  If that fails, try /proc/mounts.
170  * This produces a linked list. The list head mounttable is a dummy.
171  * Return 0 on success.
172  */
173 static void
174 read_mounttable() {
175         mntFILE *mfp;
176         const char *fnam;
177         struct mntentchn *mc = &mounttable;
178
179         got_mtab = 1;
180         mc->nxt = mc->prev = NULL;
181
182         fnam = MOUNTED;
183         mfp = nfs_setmntent (fnam, "r");
184         if (mfp == NULL || mfp->mntent_fp == NULL) {
185                 int errsv = errno;
186                 fnam = PROC_MOUNTS;
187                 mfp = nfs_setmntent (fnam, "r");
188                 if (mfp == NULL || mfp->mntent_fp == NULL) {
189                         nfs_error(_("warning: can't open %s: %s"),
190                               MOUNTED, strerror (errsv));
191                         return;
192                 }
193                 if (verbose)
194                         printf(_("%s: could not open %s; using %s instead\n"),
195                                 progname, MOUNTED, PROC_MOUNTS);
196         }
197         read_mntentchn(mfp, fnam, mc);
198 }
199
200 /*
201  * Read /proc/mounts.
202  * This produces a linked list. The list head procmounts is a dummy.
203  * Return 0 on success.
204  */
205 static void
206 read_procmounts() {
207         mntFILE *mfp;
208         const char *fnam;
209         struct mntentchn *mc = &procmounts;
210
211         got_procmounts = 1;
212         mc->nxt = mc->prev = NULL;
213
214         fnam = PROC_MOUNTS;
215         mfp = nfs_setmntent(fnam, "r");
216         if (mfp == NULL || mfp->mntent_fp == NULL) {
217                 nfs_error(_("warning: can't open %s: %s"),
218                           PROC_MOUNTS, strerror (errno));
219                 return;
220         }
221         read_mntentchn(mfp, fnam, mc);
222 }
223
224 static void
225 read_fstab()
226 {
227         mntFILE *mfp = NULL;
228         const char *fnam;
229         struct mntentchn *mc = &fstab;
230
231         got_fstab = 1;
232         mc->nxt = mc->prev = NULL;
233
234         fnam = _PATH_FSTAB;
235         mfp = nfs_setmntent (fnam, "r");
236         if (mfp == NULL || mfp->mntent_fp == NULL) {
237                 int errsv = errno;
238                 nfs_error(_("warning: can't open %s: %s"),
239                           _PATH_FSTAB, strerror (errsv));
240                 return;
241         }
242         read_mntentchn(mfp, fnam, mc);
243 }
244
245 /*
246  * Given the directory name NAME, and the place MCPREV we found it last time,
247  * try to find more occurrences.
248  */ 
249 struct mntentchn *
250 getmntdirbackward (const char *name, struct mntentchn *mcprev) {
251         struct mntentchn *mc, *mc0;
252
253         mc0 = mtab_head();
254         if (!mcprev)
255                 mcprev = mc0;
256         for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
257                 if (streq(mc->m.mnt_dir, name))
258                         return mc;
259         return NULL;
260 }
261
262 /*
263  * Given the directory name NAME, and the place MCPREV we found it last time,
264  * try to find more occurrences.
265  */
266 struct mntentchn *
267 getprocmntdirbackward (const char *name, struct mntentchn *mcprev) {
268         struct mntentchn *mc, *mc0;
269
270         mc0 = procmounts_head();
271         if (!mcprev)
272                 mcprev = mc0;
273         for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
274                 if (streq(mc->m.mnt_dir, name))
275                         return mc;
276         return NULL;
277 }
278
279 /*
280  * Given the device name NAME, and the place MCPREV we found it last time,
281  * try to find more occurrences.
282  */ 
283 struct mntentchn *
284 getmntdevbackward (const char *name, struct mntentchn *mcprev) {
285         struct mntentchn *mc, *mc0;
286
287         mc0 = mtab_head();
288         if (!mcprev)
289                 mcprev = mc0;
290         for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
291                 if (streq(mc->m.mnt_fsname, name))
292                         return mc;
293         return NULL;
294 }
295
296 /* Find the dir FILE in fstab.  */
297 struct mntentchn *
298 getfsfile (const char *file)
299 {
300         struct mntentchn *mc, *mc0;
301
302         mc0 = fstab_head();
303         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
304                 if (streq(mc->m.mnt_dir, file))
305                         return mc;
306         return NULL;
307 }
308
309 /* Find the device SPEC in fstab.  */
310 struct mntentchn *
311 getfsspec (const char *spec)
312 {
313         struct mntentchn *mc, *mc0;
314
315         mc0 = fstab_head();
316         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
317                 if (streq(mc->m.mnt_fsname, spec))
318                         return mc;
319         return NULL;
320 }
321
322 /* Updating mtab ----------------------------------------------*/
323
324 /* Flag for already existing lock file. */
325 static int we_created_lockfile = 0;
326 static int lockfile_fd = -1;
327
328 /* Flag to indicate that signals have been set up. */
329 static int signals_have_been_setup = 0;
330
331 /* Ensure that the lock is released if we are interrupted.  */
332 extern char *strsignal(int sig);        /* not always in <string.h> */
333
334 static void
335 handler (int sig) {
336      die(EX_USER, "%s", strsignal(sig));
337 }
338
339 static void
340 setlkw_timeout (__attribute__((unused)) int sig) {
341      /* nothing, fcntl will fail anyway */
342 }
343
344 /* Remove lock file.  */
345 void
346 unlock_mtab (void) {
347         if (we_created_lockfile) {
348                 close(lockfile_fd);
349                 lockfile_fd = -1;
350                 unlink (MOUNTED_LOCK);
351                 we_created_lockfile = 0;
352         }
353 }
354
355 /* Create the lock file.
356    The lock file will be removed if we catch a signal or when we exit. */
357 /* The old code here used flock on a lock file /etc/mtab~ and deleted
358    this lock file afterwards. However, as rgooch remarks, that has a
359    race: a second mount may be waiting on the lock and proceed as
360    soon as the lock file is deleted by the first mount, and immediately
361    afterwards a third mount comes, creates a new /etc/mtab~, applies
362    flock to that, and also proceeds, so that the second and third mount
363    now both are scribbling in /etc/mtab.
364    The new code uses a link() instead of a creat(), where we proceed
365    only if it was us that created the lock, and hence we always have
366    to delete the lock afterwards. Now the use of flock() is in principle
367    superfluous, but avoids an arbitrary sleep(). */
368
369 /* Where does the link point to? Obvious choices are mtab and mtab~~.
370    HJLu points out that the latter leads to races. Right now we use
371    mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
372 #define MOUNTLOCK_LINKTARGET            MOUNTED_LOCK "%d"
373 #define MOUNTLOCK_LINKTARGET_LTH        (sizeof(MOUNTED_LOCK)+20)
374
375 void
376 lock_mtab (void) {
377         int tries = 100000, i;
378         char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
379
380         at_die = unlock_mtab;
381
382         if (!signals_have_been_setup) {
383                 int sig = 0;
384                 struct sigaction sa;
385
386                 sa.sa_flags = 0;
387                 sigfillset (&sa.sa_mask);
388   
389                 while (sigismember (&sa.sa_mask, ++sig) != -1) {
390                         switch(sig) {
391                         case SIGCHLD:
392                         case SIGKILL:
393                         case SIGCONT:
394                         case SIGSTOP:
395                                 /* The cannot be caught, or should not,
396                                  * so don't even try.
397                                  */
398                                 continue;
399                         case SIGALRM:
400                                 sa.sa_handler = setlkw_timeout;
401                                 break;
402                         case SIGHUP:
403                         case SIGINT:
404                         case SIGQUIT:
405                         case SIGWINCH:
406                         case SIGTSTP:
407                         case SIGTTIN:
408                         case SIGTTOU:
409                         case SIGPIPE:
410                         case SIGXFSZ:
411                         case SIGXCPU:
412                                 /* non-priv user can cause these to be
413                                  * generated, so ignore them.
414                                  */
415                                 sa.sa_handler = SIG_IGN;
416                                 break;
417                         default:
418                                 /* The rest should not be possible, so just
419                                  * print a message and unlock mtab.
420                                  */
421                                 sa.sa_handler = handler;
422                         }
423                         sigaction (sig, &sa, (struct sigaction *) 0);
424                 }
425                 signals_have_been_setup = 1;
426         }
427
428         sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
429
430         i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
431         if (i < 0) {
432                 int errsv = errno;
433                 /* linktargetfile does not exist (as a file)
434                    and we cannot create it. Read-only filesystem?
435                    Too many files open in the system?
436                    Filesystem full? */
437                 die (EX_FILEIO, _("can't create lock file %s: %s "
438                                                   "(use -n flag to override)"),
439                          linktargetfile, strerror (errsv));
440         }
441         close(i);
442         
443         /* Repeat until it was us who made the link */
444         while (!we_created_lockfile) {
445                 struct flock flock;
446                 int j;
447
448                 j = link(linktargetfile, MOUNTED_LOCK);
449
450                 {
451                         int errsv = errno;
452
453                         if (j == 0)
454                                 we_created_lockfile = 1;
455
456                         if (j < 0 && errsv != EEXIST) {
457                                 (void) unlink(linktargetfile);
458                                 die (EX_FILEIO, _("can't link lock file %s: %s "
459                                      "(use -n flag to override)"),
460                                      MOUNTED_LOCK, strerror (errsv));
461                         }
462                 }
463
464                 lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
465
466                 if (lockfile_fd < 0) {
467                         int errsv = errno;
468                         /* Strange... Maybe the file was just deleted? */
469                         if (errno == ENOENT && tries-- > 0) {
470                                 if (tries % 200 == 0)
471                                         usleep(30);
472                                 continue;
473                         }
474                         (void) unlink(linktargetfile);
475                         die (EX_FILEIO, _("can't open lock file %s: %s "
476                              "(use -n flag to override)"),
477                              MOUNTED_LOCK, strerror (errsv));
478                 }
479
480                 flock.l_type = F_WRLCK;
481                 flock.l_whence = SEEK_SET;
482                 flock.l_start = 0;
483                 flock.l_len = 0;
484
485                 if (j == 0) {
486                         /* We made the link. Now claim the lock. */
487                         if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
488                                 if (verbose) {
489                                     int errsv = errno;
490                                     nfs_error(_("%s: Can't lock lock file "
491                                                 "%s: %s"), progname,
492                                                 MOUNTED_LOCK,
493                                                 strerror (errsv));
494                                 }
495                                 /* proceed anyway */
496                         }
497                         (void) unlink(linktargetfile);
498                 } else {
499                         static int retries = 0;
500
501                         /* Someone else made the link. Wait. */
502                         alarm(LOCK_TIMEOUT);
503                         if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
504                                 int errsv = errno;
505                                 (void) unlink(linktargetfile);
506                                 die (EX_FILEIO, _("can't lock lock file %s: %s"),
507                                      MOUNTED_LOCK, (errno == EINTR) ?
508                                      _("timed out") : strerror (errsv));
509                         }
510                         alarm(0);
511                         /* Limit the number of iterations - maybe there
512                            still is some old /etc/mtab~ */
513                         ++retries;
514                         if (retries % 200 == 0)
515                            usleep(30);
516                         if (retries > 100000) {
517                                 (void) unlink(linktargetfile);
518                                 close(lockfile_fd);
519                                 die (EX_FILEIO, _("Cannot create link %s\n"
520                                                   "Perhaps there is a stale lock file?\n"),
521                                          MOUNTED_LOCK);
522                         }
523                         close(lockfile_fd);
524                 }
525         }
526 }
527
528 /*
529  * Update the mtab.
530  *  Used by umount with null INSTEAD: remove the last DIR entry.
531  *  Used by mount upon a remount: update option part,
532  *   and complain if a wrong device or type was given.
533  *   [Note that often a remount will be a rw remount of /
534  *    where there was no entry before, and we'll have to believe
535  *    the values given in INSTEAD.]
536  */
537
538 void
539 update_mtab (const char *dir, struct mntent *instead)
540 {
541         mntFILE *mfp, *mftmp;
542         const char *fnam = MOUNTED;
543         struct mntentchn mtabhead;      /* dummy */
544         struct mntentchn *mc, *mc0, *absent = NULL;
545
546         if (mtab_does_not_exist() || !mtab_is_writable())
547                 return;
548
549         lock_mtab();
550
551         /* having locked mtab, read it again */
552         mc0 = mc = &mtabhead;
553         mc->nxt = mc->prev = NULL;
554
555         mfp = nfs_setmntent(fnam, "r");
556         if (mfp == NULL || mfp->mntent_fp == NULL) {
557                 int errsv = errno;
558                 nfs_error (_("cannot open %s (%s) - mtab not updated"),
559                        fnam, strerror (errsv));
560                 goto leave;
561         }
562
563         read_mntentchn(mfp, fnam, mc);
564
565         /* find last occurrence of dir */
566         for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
567                 if (streq(mc->m.mnt_dir, dir))
568                         break;
569         if (mc && mc != mc0) {
570                 if (instead == NULL) {
571                         /* An umount - remove entry */
572                         if (mc && mc != mc0) {
573                                 mc->prev->nxt = mc->nxt;
574                                 mc->nxt->prev = mc->prev;
575                                 free(mc);
576                         }
577                 } else {
578                         /* A remount */
579                         mc->m.mnt_opts = instead->mnt_opts;
580                 }
581         } else if (instead) {
582                 /* not found, add a new entry */
583                 absent = xmalloc(sizeof(*absent));
584                 absent->m = *instead;
585                 absent->nxt = mc0;
586                 absent->prev = mc0->prev;
587                 mc0->prev = absent;
588                 if (mc0->nxt == NULL)
589                         mc0->nxt = absent;
590         }
591
592         /* write chain to mtemp */
593         mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
594         if (mftmp == NULL || mftmp->mntent_fp == NULL) {
595                 int errsv = errno;
596                 nfs_error (_("cannot open %s (%s) - mtab not updated"),
597                        MOUNTED_TEMP, strerror (errsv));
598                 goto leave;
599         }
600
601         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
602                 if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
603                         int errsv = errno;
604                         die (EX_FILEIO, _("error writing %s: %s"),
605                              MOUNTED_TEMP, strerror (errsv));
606                 }
607         }
608
609 #if 0
610         /* the chain might have strings copied from 'instead',
611          * so we cannot safely free it.
612          * And there is no need anyway because we are going to exit
613          * shortly.  So just don't call discard_mntentchn....
614          */
615         discard_mntentchn(mc0);
616 #endif
617         if (fchmod (fileno (mftmp->mntent_fp),
618                     S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
619                 int errsv = errno;
620                 nfs_error(_("%s: error changing mode of %s: %s"),
621                                 progname, MOUNTED_TEMP, strerror (errsv));
622         }
623         nfs_endmntent (mftmp);
624
625         { /*
626            * If mount is setuid and some non-root user mounts sth,
627            * then mtab.tmp might get the group of this user. Copy uid/gid
628            * from the present mtab before renaming.
629            */
630             struct stat sbuf;
631             if (stat (MOUNTED, &sbuf) == 0) {
632                         if (chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid) < 0) {
633                                 nfs_error(_("%s: error changing owner of %s: %s"),
634                                         progname, MOUNTED_TEMP, strerror (errno));
635                         }
636                 }
637         }
638
639         /* rename mtemp to mtab */
640         if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
641                 int errsv = errno;
642                 nfs_error(_("%s: can't rename %s to %s: %s\n"),
643                                 progname, MOUNTED_TEMP, MOUNTED,
644                                 strerror(errsv));
645         }
646
647  leave:
648         unlock_mtab();
649 }